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

710 Views Asked by At

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

2

There are 2 best solutions below

2
NoName On BEST ANSWER

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>
2
Andrew KeepCoding On

You can do it this way.

First, name you Window.

<Window
    :
    x:Name="ThisWindow">

Then bind the ComboBox's ItemsSource using the Window's name. Also take a look at the SelectedValuePath and the DisplayMemberPath.

<ComboBox
    x:Name="ComboBox1"
    ItemsSource="{Binding ElementName=ThisWindow, Path=ALLMYDATA, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
    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>