Search code examples
c#wpfxamldata-bindingdatagrid

Why are DataGrid items empty even if it is bound to an ObservableCollection that has rows in WPF?


I'm using C# WPF

I have Datagrid that is filled by an observable collection named: "ALLTASKLIST" filled with Select data from a Database

I used DataBdinging to display data from "ALLTASKLIST" in my DataGrid

but it's empty!

XAML: <DataGrid.Columns>

            <DataGridComboBoxColumn Header="CMB">
                <DataGridComboBoxColumn.EditingElementStyle>
                    <Style TargetType="ComboBox">
                        <Setter Property="ItemsSource" Value="{Binding Path=ALLTASKLIST}" />
                        <Setter Property="DisplayMemberPath" Value="NAMES" />
                        <Setter Property="SelectedValuePath" Value="CODE" />
                    </Style>
                </DataGridComboBoxColumn.EditingElementStyle>
            </DataGridComboBoxColumn>
        </DataGrid.Columns>

       
    </DataGrid> 

Code behind:

  public partial class MainWindow : Window
    {
        NEGIN1401Entities dbms = null;
        public class ThePart1 : INotifyPropertyChanged
        {
            private int? _CODE;
            private string _NAMES;
            private int? _KIND;
            private int? _idd;
            public int? CODE
            {
                get { return _CODE; }
                set
                {
                    _CODE = value;
                    OnPropertyChanged("CODE");
                }
            }
            public string NAMES
            {
                get { return _NAMES; }
                set
                {
                    _NAMES = value;
                    OnPropertyChanged("NAMES");
                }
            }
            public int? KIND
            {
                get { return _KIND; }
                set
                {
                    _KIND = value;
                    OnPropertyChanged("KIND");
                }
            }
            public int? idd
            {
                get { return _idd; }
                set
                {
                    _idd = value;
                    OnPropertyChanged("idd");
                }
            }
            public event PropertyChangedEventHandler PropertyChanged;
            public void OnPropertyChanged(string strCaller = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(strCaller));
            }
        }
        //public ObservableCollection<ThePart1> ALLTASKLIST { get; set; }
        public ObservableCollection<ThePart1> ALLTASKLIST = new ObservableCollection<ThePart1>();
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            dbms = new NEGIN1401Entities(Publications.TheENTTConnectionString);

            ALLTASKLIST = dbms.Database.SqlQuery<ThePart1>("SELECT * FROM TCOD_ANBAR").ToObservableCollection();
            var IsHaveItems = ALLTASKLIST.Count;//it will be 6 Items
            //MainDatagrid.ItemsSource = ALLTASKLIST;

        }
    }

The Data Grid's items: enter image description here

Result: enter image description here


Solution

    1. The binding path needs a property, not a field.
    2. Your style will set values for the ComboBox at the row level, and the data context will be the collection element ThePart1, not MainWindow. Accordingly, the binding path will be interpreted relative to ThePart1, which does not have the ALLTASKLIST property.
    3. A feature of the DataGridComboBoxColumn is that the source is obtained immediately when the DataGrid is created. Therefore, the source is usually created either in App resources or in a static property.

    Try this example:

            public static ObservableCollection<ThePart1> ALLTASKLIST {get;}
                = new ObservableCollection<ThePart1>();
    
                ALLTASKLIST.Clear();
                for(var tp1 in dbms.Database.SqlQuery<ThePart1>("SELECT * FROM TCOD_ANBAR").ToList())
                    ALLTASKLIST.Add(tp1);
    
        <DataGridComboBoxColumn Header="CMB"
                                ItemsSource="{x:Static local:MainWindow.ALLTASKLIST}">
    

    I don't understand your second point, can you make it more clear?

    For understanding, try adding this column:

                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding}"/>
                </DataGrid.Columns>
    

    thanks so much but only one thing not work yet ! it's ComboBox ! Cannot Access it in DataGridTemplateColumn or Binding for CombobBox Column

    Without seeing your code, I can't tell the cause of the problem.

    I made a simple example to demonstrate the different ways to create and bind a ComboBox column. Consider this example. Perhaps you will find something useful for you and you yourself will understand what is the cause of the error.

    using System;
    using System.Collections.ObjectModel;
    using System.Windows;
    
    namespace Core2022.SO.DataGridComboBoxColumnExample
    {
        public partial class ExampleWindow : Window
        {
            public ExampleWindow()
            {
                InitializeComponent();
            }
    
            private void OnLoaded(object sender, RoutedEventArgs e)
            {
                EntitiesViewModel vm = (EntitiesViewModel)DataContext;
                Random random = new();
                for (int i = 0; i < 10; i++)
                {
                    int num = random.Next();
                    vm.Entities.Add(new IdEntity { Id = num });
                    vm.Ids.Add(num);
                    EntitiesViewModel.StaticIds.Add(num);
                }
            }
        }
    
        public class EntitiesViewModel
        {
            public ObservableCollection<int> Ids { get; } = new();
    
            public static ObservableCollection<int> StaticIds { get; } = new();
    
            public ObservableCollection<IdEntity> Entities { get; } = new();
    
        }
    
        public class IdEntity
        {
            public int Id { get; set; }
        }
    }
    
    <Window x:Class="Core2022.SO.DataGridComboBoxColumnExample.ExampleWindow"
            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:Core2022.SO.DataGridComboBoxColumnExample"
            mc:Ignorable="d"
            Title="ExampleWindow" Height="450" Width="800"
            Loaded="OnLoaded">
        <FrameworkElement.DataContext>
            <local:EntitiesViewModel/>
        </FrameworkElement.DataContext>
        <Grid>
            <DataGrid ItemsSource="{Binding Entities}">
                <DataGrid.Columns>
                    <DataGridComboBoxColumn SelectedItemBinding="{Binding Id, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{x:Static local:EntitiesViewModel.StaticIds}"/>
                    <DataGridComboBoxColumn SelectedItemBinding="{Binding Id, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding DataContext.Ids, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                    <DataGridTemplateColumn>
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox SelectedItem="{Binding Id, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{x:Static local:EntitiesViewModel.StaticIds}"/>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTemplateColumn>
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox SelectedItem="{Binding Id, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding DataContext.Ids, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
    
            </DataGrid>
        </Grid>
    </Window>
    

    Pay attention to the second column - it is not created due to a binding error. That's exactly what I wrote in point 3.