0

I have both a DataGrid and a ComboBox on wpf UserControl.

namespace ApSap
{ 
public partial class DocumentView : UserControl
{
    public Document document;
    public DocumentView(Document selectedDoc)
    {          
        document = selectedDoc;
        InitializeComponent();
        DocBrowser.Navigate(document.FilePath);
     
        // shows only empty rows
        SapGrid.ItemsSource = document.SapDocNumbers;   
        // shows list of values correctly       
        Combo.ItemsSource = document.SapDocNumbers;
       }
    }
}

The combo Box correctly displays the content of the public property "SapDocNumbers" (a list of integers),

However the datagrid only displays empty rows, albiet the correct number of them.

the XAML is as follows:

<UserControl x:Class="ApSap.DocumentView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         HorizontalAlignment="Stretch"
         VerticalAlignment="Stretch"
        >
<Grid  VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    
    <StackPanel  Grid.Column="1" Grid.Row="1"> 
        <DataGrid  AutoGenerateColumns="True"  x:Name="SapGrid"   Margin="10,10,10,10" >    
    </DataGrid>
        <Button x:Name="CreateInvoice" Content="Create Invoice"  Margin="10,10,10,10"  />  
        <Button x:Name="Save" Content="Save and Exit"   Margin="10,10,10,10"  />
        <ComboBox x:Name="Combo" Margin="10,10,10,10" />
    </StackPanel>
    


</Grid>

Is there anything I am missing from XAML grid definition that would mean the combo works correctly, but the datagrid does not?

as requested here is the definition of the class:

public class Document : INotifyPropertyChanged
{
    private int _docID; 

    private List<Int64> _sapDocNumbers;     

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    } 

    public int DocID
    {
        get { return _docID; }
        set
        {
            if (value != _docID)
            {
                _docID = value;
                NotifyPropertyChanged();  
            }
        }
    }  

    public List<Int64> SapDocNumbers
    {
        get { return _sapDocNumbers; }
        set
        {
            if (value != _sapDocNumbers)
            {
                _sapDocNumbers = value;
                NotifyPropertyChanged();
            }
        }
    }

Thank you

Praxiom
  • 578
  • 1
  • 8
  • 21

2 Answers2

1

The answer to your question depends on the implementation of the collection item type.
ComboBox uses ToString () by default to represent the item.
And DataGrid renders the properties of the element - for each property there is a separate column.
If the element type has no properties, the DataGrid will not create columns and will not display anything.

For a more precise answer, show the type of the collection and the implementation of the type of its element.

Completion of the answer in connection with the clarification of the question:
You want to display the ulong collection.
This type has no properties and therefore autogenerating columns in the DataGrid cannot create columns.

For your task you need:

    <DataGrid  AutoGenerateColumns="False"  x:Name="SapGrid"   Margin="10" >
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Mode=OneWay}" Header="Number"/>
        </DataGrid.Columns>
    </DataGrid>
EldHasp
  • 6,079
  • 2
  • 9
  • 24
  • I have added the definition of the class, is that the information you need? – Praxiom May 06 '21 at 12:34
  • 1
    Yes. I have provided additional clarification. – EldHasp May 06 '21 at 12:42
  • Thank you, the issue is resolved. If i Wanted to display both properties of the document class (DocId & the list) how would i do that? SapGrid.ItemsSource = document throws an error – Praxiom May 06 '21 at 13:16
  • 1
    For the ItemsSource, the IEnumerable type is required: arrays, collections, lists, etc. Document type is ONE instance. And it doesn't have an enumerator. Therefore, it is impossible to set it as the source of ItemsSource. You will write more details of what you want to SEE in the columns and rows of the DataGrid. Perhaps I can tell you the Solution. – EldHasp May 06 '21 at 15:34
0

one final question, How do i get a blank row on the DataGrid so the user can add their own SapDocNumbers to the list ?

  1. The item of the collection goes to the Data Context of the DataGrid row. UI elements cannot edit their DataContext. Therefore, for a string, you need to create a reference ty with a property of the desired type and edit this property. And this type must have a default constructor.

  2. Since the list can be used in several bindings, the type of the list should not be a simple collection, but an observable collection.

For examples used class BaseInpc

using Simplified;

namespace ApSap
{
    public class DocumentRow : BaseInpc
    {
        private long _sapDocNumber;

        public long SapDocNumber { get => _sapDocNumber; set => Set(ref _sapDocNumber, value); }

        public DocumentRow(long sapDocNumber) => SapDocNumber = sapDocNumber;

        public DocumentRow() { }
    }

}
using Simplified;
using System.Collections.ObjectModel;

namespace ApSap
{
    public class Document : BaseInpc
    {
        private int _docID;

        public int DocID { get => _docID; set => Set(ref _docID, value); }

        public ObservableCollection<DocumentRow> SapDocNumbers { get; }
            = new ObservableCollection<DocumentRow>();

        public Document()
        {
        }

        public Document(int docID, params long[] sapDocNumbers)
        {
            DocID = docID;
            foreach (var number in sapDocNumbers)

                SapDocNumbers.Add(new DocumentRow(number));
        }

        public static Document ExampleInstance { get; } = new Document(123, 4, 5, 6, 7, 8, 9, 0);
    }

}
<Window x:Class="ApSap.DocumentWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ApSap"
        mc:Ignorable="d"
        Title="DocumentWindow" Height="450" Width="800"
        DataContext="{x:Static local:Document.ExampleInstance}">

    <Grid  VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <StackPanel  Grid.Column="1" Grid.Row="1">
            <TextBlock Margin="10,10,10,0">
                <Run Text="DocID:"/>
                <Run Text="{Binding DocID}"/>
            </TextBlock>
            <DataGrid  AutoGenerateColumns="True"  Margin="10"
                       ItemsSource="{Binding SapDocNumbers}"/>
            <Button x:Name="CreateInvoice" Content="Create Invoice"  Margin="10"  />
            <Button x:Name="Save" Content="Save and Exit"   Margin="10"  />
            <ComboBox x:Name="Combo" Margin="10" />
        </StackPanel>

        <DataGrid Grid.Row="1" AutoGenerateColumns="True"  Margin="10"
                  ItemsSource="{Binding SapDocNumbers}" VerticalAlignment="Top"/>
    </Grid>
</Window>

P.S. Learn to set the Data Context and bindings to it. Using the names of UI elements, referring to them in Sharp is most often very bad code.

EldHasp
  • 6,079
  • 2
  • 9
  • 24
  • Hi Thanks for your Help. Some Questions: 1. public long SapDocNumber { get => _sapDocNumber; set => Set(ref _sapDocNumber, value); } is that simplified as it shows errors on most of it?. 2. is there no way to modify the existing class to handle the adding of items to the list without the additional rows class? – Praxiom May 07 '21 at 09:15
  • 1
    The `Set (ref field, value)` method is part of the INotifyPropertyChanged implementation of the BaseInpc class. I gave a link to the implementation of this class in my answer. Take a closer look. If you are using your own INPC implementation, then change the property declaration. – EldHasp May 07 '21 at 12:51
  • 1
    "нет возможности изменить существующий класс"-It is not possible to modify the DataContext through element bindings that use it. The elements of the `SapDocNumbers` collection are fed into the row context of the DataGrid (DataGrid). Therefore, UI bindings of elements within a row (and a cell is within a row) cannot change this element. Therefore, if you need editing in a DataGrid, then the element of the `SapDocNumbers` collection must be a class with properties. Then you will be able to edit the property values ​​of the collection item. – EldHasp May 07 '21 at 13:03
  • 1
    Also in this case, DataGrid autogeneration is triggered, which simplifies XALM. And to add a new row to the DataGrid, the collection item must have a default constructor. For this reason, I have shown you two constructors as an example. You can delete either a constructor with parameters, or both. But you cannot remove a constructor without parameters, leaving the second one. – EldHasp May 07 '21 at 13:03