Собственная коллекция, измененная по предмету в коллекции
У меня есть ObservableCollection<T>
. Я привязал его к элементу управления ListBox, и я добавил SortDescriptions
в коллекцию Items в ListBox, чтобы сделать сортировку списка так, как я хочу.
Я хочу использовать список в ЛЮБОЙ, когда какое-либо свойство изменено на дочернем элементе.
Все мои дочерние элементы реализуют INotifyPropertyChanged
.
Ответы
Ответ 1
Грубая сила:
- Прикрепите обработчик к каждому событию PropertyChanged для каждого дочернего элемента
- Возьмите ListCollectionView из коллекции CollectionViewSource
- Обновление вызова.
EDIT:
Код для 1, 2 будет жить в вашем коде.
Для # 1 вы сделаете что-то вроде:
private void Source_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach( SomeItem item in e.NewItems)
{
item.PropertyChanged += new PropertyChangedEventHandler(_SomeItem_PropertyChanged);
}
break;
....
**HANDLE OTHER CASES HERE**
....
}
}
Для # 2 в вашем обработчике CollectionChanged вы сделаете что-то вроде:
private void _SomeItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
ListCollectionView lcv = (ListCollectionView)(CollectionViewSource.GetDefaultView(theListBox.ItemsSource));
lcv.Refresh();
}
EDIT2:
Однако в этом случае я бы сильно предлагал вам также проверить ListCollectionView.NeedsRefresh и обновлять только в том случае, если он установлен. Нет причин для повторной сортировки, если ваши свойства изменились, которые не влияют на сортировку.
Ответ 2
Это работает. Всякий раз, когда коллекция изменяется, она повторно сортирует коллекцию. Могу быть выполнимым более эффективным способом, но это его суть.
public partial class TestWindow : Window {
ObservableCollection<TestClass> oc;
public TestWindow() {
InitializeComponent();
// Fill in the OC for testing
oc = new ObservableCollection<TestClass>();
foreach( char c in "abcdefghieeddjko" ) {
oc.Add( new TestClass( c.ToString(), c.ToString(), c.GetHashCode() ) );
}
lstbox.ItemsSource = oc;
// Set up the sorting (this is how you did it.. doesn't work)
lstbox.Items.SortDescriptions.Add( new SortDescription("A", ListSortDirection.Ascending) );
// This is how we're going to do it
oc.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler( oc_Sort );
}
void oc_Sort( object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e ) {
// This sorts the oc and returns IEnumerable
var items = oc.OrderBy<TestClass, int>( ( x ) => ( x.C ) );
// Rest converst IEnumerable back to OC and assigns it
ObservableCollection<TestClass> temp = new ObservableCollection<TestClass>();
foreach( var item in items ) {
temp.Add( item );
}
oc = temp;
}
private void Button_Click( object sender, RoutedEventArgs e ) {
string a = "grrrr";
string b = "ddddd";
int c = 383857;
oc.Add( new TestClass( a, b, c ) );
}
}
public class TestClass : INotifyPropertyChanged {
private string a;
private string b;
private int c;
public TestClass( string f, string g, int i ) {
a = f;
b = g;
c = i;
}
public string A {
get { return a; }
set { a = value; OnPropertyChanged( "A" ); }
}
public string B {
get { return b; }
set { b = value; OnPropertyChanged( "B" ); }
}
public int C {
get { return c; }
set { c = value; OnPropertyChanged( "C" ); }
}
#region onpropertychanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged( string propertyName ) {
if( this.PropertyChanged != null ) {
PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
}
}
#endregion
}
XAML:
<Window x:Class="ServiceManager.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="500" Width="500">
<DockPanel>
<ListBox ItemsSource="{Binding}" x:Name="lstbox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=A}"/>
<Label Content="{Binding Path=B}"/>
<Label Content="{Binding Path=C}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Click="Button_Click" Content="Click" />
</DockPanel>
</Window>