DataGridTemplateComboboxColumn in WPF

Carlos Lino 6 Reputation points
2020-06-09T21:34:01.697+00:00

Hi all:

I'm new in WPF and I'm having a hard time trying to make the Combobox works in a DataGridTemplateColumn. I don't want to use DatagridComboboxColumn because the cells on this column look like a text column, the user has to click on it so the arrow down shows up.

I'm able to populate the Combobox but when I select a value from any row, it changes to all the rest of the cells in the Datagrid. What am I doing wrong? Hope you can help me, please.

Thanks in advance.

.cs

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {           
        LoadEmployee();
    }

public List<GenderList> GenderList { get; set; }

void LoadEmployee()
        {
            GenderList = new List<GenderList>()
            {
                new GenderList() { Codigo = 100, Descripcion = "MALE" },
                new GenderList() { Codigo = 200, Descripcion = "FEMALE"; }
            };

            Employees = new List<Employee>()
            {
                new Employee() {Codigo = 1, Name = "CARLOS", Gender = "MALE", Birthday = new DateTime(1971, 7, 23)}, 
            new Employee() {Codigo = 2, Name = "ANDREA", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},
            new Employee() {Codigo = 3, Name = "JOSE", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},
            new Employee() {Codigo = 4, Name = "CHICHI", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},
            new Employee() {Codigo = 5, Name = "MILU", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},
            new Employee() {Codigo = 6, Name = "MIA", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)}
            };            
            dglist.ItemsSource = Employees;

            var cvs = (FindResource(&#34;StatusItems&#34;) as CollectionViewSource);
            cvs.Source = GenderList;
        }

public class Employee
        {
            public int Codigo { get; set; }
            public string Name { get; set; }
            public string Gender { get; set; }
            public DateTime Birthday { get; set; }
        }

.xaml

<Window.Resources>
        <CollectionViewSource x:Key="StatusItems"/>
 </Window.Resources>
.
.
.
<DataGridTemplateColumn Header="Combo">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox BorderThickness="0"
                                ItemsSource="{Binding Source={StaticResource StatusItems}}"
                                SelectedValue="{Binding GenderList, UpdateSourceTrigger=PropertyChanged}"
                                DisplayMemberPath="Descripcion"
                                SelectedValuePath="Codigo"
                                      />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,671 questions
0 comments No comments
{count} votes

7 answers

Sort by: Most helpful
  1. Xiaodi Yan 876 Reputation points MVP
    2020-06-09T23:11:39.95+00:00

    Update: Sorry I didn't notice the CellTemplate. @CarlosLino-2367 please ignore my answer.

    Hi, I think the error is when you set the data-binding for the ComboBox, you use GederList as the SelectedValue. I guess you should use a property of your Employee model. Also, I found that the SelectedValuePath is Codigo so it makes another error because in your Employee model, the Gender property is a string, which should be the Description property. Could you please try this:

     <ComboBox BorderThickness="0"
                                      ItemsSource="{Binding Source={StaticResource StatusItems}}"
                                      SelectedValue="{Binding Gender, UpdateSourceTrigger=PropertyChanged}"
                                      DisplayMemberPath="Descripcion"
                                      SelectedValuePath="Descripcion"
                                            />
    

    Just a guess and let me know if it doesn't work. Thanks.

    1 person found this answer helpful.

  2. Peter Fleischer (former MVP) 19,231 Reputation points
    2020-06-10T07:19:33.297+00:00

    Hi, try following demo. Combobox in CellTemplate doesn't work. It's necessary to use CellEditingTemplate. I think you want use single click edit like in following demo. If you use in Employee property Gender as string you must use Description in GenderList.

    XAML

    <Window x:Class="WpfApp1.Window43"  
            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:WpfApp1"  
            mc:Ignorable="d"  
            Title="Window43" Height="450" Width="800" Loaded="Window_Loaded">  
      <Window.Resources>  
        <CollectionViewSource x:Key="StatusItems"/>  
      </Window.Resources>  
      <Grid>  
        <DataGrid x:Name="dglist" PreviewMouseLeftButtonDown="dglist_PreviewMouseLeftButtonDown">  
          <DataGrid.Columns>  
            <DataGridTemplateColumn Header="Combo">  
              <DataGridTemplateColumn.CellTemplate>  
                <DataTemplate>  
                  <TextBlock Text="{Binding Gender}" />  
                </DataTemplate>  
              </DataGridTemplateColumn.CellTemplate>  
              <DataGridTemplateColumn.CellEditingTemplate>  
                <DataTemplate>  
                  <ComboBox BorderThickness="0"  
                            IsDropDownOpen="True"  
                            ItemsSource="{Binding Source={StaticResource StatusItems}}"  
                            DisplayMemberPath="Descripcion"  
                            SelectedValuePath="Descripcion"  
                            SelectedValue="{Binding Gender, UpdateSourceTrigger=PropertyChanged}"/>  
                </DataTemplate>  
              </DataGridTemplateColumn.CellEditingTemplate>  
            </DataGridTemplateColumn>  
          </DataGrid.Columns>  
        </DataGrid>  
      </Grid>  
    </Window>  
    

    CodeBehind:

    using System;  
    using System.Collections.Generic;  
    using System.Windows;  
    using System.Windows.Controls;  
    using System.Windows.Data;  
    using System.Windows.Input;  
    using System.Windows.Media;  
      
    namespace WpfApp1  
    {  
      /// <summary>  
      /// Interaction logic for Window43.xaml  
      /// </summary>  
      public partial class Window43 : Window  
      {  
        public Window43()  
        {  
          InitializeComponent();  
        }  
      
        private void Window_Loaded(object sender, RoutedEventArgs e)  
        {  
          LoadEmployee();  
        }  
      
        public List<Gender> GenderList { get; set; }  
        public List<Employee> Employees { get; set; }  
      
        void LoadEmployee()  
        {  
          GenderList = new List<Gender>() {  
            new Gender() { Codigo = 100, Descripcion = "MALE" },  
            new Gender() { Codigo = 200, Descripcion = "FEMALE" } };  
      
          Employees = new List<Employee>() {  
            new Employee() { Codigo = 1, Name = "CARLOS", Gender = "MALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 2, Name = "ANDREA", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 3, Name = "JOSE", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 4, Name = "CHICHI", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 5, Name = "MILU", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 6, Name = "MIA", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)}};  
      
          dglist.ItemsSource = Employees;  
      
          var cvs = (FindResource("StatusItems") as CollectionViewSource);  
          cvs.Source = GenderList;  
        }  
      
      
        public class Gender  
        {  
          public int Codigo { get; set; }  
          public string Descripcion { get; set; }  
        }  
      
        public class Employee  
        {  
          public int Codigo { get; set; }  
          public string Name { get; set; }  
          public string Gender { get; set; }  
          public DateTime Birthday { get; set; }  
        }  
      
        private void dglist_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)  
        {  
          var mbe = e as MouseButtonEventArgs;  
          DependencyObject obj = null;  
          obj = mbe?.OriginalSource as DependencyObject;  
          while (obj != null && !(obj is DataGridCell)) obj = VisualTreeHelper.GetParent(obj);  
          DataGridCell cell = null;  
          DataGrid dataGrid = null;  
          if (obj != null) cell = obj as DataGridCell;  
          if (cell != null && !cell.IsEditing && !cell.IsReadOnly)  
          {  
            if (!cell.IsFocused) cell.Focus();  
            dataGrid = FindVisualParent<DataGrid>(cell);  
            if (dataGrid != null)  
            {  
              if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)  
              {  
                if (!cell.IsSelected) cell.IsSelected = true;  
              }  
              else  
              {  
                var row = FindVisualParent<DataGridRow>(cell);  
                if (row != null && !row.IsSelected) row.IsSelected = true;  
              }  
            }  
          }  
        }  
      
        private T FindVisualParent<T>(UIElement element) where T : UIElement  
        {  
          UIElement parent = element;  
          while (parent != null)  
          {  
            T correctlyTyped = parent as T;  
            if (correctlyTyped != null) return correctlyTyped;  
            parent = VisualTreeHelper.GetParent(parent) as UIElement;  
          }  
          return null;  
        }  
      }  
    }  
    

    9715-10-06-2020-09-23-53.gif

    1 person found this answer helpful.
    0 comments No comments

  3. Peter Fleischer (former MVP) 19,231 Reputation points
    2020-06-10T18:17:19.627+00:00

    Hi, if you show Combobox in CellTemplate each select (in edit mode) take effect in every row. You must use CellTemplate with TextBlock and CellEditingTemplate with Combobox. If you display Description and use ID for reference beetween Master and Child you must convert in TextBlock binded value.

    Try following demo:

    <Window x:Class="WpfApp1.Window43"  
            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:WpfApp43"  
            mc:Ignorable="d"  
            Title="Window43" Height="450" Width="800" Loaded="Window_Loaded">  
      <Window.Resources>  
        <CollectionViewSource x:Key="StatusItems"/>  
        <local:Converter x:Key="conv"/>  
      </Window.Resources>  
      <Grid>  
        <DataGrid x:Name="dglist" PreviewMouseLeftButtonDown="dglist_PreviewMouseLeftButtonDown">  
          <DataGrid.Columns>  
            <DataGridTemplateColumn Header="Combo">  
              <DataGridTemplateColumn.CellTemplate>  
                <DataTemplate>  
                  <Grid>  
                    <TextBlock Text="{Binding Codigo, Converter={StaticResource conv}, ConverterParameter={StaticResource StatusItems}}" />  
                    <Polyline Points="5 5 8 8 11 5" Stroke="Black" HorizontalAlignment="Right" Margin="0 0 5 0"/>  
                  </Grid>  
                </DataTemplate>  
              </DataGridTemplateColumn.CellTemplate>  
              <DataGridTemplateColumn.CellEditingTemplate>  
                <DataTemplate>  
                  <ComboBox BorderThickness="0"  
                            IsDropDownOpen="True"  
                            ItemsSource="{Binding Source={StaticResource StatusItems}}"  
                            DisplayMemberPath="Descripcion"  
                            SelectedValuePath="Codigo"  
                            SelectedValue="{Binding Codigo, UpdateSourceTrigger=PropertyChanged}"/>  
                </DataTemplate>  
              </DataGridTemplateColumn.CellEditingTemplate>  
            </DataGridTemplateColumn>  
          </DataGrid.Columns>  
        </DataGrid>  
      </Grid>  
    </Window>  
    

    Code:

    using System;  
    using System.Collections.Generic;  
    using System.Globalization;  
    using System.Linq;  
    using System.Windows;  
    using System.Windows.Controls;  
    using System.Windows.Data;  
    using System.Windows.Input;  
    using System.Windows.Media;  
    using WpfApp43;  
      
    namespace WpfApp1  
    {  
      /// <summary>  
      /// Interaction logic for Window43.xaml  
      /// </summary>  
      public partial class Window43 : Window  
      {  
        public Window43()  
        {  
          InitializeComponent();  
        }  
      
        private void Window_Loaded(object sender, RoutedEventArgs e)  
        {  
          LoadEmployee();  
        }  
      
        public List<GenderList> GenderList { get; set; }  
        public List<Employee> Employees { get; set; }  
      
        void LoadEmployee()  
        {  
          GenderList = new List<GenderList>() {  
            new GenderList() { Codigo = 1, Descripcion = "MALE" },  
            new GenderList() { Codigo = 2, Descripcion = "FEMALE" } };  
      
          Employees = new List<Employee>() {  
            new Employee() { Codigo = 1, Name = "CARLOS", Gender = "MALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 2, Name = "ANDREA", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 2, Name = "JOSE", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 2, Name = "CHICHI", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 2, Name = "MILU", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)},  
            new Employee() { Codigo = 2, Name = "MIA", Gender = "FEMALE", Birthday = new DateTime(1971, 7, 23)}};  
      
          dglist.ItemsSource = Employees;  
      
          var cvs = (FindResource("StatusItems") as CollectionViewSource);  
          cvs.Source = GenderList;  
        }  
      
        public class Employee  
        {  
          public int Codigo { get; set; }  
          public string Name { get; set; }  
          public string Gender { get; set; }  
          public DateTime Birthday { get; set; }  
        }  
      
        private void dglist_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)  
        {  
          var mbe = e as MouseButtonEventArgs;  
          DependencyObject obj = null;  
          obj = mbe?.OriginalSource as DependencyObject;  
          while (obj != null && !(obj is DataGridCell)) obj = VisualTreeHelper.GetParent(obj);  
          DataGridCell cell = null;  
          DataGrid dataGrid = null;  
          if (obj != null) cell = obj as DataGridCell;  
          if (cell != null && !cell.IsEditing && !cell.IsReadOnly)  
          {  
            if (!cell.IsFocused) cell.Focus();  
            dataGrid = FindVisualParent<DataGrid>(cell);  
            if (dataGrid != null)  
            {  
              if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)  
              {  
                if (!cell.IsSelected) cell.IsSelected = true;  
              }  
              else  
              {  
                var row = FindVisualParent<DataGridRow>(cell);  
                if (row != null && !row.IsSelected) row.IsSelected = true;  
              }  
            }  
          }  
        }  
      
        private T FindVisualParent<T>(UIElement element) where T : UIElement  
        {  
          UIElement parent = element;  
          while (parent != null)  
          {  
            T correctlyTyped = parent as T;  
            if (correctlyTyped != null) return correctlyTyped;  
            parent = VisualTreeHelper.GetParent(parent) as UIElement;  
          }  
          return null;  
        }  
      }  
    }  
      
    namespace WpfApp43  
    {  
      public class GenderList  
      {  
        public int Codigo { get; set; }  
        public string Descripcion { get; set; }  
      }  
      
      public class Converter : IValueConverter  
      {  
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
        {  
          CollectionViewSource cvs = parameter as CollectionViewSource;  
          var col = cvs.Source as List<GenderList>;  
          return col.Where((item) => item.Codigo == (int)value).FirstOrDefault().Descripcion;  
        }  
      
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)  
        {  
          throw new NotImplementedException();  
        }  
      }  
    }  
    

    9782-10-06-2020-20-16-16.gif

    1 person found this answer helpful.
    0 comments No comments

  4. Carlos Lino 6 Reputation points
    2020-06-11T19:12:46.44+00:00

    *anonymous user*-3316 I see you use the PreviewMouseLeftButtonDown event. What if I use the arrow down of the keyboard?

    If you have a better way to populate a Combobox but using a Datagrid template in a simple way that would be great! I accept suggestions, guys.

    I just started working with WPF and don't have much experience with this technology.

    Much appreciate it!

    1 person found this answer helpful.

  5. Carlos Lino 6 Reputation points
    2020-06-10T17:00:58.32+00:00

    Hi, thanks for your reply guys.

    Clarifying the scenario:

    The column Gender in the DataGrid is only for information, I don't want to bind it to the combo box. The Combobox has to be for selecting only without being bound to the Gender column. The Combobox has its own ID which is "Codigo" (int data type) and the Display name is "Descripcion" (string data type).

    *anonymous user*-3316 I see in your demo when you select the Combo column, the value goes away, it doesn't stay there. Also, when you use DataTemplate the whole Combobox should display in the cell.

    This is the class for the GenderList that I was missing here:

     public class GenderList
         {
             public int Codigo { get; set; }
             public string Descripcion { get; set; }
     
             public GenderList()
             {
                 Codigo = 0;
                 Descripcion = "";
             }
         }
    
    0 comments No comments