Search code examples
c#wpfxamlbindingdatagridcomboboxcolumn

How to Access the property of Combobox inside of a DataGrid in WPF


I want to Display data in my ComboBox inside of DataGridTemplateColumn in the DataGrid but it's empty in the binding so I decided to fill it in code behind but I couldn't access it by name!

XAML:

<Window x:Class="ComboDataWPF.MainWindow"
        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:ComboDataWPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" WindowStartupLocation="CenterScreen" Loaded="Window_Loaded">
    
    <Grid>
        <DataGrid x:Name="MainDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding ALLMYDATA, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">

            <DataGrid.Columns>
                <DataGridTextColumn Header="The Name :" Width="120" Binding="{Binding NAMES, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />

                <DataGridTemplateColumn Header=" Name and Code " Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="ComboBox1"
                                      ItemsSource="{Binding ALLMYDATA, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
                                      SelectedValuePath="{Binding CODE, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
                                      DisplayMemberPath="{Binding NAMES, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
                                      IsTextSearchEnabled="True"
                                      IsEditable="True"
                                      SelectedIndex="0"  BorderBrush="#FFADEEB4" Background="{x:Null}" BorderThickness="1">
                                
                                <ComboBox.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <VirtualizingStackPanel VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling"/>
                                    </ItemsPanelTemplate>
                                </ComboBox.ItemsPanel>
                            </ComboBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>

        </DataGrid>
    </Grid>
</Window>

Code Behind:

 using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace ComboDataWPF
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            MyerEntities dbms = new MyerEntities();
            public ObservableCollection<MyCustomModel> ALLMYDATA { get; set; } = new ObservableCollection<MyCustomModel>();
            public class MyCustomModel
            {
                public int CODE { get; set; }
                public string NAMES { get; set; }
            }
            public MainWindow()
            {
                InitializeComponent();
                DataContext = this;
            }
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                ALLMYDATA.Clear();
                var RST = dbms.Database.SqlQuery<MyCustomModel>("SELECT CODE,NAMES FROM TCOD_ANBAR").ToList();
                foreach (var item in RST)
                {
                    ALLMYDATA.Add(item);
                }
            }
        }
    }

my source: https://ufile.io/zvfoj4we

NOTE: I even tried to switch to GridView but it was harder than Datagrid.

so Why the ComboBox is empty and how can I access the property like ItemsSource and ... in C#?

Please guide me


Solution

  • I downloaded your source project. And filled ALLMYDATA collection with 2 objects(cuz I don't have DB for it).

    private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ALLMYDATA.Clear();
            MyCustomModel myCustom = new MyCustomModel()
            {
                CODE = 20,
                NAMES = "Yes"
            };
            MyCustomModel myCustom2 = new MyCustomModel()
            {
                CODE = 30,
                NAMES = "No"
            };
    
            ALLMYDATA.Add(myCustom);
            ALLMYDATA.Add(myCustom2);
        }
    

    So the main problems I saw were in your XAML. As you set DataGrid's ItemsSource property to ALLMYDATA collection, it is DataContext for all child elements of DataGrid, and when you try to bind ComboBox's ItemsSource to property like

    ItemsSource="{Binding ALLMYDATA, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
    

    you get the error because DataContext for ComboBox is the ALLMYDATA collection and this collection doesn't contain the ALLMYDATA property, that's why you can't bind to it in ComboBox. So I changed XAML code above to

    ItemsSource="{Binding ElementName=MainDataGrid, Path=ItemsSource}"
    

    and it works well as ItemsSource of ComboBox is the same as DataGrid now.

    Other problems are related to these pieces of code:

     SelectedValuePath="{Binding CODE, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
    DisplayMemberPath="{Binding NAMES, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
    

    As your ComboBox's ItemsSource is ALLMYDATA, now you don't need to bind anything just write

    SelectedValuePath="CODE"
    DisplayMemberPath="NAMES"
    

    Now your ComboBox is not empty:

    enter image description here

    Here is the full XAML of your DataGrid

    <DataGrid x:Name="MainDataGrid" AutoGenerateColumns="False" ItemsSource="{Binding ALLMYDATA, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">
    
            <DataGrid.Columns>
                <DataGridTextColumn Header="The Name :" Width="120" Binding="{Binding NAMES, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
    
                <DataGridTemplateColumn Header=" Name and Code " Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="ComboBox1"
                                      ItemsSource="{Binding ElementName=MainDataGrid, Path=ItemsSource}"
                                      SelectedValuePath="CODE"
                                      DisplayMemberPath="NAMES"
                                      IsTextSearchEnabled="True"
                                      IsEditable="True"
                                      SelectedIndex="0"  BorderBrush="#FFADEEB4" Background="{x:Null}" BorderThickness="1">
                                
                                <ComboBox.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <VirtualizingStackPanel VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling"/>
                                    </ItemsPanelTemplate>
                                </ComboBox.ItemsPanel>
                            </ComboBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
        </DataGrid>