It looks WFP DataGridComboBoxColumn is using a single ItemsSource for all cells in this column. I have a case where the ComboBox items are dependent on the other cell in the same row. I managed to populate the ItemsSource in PreparingCellForEdit event. However, it doesn't work as desired. Initially, all cells in this column is empty. Once I populate the ItemsSource for this column's ComboBox, all related cells (with the same items source) are showing values. However, if I click another type of cell (a different items source is populated), all values disappear and the new type cells show values. You can only use one set of Items Source for a column? I can't believe it is true. Did I miss anything? Any workaround?
You probably can't do it reliably. The grid may reuse the combo box or randomly create/destroy it.
By chance I just happen to be working on a screen that does just that. Given these...
This gives me the ability to bind the ItemsSource to the TerritoryCanidates property. Which in turn the DataGrid will honor under all circumstances.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid Name="Zoom" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="State">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding State}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding State}" ItemsSource="{Binding StateCanidates}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Territory">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Territory}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Territory}" ItemsSource="{Binding TerritoryCanidates}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Imports System.ComponentModel
Class MainWindow
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Dim x As New List(Of Model)
x.Add(New Model)
x.Add(New Model)
x.Add(New Model)
Zoom.ItemsSource = x
End Sub
End Class
Class Model
Implements INotifyPropertyChanged
Public ReadOnly Property StateCanidates As List(Of String)
Get
Return New List(Of String) From {"CA", "TX", "NY"}
End Get
End Property
Public ReadOnly Property TerritoryCanidates As List(Of String)
Get
If State = "" Then Return Nothing
Return New List(Of String) From {State & "1", State & "2"}
End Get
End Property
Private m_State As String
Public Property State() As String
Get
Return m_State
End Get
Set(ByVal value As String)
m_State = value
OnPropertyChanged("State")
OnPropertyChanged("TerritoryCanidates")
End Set
End Property
Private m_Territory As String
Public Property Territory() As String
Get
Return m_Territory
End Get
Set(ByVal value As String)
m_Territory = value
OnPropertyChanged("Territory")
End Set
End Property
Public Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Thanks to Jonathan's example, I resolved my problem as follows. I modifiedy Jonathan's code to highlight my solution. I removed the Territory property from his example because I don't need it for my problem.
There are two columns. First column is State. Second column is StateCandidate. State column is bind to a States list, and StateCandidate column is bind to a StateCandidates list. The key point is that the StateCandidates list is recreated when State is changed. So, there could be a different list of StateCandidates in each row (based on the selected State).
MainWindow.xaml
<Window x:Class="WpfTest1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid Name="Zoom" AutoGenerateColumns="False" Background="DarkGray" RowHeaderWidth="50" HeadersVisibility="All">
<DataGrid.Columns>
<DataGridTemplateColumn x:Name="colState" Header="State" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding State}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding State}" ItemsSource="{Binding States}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn x:Name="colStateCandiate" Header="State Candidate" Width="200">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding StateCandidate}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding StateCandidate}" ItemsSource="{Binding StateCandidates}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using System.ComponentModel;
namespace WpfTest1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Model> list = new List<Model>();
list.Add(new Model() { State = "TX", StateCandidate = "TX2" });
list.Add(new Model() { State = "CA" });
list.Add(new Model() { State = "NY", StateCandidate = "NY1" });
list.Add(new Model() { State = "TX" });
list.Add(new Model() { State = "AK" });
list.Add(new Model() { State = "MN" });
Zoom.ItemsSource = list;
Zoom.PreparingCellForEdit += new EventHandler<DataGridPreparingCellForEditEventArgs>(Zoom_PreparingCellForEdit);
}
void Zoom_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
{
if (e.Column == colStateCandiate)
{
DataGridCell cell = e.Column.GetCellContent(e.Row).Parent as DataGridCell;
cell.IsEnabled = (e.Row.Item as Model).StateCandidates != null;
}
}
}
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _state;
private List<string> _states = new List<string>() { "CA", "TX", "NY", "IL", "MN", "AK" };
private string _stateCandidate;
private List<string> _stateCandidates;
public string State
{
get { return _state; }
set
{
if (_state != value)
{
_state = value;
_stateCandidate = null;
if (_state == "CA" || _state == "TX" || _state == "NY")
_stateCandidates = new List<string> { _state + "1", _state + "2" };
else
_stateCandidates = null;
OnPropertyChanged("State");
}
}
}
public List<string> States
{
get { return _states; }
}
public string StateCandidate
{
get { return _stateCandidate; }
set
{
if (_stateCandidate != value)
{
_stateCandidate = value;
OnPropertyChanged("StateCandidate");
}
}
}
public List<string> StateCandidates
{
get { return _stateCandidates; }
}
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Note that, when State is changed, it won't update StateCandidates list until a different row is selected, which is a separated issue I'll be fighting. Do anybody know how I can force commit?
Thanks to Jonathan again for his inspiration. I'll keep looking for a better solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With