(真的)ObservableCollection <t>没有更新UI </t>

时间:2014-10-20 09:17:16

标签: c# xaml observablecollection inotifypropertychanged inotifycollectionchanged

我有一个Emblem类型的对象列表,我在LongListMultiSelector中显示。我只想展示尚未实现的那些。我可以选择一个或多个项目并将其更改为IsAchieved = true,但问题是它们不会立即消失,用户界面不会自动更新。

我认为这不是一个问题,因为我使用了ObservableCollection<T>。然后我发现如果项目的属性发生变化,则不会通知集合。因此,实施了INotifyPropertyChanged界面,但这也不起作用。

在这里,我发现以下问题(以及更多)分享了这个问题:

我也试过实现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}" />

3 个答案:

答案 0 :(得分:1)

一种解决方案可能是您创建一个派生自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>

答案 1 :(得分:1)

您只需要在集合上设置一次过滤器,而不是每次过滤器选项更改时。单次调用

_viewSource.Filter += new FilterEventHandler(CollectionViewSource_Filter);

应该是您所需要的,然后在您更改的列表框选择中,您可以调用_viewSource。Refresh()来强制过滤谓词重新评估列表项。

另一个选项可能是让代表徽章的XAML数据模板使用转换器将可见性属性直接绑定到IsAchieved的{​​{1}}属性:

Emblem

<DataTemplate> <Border Visibility="{Binding IsAchieved, Converter={StaticResource BoolVisibilityConverter}}"> ... ValueConverter的位置。

您必须尝试查看它是否适合您的方案 - 运行大量值转换器会因大型数据集而受损,但它具有简单的优势!

答案 2 :(得分:1)

我已经实现了一个ObservableCollectionView类,您可以在其上设置Filter(谓词)并且可以跟踪所包含项目的更改并在项目发生更改时重新过滤...

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