(真的)ObservableCollection<;T>;不更新UI
本文关键字:gt UI 更新 lt 真的 ObservableCollection | 更新日期: 2023-09-27 18:27:04
我有一个类型为Emblem
的对象列表,这些对象显示在LongListMultiSelector
中。我只想展示那些尚未实现的目标。我可以选择一个或多个项目并将其更改为IsAchieved = true
,但问题是它们不会立即消失,UI也不会自动更新。
我认为这不会是一个问题,因为我使用了ObservableCollection<T>
。然后我发现,如果项目的属性发生更改,则不会通知集合。因此,a实现了INotifyPropertyChanged
接口,但这也不起作用。
在SO上,我发现了以下问题(以及更多)与此问题相同:
- 项目更改时通知ObservableCollection
- ObservableCollection没有注意到其中的Item何时更改(即使使用INotifyPropertyChanged)
- 如何检测我的ObservableCollection中的项目是否已更改
我也尝试过实现TrulyObservableCollection<T>
的用法,但也没有结果。这是我的
XAML控件:
<toolkit:LongListMultiSelector Name="EmblemsList"
ItemsSource="{Binding Emblems}"
Background="Transparent"
LayoutMode="List"
ItemTemplate="{StaticResource ItemTemplate}" />
项目通过EmblemsViewModel
:绑定
public class EmblemsViewModel
{
public EmblemsViewModel()
{
Emblems = new TrulyObservableCollection<Emblem>();
}
public TrulyObservableCollection<Emblem> Emblems { get; set; }
}
//Usage on the page
DataContext = new EmblemsViewModel { Emblems = DB.GetEmblems() }
Emblem
类如下:
public class Emblem : Achievement
{
public int Level { get; set; }
}
public abstract class Achievement : INotifyPropertyChanged
{
private bool _isAchieved;
public string Description { get; set; }
public bool IsAchieved
{
get { return _isAchieved; }
set
{
if (_isAchieved != value)
{
_isAchieved = value;
NotifyPropertyChanged("IsAchieved");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
我错过了什么/做错了什么,使它无法工作?
更新:
我已经应用了CollectionViewSource
来应用筛选,但现在没有显示任何项目。
//Reference to the CollectionViewSource
_viewSource = (CollectionViewSource)Resources["EmblemsViewSource"];
//3 options in the ListBox: all, achieved & unachieved
private void FilterListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedItem = ((ListBoxItem)FilterListBox.SelectedItem).Content.ToString();
switch (selectedItem)
{
case "achieved": _filter = Filter.Achieved; _viewSource.Filter += new FilterEventHandler(CollectionViewSource_Filter); break;
case "unachieved": _filter = Filter.Unachieved; _viewSource.Filter += new FilterEventHandler(CollectionViewSource_Filter); break;
default: _filter = Filter.All; _viewSource.Filter -= new FilterEventHandler(CollectionViewSource_Filter); break;
}
}
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
var item = e.Item as Emblem;
switch (_filter)
{
case Filter.Achieved: e.Accepted = item.IsAchieved; break;
case Filter.Unachieved: e.Accepted = !item.IsAchieved; break;
case Filter.All: e.Accepted = true; break;
}
}
XAML:
<CollectionViewSource x:Key="EmblemsViewSource" Source="{Binding Emblems}" />
<toolkit:LongListMultiSelector Name="EmblemsList"
ItemsSource="{Binding Source={StaticResource EmblemsViewSource}}"
Background="Transparent"
LayoutMode="List"
ItemTemplate="{StaticResource ItemTemplate}" />
一种解决方案可以是创建一个从ObservableCollection
派生的新集合,并添加一个新属性,例如FilteredItems
。
主窗口:
public partial class MainWindow : Window
{
FilterableObservableCollection items;
public MainWindow()
{
items = new FilterableObservableCollection()
{
new ListViewItem() { Name = "Hallo", IsArchived = false },
new ListViewItem() { Name = "world", IsArchived = true },
new ListViewItem() { Name = "!!!", IsArchived = false }
};
InitializeComponent();
}
public FilterableObservableCollection MyItems
{
get { return items; }
}
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
items.NotArchivedOnlyFilterEnabled = (sender as CheckBox).IsChecked.Value;
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
{
items.NotArchivedOnlyFilterEnabled = (sender as CheckBox).IsChecked.Value;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
items.Add(new ListViewItem() { Name = "Item" + (items.Count + 1), IsArchived = items.Count % 2 == 0 });
}
}
自定义可观察集合:
public class FilterableObservableCollection : ObservableCollection<ListViewItem>
{
private bool notArchivedOnlyFilterEnabled;
public IEnumerable<ListViewItem> FilteredItems
{
get
{
if (notArchivedOnlyFilterEnabled)
{
return this.Where(x => x.IsArchived == false);
}
else
{
return this;
}
}
}
public bool NotArchivedOnlyFilterEnabled
{
get { return notArchivedOnlyFilterEnabled; }
set
{
notArchivedOnlyFilterEnabled = value;
OnPropertyChanged(new PropertyChangedEventArgs("FilteredItems"));
}
}
}
数据项:
public class ListViewItem
{
public string Name { get; set; }
public bool IsArchived { get; set; }
}
XAML:
<Window x:Class="ObservableCollectionDemo1.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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
xmlns:c="clr-namespace:ObservableCollectionDemo1">
<Grid>
<ListView HorizontalAlignment="Left" Height="142" Margin="81,47,0,0" VerticalAlignment="Top" Width="302" x:Name="listView" DataContext="{Binding MyItems}" ItemsSource="{Binding FilteredItems}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="100"/>
<GridViewColumn Header="Is Archived" DisplayMemberBinding="{Binding IsArchived}" Width="100"/>
</GridView>
</ListView.View>
</ListView>
<CheckBox Content="Is Not Archived" HorizontalAlignment="Left" Margin="278,194,0,0" VerticalAlignment="Top" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked"/>
<Button Content="New Item" HorizontalAlignment="Left" Margin="278,214,0,0" VerticalAlignment="Top" Width="105" Click="Button_Click"/>
</Grid>
</Window>
您只需要在集合上设置一次过滤器,而不是每次过滤器选项更改时。对的一次呼叫
_viewSource.Filter += new FilterEventHandler(CollectionViewSource_Filter);
应该是您所需要的,然后在您的列表框中更改选择,您可以调用_viewSource.Refresh()来强制筛选器谓词重新评估列表项。
另一种选择可能是让表示徽章的XAML数据模板使用转换器将可见性属性直接绑定到Emblem
的IsAchieved
属性:
<DataTemplate>
<Border Visibility="{Binding IsAchieved, Converter={StaticResource BoolVisibilityConverter}}">
...
其中BoolVisibilityConverter
是ValueConverter。
你必须尝试一下,看看它是否适合你的场景——运行大量的值转换器可能会对大数据集造成伤害,但它的优点是简单!
我实现了一个ObservableCollectionView
类,您可以在该类上设置Filter
(谓词),它可以跟踪所包含项的更改,并在项发生更改时重新筛选。。。
看看https://mytoolkit.codeplex.com/wikipage?title=ObservableCollectionView