Я пытаюсь понять концепцию привязки данных WPF на простом примере, но, похоже, я не совсем понял смысл всего этого.
Примером является один из каскадных выпадающих списков; XAML выглядит следующим образом:
<Window x:Class="CascadingDropDown.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="496" Width="949" Loaded="Window_Loaded">
<Grid>
<ComboBox Name="comboBox1" ItemsSource="{Binding}" DisplayMemberPath="Key" SelectionChanged="comboBox1_SelectionChanged" />
<ComboBox Name="comboBox2" ItemsSource="{Binding}" DisplayMemberPath="Name" />
</Grid>
</Window>
Это код формы:
public partial class MainWindow : Window
{
private ObservableCollection<ItemA> m_lstItemAContext = new ObservableCollection<ItemA>();
private ObservableCollection<ItemB> m_lstItemBContext = new ObservableCollection<ItemB>();
private IEnumerable<ItemB> m_lstAllItemB = null;
public MainWindow()
{
InitializeComponent();
this.comboBox1.DataContext = m_lstItemAContext;
this.comboBox2.DataContext = m_lstItemBContext;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var lstItemA = new List<ItemA>() { new ItemA("aaa"), new ItemA("bbb"), new ItemA("ccc") };
var lstItemB = new List<ItemB>() { new ItemB("aaa", "a11"), new ItemB("aaa", "a22"), new ItemB("bbb", "b11"), new ItemB("bbb", "b22") };
initPicklists(lstItemA, lstItemB);
}
private void initPicklists(IEnumerable<ItemA> lstItemA, IEnumerable<ItemB> lstItemB)
{
this.m_lstAllItemB = lstItemB;
this.m_lstItemAContext.Clear();
lstItemA.ToList().ForEach(a => this.m_lstItemAContext.Add(a));
}
#region Control event handlers
private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox ddlSender = (ComboBox)sender;
ItemA itemaSelected = (ItemA)ddlSender.SelectedItem;
var lstNewItemB = this.m_lstAllItemB.Where(b => b.KeyA == itemaSelected.Key);
this.m_lstItemBContext.Clear();
lstNewItemB.ToList().ForEach(b => this.m_lstItemBContext.Add(b));
}
private void comboBox2_?(object sender, ?EventArgs e)
{
// disable ComboBox if empty
}
#endregion Control event handlers
}
А это мои классы данных:
class ItemA
{
public string Key { get; set; }
public ItemA(string sKey)
{
this.Key = sKey;
}
}
class ItemB
{
public string KeyA { get; set; }
public string Name { get; set; }
public ItemB(string sKeyA, string sName)
{
this.KeyA = sKeyA;
this.Name = sName;
}
}
Таким образом, всякий раз, когда элемент выбирается в comboBox1, соответствующие элементы должны отображаться в comboBox2. Это работает с текущим кодом, хотя я не уверен, идеален ли мой способ повторного заполнения соответствующей коллекции ObservableCollection.
Чего я не смог добиться, так это на самом деле реагировать на изменения в базовой коллекции comboBox2, например, для деактивации элемента управления, когда список пуст (т.е. когда в comboBox1 выбран «ccc»).
Конечно, я могу использовать обработчик события для события CollectionChanged ObservableCollection, и это будет работать в этом примере, но в более сложном сценарии, где DataContext ComboBox может измениться на совершенно другой объект (и, возможно, назад), это означало бы двойную зависимость - мне всегда приходилось бы переключать не только DataContext, но и обработчики событий назад и вперед. Мне это не кажется правильным, но я, вероятно, просто нахожусь на совершенно неверном пути по этому поводу.
По сути, я ищу событие, запускающее элемент управления, а не базовый список; не ObservableCollection, объявляющая «мое содержимое изменилось», а ComboBox, говорящий мне «что-то счастливое для моих предметов».
Что мне нужно сделать, или где я должен исправить свое восприятие всей концепции?