Я сократил свой код до как можно меньшего размера тестового примера, но он все еще довольно большой; Я надеюсь, что это довольно просто.
Foo имеет ObservableCollection типов Bar и Baz. База хранит ObservableCollection ссылок на объекты Bar в Foo.
В главном окне есть ListBox всех объектов Baz в Foo, которые проходят через конвертер, чтобы сделать их простой строкой. SelectedItem устанавливается как свойство DependencyProperty окна для удобства ссылки. Позже в этом окне перечислены все объекты Bar в Foo, которые можно добавить / удалить с помощью этого свойства DependencyProperty (SelectedBaz). В целях отладки добавлен еще один ListBox, который показывает объекты Bar SelectedBaz.
Что происходит, обновляется SelectedBaz, обновляется Baz в коллекции ObservableCollection, удерживаемой Foo, вызывается событие CollectionChanged для коллекции Foo базы Baz, но ListBox с конвертером никогда не обновляется.
Я попытался разбрызгать немного «Mode = TwoWay», но безуспешно (удалено, поскольку они не дали эффекта). Я пытался использовать SelectedValue против SelectedItem (кажется, что SelectedItem является правильным способом сделать это из моего исследования, поэтому я оставил его как таковой). Я пытался вручную запустить обновление цели привязки в Baz ListBox при нажатии кнопок добавления / удаления, но это не имело никакого эффекта.
Затем я расстроился и попытался взломать его и использовать целое число с SelectedIndex, MultiBinding, MultiValueConverter и т. Д. И т. Д., И обнаружил, что у меня та же проблема; источник обновлен, но не является целью в привязке Baz ListBox.
Итак, мы здесь.
Foo.cs
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows.Data;
namespace WpfApp1
{
public class Foo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Foo()
{
bars.CollectionChanged += Bars_CollectionChanged;
bazes.CollectionChanged += Bazes_CollectionChanged;
bars.Add(new Bar("Bar 1"));
bars.Add(new Bar("Bar 2"));
bars.Add(new Bar("Bar 3"));
bazes.Add(new Baz("Baz 1")
{
Bars = { bars[0] }
});
bazes.Add(new Baz("Baz 2")
{
Bars = { bars[1] }
});
bazes.Add(new Baz("Baz 3")
{
Bars = { bars[0], bars[1], bars[2] }
});
}
public ObservableCollection<Bar> Bars
{
get
{
return bars;
}
}
public ObservableCollection<Baz> Bazes
{
get
{
return bazes;
}
}
private void Bars_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("Bars");
}
private void Bazes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("Bazes");
}
private void NotifyPropertyChanged([CallerMemberName] string caller = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
private ObservableCollection<Bar> bars = new ObservableCollection<Bar>();
private ObservableCollection<Baz> bazes = new ObservableCollection<Baz>();
}
public class Bar : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Bar(string name)
{
this.name = name;
}
public string Name
{
get
{
return name;
}
set
{
if (name != value)
{
name = value;
NotifyPropertyChanged();
}
}
}
private void NotifyPropertyChanged([CallerMemberName] string caller = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
private string name = "";
}
public class Baz : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Baz(string name)
{
this.name = name;
bars.CollectionChanged += Bars_CollectionChanged;
}
public ObservableCollection<Bar> Bars
{
get
{
return bars;
}
}
public string Name
{
get
{
return name;
}
set
{
if (name != value)
{
name = value;
NotifyPropertyChanged();
}
}
}
private void Bars_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("Bars");
}
private void NotifyPropertyChanged([CallerMemberName] string caller = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
}
private ObservableCollection<Bar> bars = new ObservableCollection<Bar>();
private string name = "";
}
public class BazToString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Baz b = value as Baz;
string s = "Baz is " + b.Name + " ";
foreach (Bar bar in b.Bars)
{
s += "with a Bar " + bar.Name + " ";
}
return s;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
MainWindow.xaml
<Window x:Class="WpfApp1.MainWindow"
x:Name="Main"
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="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:Foo />
</Window.DataContext>
<Window.Resources>
<local:BazToString x:Key="BazToString" />
</Window.Resources>
<Grid>
<ListBox Width="300" Height="150" Margin="10,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" ItemsSource="{Binding Bazes}" SelectedItem="{Binding ElementName=Main, Path=SelectedBaz}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Converter={StaticResource BazToString}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="ListBoxBarSelector" Width="300" Height="150" Margin="10,170,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" ItemsSource="{Binding Bars}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Width="300" Height="150" Margin="320,170,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" ItemsSource="{Binding ElementName=Main, Path=SelectedBaz.Bars}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Width="100" Height="30" Margin="10,330,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="ButtonAddBar_Click" Content="Add Bar" />
<Button Width="100" Height="30" Margin="120,330,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="ButtonDelBar_Click" Content="Delete Bar" />
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public Baz SelectedBaz
{
get
{
return (Baz)GetValue(SelectedBazProperty);
}
set
{
SetValue(SelectedBazProperty, value);
}
}
private void ButtonAddBar_Click(object sender, RoutedEventArgs e)
{
Bar bar = ListBoxBarSelector.SelectedItem as Bar;
if (bar != null && SelectedBaz != null && !SelectedBaz.Bars.Contains(bar))
{
SelectedBaz.Bars.Add(bar);
}
}
private void ButtonDelBar_Click(object sender, RoutedEventArgs e)
{
Bar bar = ListBoxBarSelector.SelectedItem as Bar;
if (bar != null && SelectedBaz != null && SelectedBaz.Bars.Contains(bar))
{
SelectedBaz.Bars.Remove(bar);
}
}
private static readonly DependencyProperty SelectedBazProperty =
DependencyProperty.Register(
"SelectedBaz",
typeof(Baz),
typeof(MainWindow),
new PropertyMetadata());
}
}