Как получить уровень ячеек ComboBox для WPF DataGrid?
Похоже, что WFP DataGridComboBoxColumn использует один элемент ItemsSource для всех ячеек в этом столбце. У меня есть случай, когда элементы ComboBox зависят от другой ячейки в той же строке. Мне удалось заполнить ItemsSource в событии PreparingCellForEdit. Однако это не работает так, как хотелось бы. Изначально все ячейки в этом столбце пустые. Как только я заполняю ItemsSource для ComboBox этого столбца, все связанные ячейки (с тем же источником элементов) показывают значения. Однако если щелкнуть ячейку другого типа (заполняется другой источник элементов), все значения исчезнут, а в ячейках нового типа отобразятся значения. Вы можете использовать только один набор элементов источника для столбца? Я не могу поверить, что это правда. Я что-то пропустил? Любое решение?
2 ответа
Благодаря примеру Джонатана я решил свою проблему следующим образом. Я изменил код Джонатана, чтобы подчеркнуть свое решение. Я удалил свойство Territory из его примера, потому что оно мне не нужно для моей проблемы.
Есть две колонки. Первый столбец - Государство. Второй столбец - StateCandidate. Столбец состояния связан со списком состояний, а столбец StateCandidate связан со списком состояний. Ключевым моментом является то, что список StateCandidates воссоздается при изменении состояния. Таким образом, в каждой строке может быть разный список StateCandidates (в зависимости от выбранного состояния).
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));
}
}
}
Обратите внимание, что при изменении состояния он не будет обновлять список StateCandidates до тех пор, пока не будет выбрана другая строка, что является отдельной проблемой, с которой я буду бороться. Кто-нибудь знает, как я могу заставить совершить?
Еще раз спасибо Джонатану за его вдохновение. Я буду продолжать искать лучшее решение.
Вы, вероятно, не можете сделать это надежно. Сетка может повторно использовать поле со списком или случайным образом создавать / уничтожать его.
Случайно я просто работаю на экране, который делает именно это. Учитывая эти...
- Каждая строка в сетке связана с объектом типа Trade.
- Каждая сделка имеет государственную собственность
- Каждая сделка имеет свойство TerritoryCanidates
- Изменение свойства State приведет к изменению свойства TerritoryCanidates
Это дает мне возможность привязать ItemsSource к свойству TerritoryCanidates. Что, в свою очередь, DataGrid будет соблюдать при любых обстоятельствах.
<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