我在使用CollectionViews时遇到了一个非常奇怪的行为,并想知道是否有人可以解释我这种行为。
我有一个ObservableCollection,我们称之为_sourcelist。然后我以这种方式创建_sourcelist的几个CollectionView:
CollectionViewSource cvs = new CollectionViewSource() { Source = _sourcelist };
ICollectionView list = cvs.View;
这些CollectionViews每个都使用不同的过滤器和一个没有过滤器的CollectionView(因此代表原始源)。在我的应用程序中,我有一个TabControl,其中每个TabItem包含一个ListBox,其中一个CollectionViews作为ItemsSource。当我知道从源列表中删除一个项目时,带有没有过滤器的CollectionView的ListBox会更新,但包含已过滤的CollectionViews并包含已删除项目的其他ListBox不会更新。因此删除的项目仍保留在ListBoxes中。
现在,奇怪的行为如下: 当我在上面编写的代码后添加以下代码时:
list.CollectionChanged += (o,e) => { };
然后其他ListBox也会更新。 为什么?我的意思是,我刚刚为CollectionChanged事件注册了一个事件处理程序,它什么也没做!
所以,这是我的代码示例。我知道这是一个很大的代码,但它已准备好运行并重现奇怪的行为。
当您运行代码并尝试删除“全部”列表中的项目时,将不会删除已过滤的“30岁以下”或“超过30”列表中的相应项目。现在取消注释ListViewModel中的注释行(它只是一个空的Eventhandler注册),然后将删除已过滤列表中的相应项。
如果您有任何疑问,请咨询! : - )
感谢您对解决问题的兴趣。 : - )
P.S。:RelayCommand来自程序集:Microsoft.TeamFoundation.Controls
MainWindow.xaml:
<Window x:Class="CollectionTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CollectionTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ListViewModel}">
<ListBox ItemsSource="{Binding List}" SelectedItem="{Binding Selected}" BorderBrush="Transparent" Margin="0" HorizontalContentAlignment="Stretch"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:PersonViewModel}">
<TextBlock Margin="5,0" VerticalAlignment="Center" Text="{Binding Name}"/>
</DataTemplate>
<Style x:Key="{x:Type TabItem}" TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Name}"/>
</Style>
</Window.Resources>
<DockPanel>
<Button DockPanel.Dock="Bottom" Name="DeleteButton" Content="Delete" Command="{Binding DeleteCommand}"/>
<TabControl Name="TabControl" ItemsSource="{Binding Lists}" Padding="0" SelectedItem="{Binding Selected}"/>
</DockPanel>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private MainViewModel mvm;
public MainWindow()
{
InitializeComponent();
mvm = new MainViewModel(CreateRepo());
this.DataContext = mvm;
}
private Repository CreateRepo()
{
Repository repo = new Repository();
for (int i = 20; i < 45; i++)
{
Person p = new Person() { Name = "Person" + i, Age = i };
repo.Add(p);
}
return repo;
}
}
人:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
存储库:
public class Repository
{
private readonly List<Person> _list;
public event EventHandler<RepositoryChangedEventArgs> Added;
public event EventHandler<RepositoryChangedEventArgs> Removed;
public Repository()
{
this._list = new List<Person>();
}
public void Add(Person person)
{
if (person == null)
{
throw new ArgumentNullException("person");
}
if (!this._list.Contains(person))
{
this._list.Add(person);
if (this.Added != null)
{
this.Added(this, new RepositoryChangedEventArgs(person));
}
}
}
public void Remove(Person person)
{
if (person == null)
{
throw new ArgumentNullException("person");
}
if (this._list.Contains(person))
{
this._list.Remove(person);
if (this.Removed != null)
{
this.Removed(this, new RepositoryChangedEventArgs(person));
}
}
}
public List<Person> Get()
{
return new List<Person>(this._list);
}
}
public class RepositoryChangedEventArgs : EventArgs
{
public Person Entity { get; set; }
public RepositoryChangedEventArgs(Person entity)
{
this.Entity = entity;
}
}
PersonViewModel:
public class PersonViewModel : INotifyPropertyChanged
{
private Person _person;
private Repository _repository;
public string Name
{
get
{
return this._person.Name;
}
set
{
if (this._person.Name != value)
{
this._person.Name = value;
this.OnPropertyChanged("Name");
}
}
}
public int Age
{
get
{
return this._person.Age;
}
set
{
if (this._person.Age != value)
{
this._person.Age = value;
this.OnPropertyChanged("Age");
}
}
}
public PersonViewModel(Person person, Repository repository)
{
this._person = person;
this._repository = repository;
}
public void Delete()
{
this._repository.Remove(this._person);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
ListViewModel:
public class ListViewModel : INotifyPropertyChanged
{
private IEnumerable _sourceList;
private ICollectionView _list;
private PersonViewModel _selected;
private string _name;
public ICollectionView List
{
get
{
return this._list;
}
}
public PersonViewModel Selected
{
get
{
return this._selected;
}
set
{
if (this._selected != value)
{
this._selected = value;
this.OnPropertyChanged("Selected");
}
}
}
public string Name
{
get
{
return this._name;
}
set
{
if (this._name != value)
{
this._name = value;
this.OnPropertyChanged("Name");
}
}
}
public ListViewModel(string name, IEnumerable list)
{
this.Name = name;
this._sourceList = list;
CollectionViewSource cvs = new CollectionViewSource() { Source = list };
this._list = cvs.View;
// cvs.View.CollectionChanged += (o, e) => { }; // uncomment this line to let it work
}
public ListViewModel(String name, IEnumerable list, Predicate<object> filter)
: this(name, list)
{
this._list.Filter = filter;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
MainViewModel:
public class MainViewModel : INotifyPropertyChanged
{
protected ObservableCollection<PersonViewModel> _sourceList;
protected ObservableCollection<ListViewModel> _lists;
protected Repository _repository;
protected Dictionary<Person, PersonViewModel> _dictionary;
private RelayCommand _deleteCommand;
private ListViewModel _selected;
public ListViewModel Selected
{
get
{
return this._selected;
}
set
{
if (this._selected != value)
{
this._selected = value;
this.OnPropertyChanged("Selected");
}
}
}
public RelayCommand DeleteCommand
{
get
{
if (this._deleteCommand == null)
{
this._deleteCommand = new RelayCommand(this.ExecutedDelete, this.CanExecuteDelete);
}
return this._deleteCommand;
}
}
public IEnumerable<ListViewModel> Lists
{
get
{
return this._lists;
}
}
public MainViewModel(Repository repository)
{
this._sourceList = new ObservableCollection<PersonViewModel>();
this._lists = new ObservableCollection<ListViewModel>();
this._dictionary = new Dictionary<Person, PersonViewModel>();
this._repository = repository;
this._repository.Added += this.OnAdded;
this._repository.Removed += this.OnRemoved;
this.CreateSourceList(repository);
this.CreateLists();
if (this._lists.Count > 0)
{
this.Selected = this._lists[0];
}
}
protected void CreateSourceList(Repository repository)
{
foreach (Person person in repository.Get())
{
PersonViewModel pvm = new PersonViewModel(person, repository);
this._dictionary.Add(person, pvm);
this._sourceList.Add(pvm);
}
}
protected void CreateLists()
{
this._lists.Add(new ListViewModel("All", this._sourceList));
this._lists.Add(new ListViewModel("Under 30", this._sourceList,
delegate(object o)
{
PersonViewModel pvm = o as PersonViewModel;
return (pvm != null && pvm.Age < 30);
}));
this._lists.Add(new ListViewModel("Over 30", this._sourceList,
delegate(object o)
{
PersonViewModel pvm = o as PersonViewModel;
return (pvm != null && pvm.Age > 30);
}));
}
protected void Remove(Person person)
{
PersonViewModel pvm = this._dictionary[person];
this._dictionary.Remove(person);
this._sourceList.Remove(pvm);
}
protected void OnAdded(object sender, RepositoryChangedEventArgs args)
{
Person addedPerson = args.Entity;
if (addedPerson != null)
{
PersonViewModel pvm = new PersonViewModel(addedPerson, this._repository);
this._dictionary.Add(addedPerson, pvm);
this._sourceList.Add(pvm);
}
}
protected void OnRemoved(object sender, RepositoryChangedEventArgs args)
{
Person removedPerson = args.Entity;
if (removedPerson != null)
{
this.Remove(removedPerson);
}
}
private bool CanExecuteDelete(object o)
{
return true;
}
private void ExecutedDelete(object o)
{
PersonViewModel pvm = null;
if (this.Selected != null && (pvm = this.Selected.Selected) != null)
{
pvm.Delete();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}