(真的)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<;T>;不更新UI

一种解决方案可以是创建一个从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数据模板使用转换器将可见性属性直接绑定到EmblemIsAchieved属性:

<DataTemplate>
  <Border Visibility="{Binding IsAchieved, Converter={StaticResource BoolVisibilityConverter}}">
...

其中BoolVisibilityConverter是ValueConverter。

你必须尝试一下,看看它是否适合你的场景——运行大量的值转换器可能会对大数据集造成伤害,但它的优点是简单!

我实现了一个ObservableCollectionView类,您可以在该类上设置Filter(谓词),它可以跟踪所包含项的更改,并在项发生更改时重新筛选。。。

看看https://mytoolkit.codeplex.com/wikipage?title=ObservableCollectionView