当单个项目的属性更改时,如何自动更新 CollectionViewSource 上的过滤器和/或排序顺序?

发布于 2024-09-24 08:33:35 字数 8852 浏览 9 评论 0 原文

好的,所以这个问题与 Windows Phone 7/Silverlight(更新的 WP7 工具,2010 年 9 月)相关,特别是过滤底层 ObservableCollection

在研究 WP7 模板 Pivot 控件应用程序时,我遇到了一个问题,即更改 ObservableCollection 中的基础项目不会导致屏幕上的 ListBox 被更新。基本上,示例应用程序有两个枢轴,第一个直接绑定到底层 ObservableCollection,第二个绑定到 CollectionViewSource(即表示底层的 ObservableCollection)。

添加到 ObservableCollection 的底层项目实现 INotifyPropertyChanged,如下所示:

public class ItemViewModel : INotifyPropertyChanged
{       
    public string LineOne
    {
        get { return _lineOne; }
        set
        {
            if (value != _lineOne)
            {
                _lineOne = value;
                NotifyPropertyChanged("LineOne");
            }
        }
    } private string _lineOne;

    public string LineTwo
    {
        get { return _lineTwo; }
        set
        {
            if (value != _lineTwo)
            {
                _lineTwo = value;
                NotifyPropertyChanged("LineTwo");
            }
        }
    } private string _lineTwo;

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (value != _isSelected)
            {
                _isSelected = value;
                NotifyPropertyChanged("IsSelected");
            }
        }
    } private bool _isSelected = false;

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

然后,在主类中,构建一个数据集合(为简洁起见,缩小了列表) ,另请注意,与其他项目不同,其中三个 LoadData() 条目具有 IsSelected == true):

 public class MainViewModel : INotifyPropertyChanged
 {
  public MainViewModel()
  {
   this.Items = new ObservableCollection<ItemViewModel>();
  }

  public ObservableCollection<ItemViewModel> Items { get; private set; }

  public bool IsDataLoaded
  {
   get;
   private set;
  }

   public void LoadData()
  {
   this.Items.Add(new ItemViewModel() { LineOne = "runtime one", IsSelected = true, LineTwo = "Maecenas praesent accumsan bibendum" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime two", LineTwo = "Dictumst eleifend facilisi faucibus" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime three", IsSelected = true, LineTwo = "Habitant inceptos interdum lobortis" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime four", LineTwo = "Nascetur pharetra placerat pulvinar" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime five", IsSelected = true, LineTwo = "Maecenas praesent accumsan bibendum" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime six", LineTwo = "Dictumst eleifend facilisi faucibus" });
   this.IsDataLoaded = true;
  }

  public event PropertyChangedEventHandler PropertyChanged;
  public void NotifyPropertyChanged(String propertyName)
  {
   if (null != PropertyChanged)
   {
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
   }
  }
 }

在 MainPage.xaml 文件中,第一个数据透视表的 ItemSource 直接基于 ObservableCollection< ;T> 列表。在第二个透视图中,屏幕上的 ListBox 将其 ItemSource 属性设置为 CollectionViewSource,其底层源基于 ObservableCollection > 在上面的 LoadData() 中填充。

<phone:PhoneApplicationPage.Resources>
    <CollectionViewSource x:Key="IsSelectedCollectionView" Filter="CollectionViewSource_SelectedListFilter">
    </CollectionViewSource>
</phone:PhoneApplicationPage.Resources>

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <!--Pivot Control-->
    <controls:Pivot Title="MY APPLICATION">
        <!--Pivot item one-->
        <controls:PivotItem Header="first">
            <!--Double line list with text wrapping-->
            <ListBox x:Name="FirstListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                      <StackPanel Margin="0,0,0,17" Width="432">
                          <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                          <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                      </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </controls:PivotItem>

        <!--Pivot item two-->
        <controls:PivotItem Header="second"> 
            <!--Triple line list no text wrapping-->
            <ListBox x:Name="SecondListBox" Margin="0,0,-12,0" ItemsSource="{Binding  Source={StaticResource IsSelectedCollectionView}}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17">
                                <TextBlock Text="{Binding LineOne}" TextWrapping="NoWrap" Margin="12,0,0,0" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                                <TextBlock Text="{Binding LineThree}" TextWrapping="NoWrap" Margin="12,-6,0,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
        </controls:PivotItem>
    </controls:Pivot>
</Grid>

<!--Sample code showing usage of ApplicationBar-->
<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1" Click="ApplicationBarIconButton_Click"/>
        <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
        <shell:ApplicationBar.MenuItems>
            <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
            <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
        </shell:ApplicationBar.MenuItems>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

请注意,在 MainPage.xaml.cs 中,上面 Resources 部分中 CollectionViewSource 上的 Filter 属性被分配了一个筛选处理程序,该处理程序会筛选通过那些将 IsSelected 设置为 true 的项目:

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();
        DataContext = App.ViewModel;
        this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        if (!App.ViewModel.IsDataLoaded)
        {
            App.ViewModel.LoadData();
            CollectionViewSource isSelectedListView = this.Resources["IsSelectedCollectionView"] as CollectionViewSource;
            if (isSelectedListView != null)
            {
                isSelectedListView .Source = App.ViewModel.Items;
            }
        }
    }

    private void CollectionViewSource_SelectedListFilter(object sender, System.Windows.Data.FilterEventArgs e)
    {
        e.Accepted = ((ItemViewModel)e.Item).IsSelected;
    }

    private void ApplicationBarIconButton_Click(object sender, EventArgs e)
    {
        ItemViewModel item = App.ViewModel.Items[App.ViewModel.Items.Count - 1];
        item.IsSelected = !item.IsSelected;
    }
}

另请注意,在加载数据后,我立即获取 CollectionViewSource 并将其数据源设置为 ObservableCollection T> 列表,以便有可以进行过滤的基础数据。

应用程序加载时,数据按预期显示,ObservableCollectionIsSelected true 的项目显示在第二个数据透视中:

alt text

您会注意到我已取消注释应用程序栏图标,其中第一个图标会切换 IsSelected单击时 ObservableCollection 中最后一项的 属性(请参阅 MainPage.xaml.cs 中的最后一个函数)。

这是我的问题的关键 - 当我单击适用的栏图标时,我可以看到列表中最后一项的 IsSelected 属性何时设置为 true,但是第二个数据透视表不显示此更改的项目。我可以看到 NotifyPropertyChanged() 处理程序正在该项目上被触发,但是集合没有接收到这一事实,因此 Pivot 2 中的列表框不会更改以反映以下事实:应该是添加到集合中的新项目。

我很确定我在这里错过了一些非常基本的/基本的东西,但如果失败了,有人知道获得该系列的最佳方法以及它的基础项目可以一起愉快地玩耍吗?

我想这个问题也适用于排序和过滤((从某种意义上说,如果CollectionViewSource基于排序,那么当排序中使用的项目的属性发生变化时,排序集合的顺序也应该反映这一点))

Ok, so this question is related to Windows Phone 7/Silverlight (updated WP7 Tools, Sept 2010), specifically filtering an underlying ObservableCollection<T>.

In mucking about with the WP7 template Pivot control application, I've run into an issue whereby changing an underlying item in an ObservableCollection<T>, does not result in the on-screen ListBox being updated. Basically, the sample app has two pivots, the first directly bound to the underlying ObservableCollection<T>, and the second bound to a CollectionViewSource (i.e., representing a filtered view on the underlying ObservableCollection<T>).

The underlying items that are being added to the ObservableCollection<T> implement INotifyPropertyChanged, like so:

public class ItemViewModel : INotifyPropertyChanged
{       
    public string LineOne
    {
        get { return _lineOne; }
        set
        {
            if (value != _lineOne)
            {
                _lineOne = value;
                NotifyPropertyChanged("LineOne");
            }
        }
    } private string _lineOne;

    public string LineTwo
    {
        get { return _lineTwo; }
        set
        {
            if (value != _lineTwo)
            {
                _lineTwo = value;
                NotifyPropertyChanged("LineTwo");
            }
        }
    } private string _lineTwo;

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (value != _isSelected)
            {
                _isSelected = value;
                NotifyPropertyChanged("IsSelected");
            }
        }
    } private bool _isSelected = false;

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Then, in the main class, a data collection is concocted (list reduced for brevity, also note that unlike other items, three of the LoadData() entries have IsSelected == true):

 public class MainViewModel : INotifyPropertyChanged
 {
  public MainViewModel()
  {
   this.Items = new ObservableCollection<ItemViewModel>();
  }

  public ObservableCollection<ItemViewModel> Items { get; private set; }

  public bool IsDataLoaded
  {
   get;
   private set;
  }

   public void LoadData()
  {
   this.Items.Add(new ItemViewModel() { LineOne = "runtime one", IsSelected = true, LineTwo = "Maecenas praesent accumsan bibendum" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime two", LineTwo = "Dictumst eleifend facilisi faucibus" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime three", IsSelected = true, LineTwo = "Habitant inceptos interdum lobortis" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime four", LineTwo = "Nascetur pharetra placerat pulvinar" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime five", IsSelected = true, LineTwo = "Maecenas praesent accumsan bibendum" });
   this.Items.Add(new ItemViewModel() { LineOne = "runtime six", LineTwo = "Dictumst eleifend facilisi faucibus" });
   this.IsDataLoaded = true;
  }

  public event PropertyChangedEventHandler PropertyChanged;
  public void NotifyPropertyChanged(String propertyName)
  {
   if (null != PropertyChanged)
   {
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
   }
  }
 }

In the MainPage.xaml file, the first Pivot has its ItemSource based directly on the ObservableCollection<T> list. Within the second Pivot, the on-screen ListBox has its ItemSource Property set to a CollectionViewSource, whose underlying source is based on the ObservableCollection<T> populated in LoadData() above.

<phone:PhoneApplicationPage.Resources>
    <CollectionViewSource x:Key="IsSelectedCollectionView" Filter="CollectionViewSource_SelectedListFilter">
    </CollectionViewSource>
</phone:PhoneApplicationPage.Resources>

<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
    <!--Pivot Control-->
    <controls:Pivot Title="MY APPLICATION">
        <!--Pivot item one-->
        <controls:PivotItem Header="first">
            <!--Double line list with text wrapping-->
            <ListBox x:Name="FirstListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                      <StackPanel Margin="0,0,0,17" Width="432">
                          <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                          <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                      </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </controls:PivotItem>

        <!--Pivot item two-->
        <controls:PivotItem Header="second"> 
            <!--Triple line list no text wrapping-->
            <ListBox x:Name="SecondListBox" Margin="0,0,-12,0" ItemsSource="{Binding  Source={StaticResource IsSelectedCollectionView}}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17">
                                <TextBlock Text="{Binding LineOne}" TextWrapping="NoWrap" Margin="12,0,0,0" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                                <TextBlock Text="{Binding LineThree}" TextWrapping="NoWrap" Margin="12,-6,0,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
        </controls:PivotItem>
    </controls:Pivot>
</Grid>

<!--Sample code showing usage of ApplicationBar-->
<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1" Click="ApplicationBarIconButton_Click"/>
        <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
        <shell:ApplicationBar.MenuItems>
            <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
            <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
        </shell:ApplicationBar.MenuItems>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Note that in the MainPage.xaml.cs, the Filter attribute on the CollectionViewSource in the Resources section above is assigned a filter handler, which sifts through those items that have IsSelected set to true:

public partial class MainPage : PhoneApplicationPage
{
    public MainPage()
    {
        InitializeComponent();
        DataContext = App.ViewModel;
        this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        if (!App.ViewModel.IsDataLoaded)
        {
            App.ViewModel.LoadData();
            CollectionViewSource isSelectedListView = this.Resources["IsSelectedCollectionView"] as CollectionViewSource;
            if (isSelectedListView != null)
            {
                isSelectedListView .Source = App.ViewModel.Items;
            }
        }
    }

    private void CollectionViewSource_SelectedListFilter(object sender, System.Windows.Data.FilterEventArgs e)
    {
        e.Accepted = ((ItemViewModel)e.Item).IsSelected;
    }

    private void ApplicationBarIconButton_Click(object sender, EventArgs e)
    {
        ItemViewModel item = App.ViewModel.Items[App.ViewModel.Items.Count - 1];
        item.IsSelected = !item.IsSelected;
    }
}

Also note that immediately after loading up the data, I obtain the CollectionViewSource and set its data source as the ObservableCollection<T> list, in order that there is base data upon which the filtering can take place.

When application loads, the data is displayed as expected, with those items in the ObservableCollection<T> which have IsSelected true, being displayed in the second Pivot:

alt text
alt text

You'll notice that I've uncommented the Application Bar Icons, the first of which toggles the IsSelected property of the last item in the ObservableCollection<T> when clicked (see the last function in MainPage.xaml.cs).

Here is the crux of my question - when I click the applicable bar icon, I can see when the last item in the list has its IsSelected property set to true, howoever the second Pivot does not display this changed item. I can see that the NotifyPropertyChanged() handler is being fired on the item, however the collection is not picking up this fact, and hence the list box in Pivot 2 does not change to reflect the fact that there should be a new item added to the collection.

I'm pretty certain that I'm missing something quite fundamental/basic here, but failing that, does anyone know the best way to get the collection and it's underlying items to play happily together?

I suppose this problem also applies to sorting as well as filtering ((in the sense that if a CollectionViewSource is based on sorting, then when a property of an item that is used in the sort changes, the sort order of the collection should reflect this as well))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冰雪梦之恋 2024-10-01 08:33:35

我必须处理这个问题,尽管“Refresh()”解决方案运行良好,但执行时间相当长,因为它仅针对一项属性更改事件刷新整个列表。不太好。在实时数据每 1 秒进入集合的情况下,我让您想象一下如果使用这种方法,用户体验的结果:)

我想出了一个解决方案,其基础是:当将项目添加到包装在集合中时CollectionView,然后通过过滤谓词评估该项目,并根据此结果在视图中显示或不显示。

因此,我没有调用refresh(),而是模拟插入更新其属性的对象。通过模拟对象的插入,过滤谓词将自动评估该对象,而无需刷新整个列表。

下面是执行此操作的代码:

派生的可观察集合:

namespace dotnetexplorer.blog.com
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

/// <summary>
/// Derived class used to be able to manage filter application when a collection item property changed
///   whithout having to do a refresh
/// </summary>
internal sealed class CustomObservableCollection : ObservableCollection<object>
{
    /// <summary>
    ///   Initializes a new instance of the <see cref = "CustomObservableCollection " /> class.
    /// </summary>
    public CustomObservableCollection ()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="CustomObservableCollection "/> class.
    /// </summary>
    /// <param name="source">
    /// The source.
    /// </param>
    public CustomObservableCollection (IEnumerable<object> source)
        : base(source)
    {
    }

    /// <summary>
    /// Custom Raise collection changed
    /// </summary>
    /// <param name="e">
    /// The notification action
    /// </param>
    public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        OnCollectionChanged(e);
    }
}
}

并且有在接收项目属性更改事件时使用的代码,其中替代源是 CustomObservableCollection:

        private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {

                // To avoid doing a refresh on a property change which would end in a very hawful user experience
                // we simulate a replace to the collection because the filter is automatically applied in this case
                int index = _substituteSource.IndexOf(sender);

                var argsReplace = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
                                                                       new List<object> { sender },
                                                                       new List<object> { sender }, index);
                _substituteSource.RaiseCollectionChanged(argsReplace);
            }

        }
    }

希望这会有所帮助!

I had to handle this problem and although the 'Refresh()' solution works well, it is quite long to execute because its refreshes the whole list just for one item property changed event. Not very good. And in a scenario of real time data entering the collection every 1 seconds, I let you imagine the result in user experience if you use this approach :)

I came up with a solution which base is : when adding an item to collection wrapped in a collectionview, then the item is evaluated by the filter predicate and, based on this result, displayed or not in the view.

So instead of calling refresh(), I came up simulating an insert of the object that got its property updated. By simulating the insert of the object, it is going to be automatically evaluated by the filter predicate without need to refresh the whole list with a refresh.

Here is the code in order to do that :

The derived observable collection :

namespace dotnetexplorer.blog.com
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

/// <summary>
/// Derived class used to be able to manage filter application when a collection item property changed
///   whithout having to do a refresh
/// </summary>
internal sealed class CustomObservableCollection : ObservableCollection<object>
{
    /// <summary>
    ///   Initializes a new instance of the <see cref = "CustomObservableCollection " /> class.
    /// </summary>
    public CustomObservableCollection ()
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="CustomObservableCollection "/> class.
    /// </summary>
    /// <param name="source">
    /// The source.
    /// </param>
    public CustomObservableCollection (IEnumerable<object> source)
        : base(source)
    {
    }

    /// <summary>
    /// Custom Raise collection changed
    /// </summary>
    /// <param name="e">
    /// The notification action
    /// </param>
    public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        OnCollectionChanged(e);
    }
}
}

And there is the code to use when receiveing item property changed event where substitute source is a CustomObservableCollection :

        private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {

                // To avoid doing a refresh on a property change which would end in a very hawful user experience
                // we simulate a replace to the collection because the filter is automatically applied in this case
                int index = _substituteSource.IndexOf(sender);

                var argsReplace = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
                                                                       new List<object> { sender },
                                                                       new List<object> { sender }, index);
                _substituteSource.RaiseCollectionChanged(argsReplace);
            }

        }
    }

Hope this will help !

阿楠 2024-10-01 08:33:35

当这种情况发生时,你难道不讨厌它吗?自从我发布问题以来不到 5 分钟,我就已经找出了问题所在 - 而且它一些非常基本的东西。在CollectionViewSource对象上,有一个View属性,它有一个Refresh()函数。在 ObservableCollection 中包含的基础项的属性更改后调用此函数,似乎已经完成了。

基本上,我所要做的就是将 CollectionViewSource 对象更改为成员变量,然后在调用 LoadData() 时保存它:

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    if (!App.ViewModel.IsDataLoaded)
    {
        App.ViewModel.LoadData();
        m_isSelectedListView = this.Resources["IsSelectedCollectionView"] as CollectionViewSource;
        if (m_isSelectedListView != null)
        {
            m_isSelectedListView.Source = App.ViewModel.Items;
        }
    }
}

然后,调用 Refresh( ) 在视图上,在底层 ObservableCollection 中的任何项目发生更改之后。因此,在 MainPage.xaml.cs 中,在更改最后一项后,添加对刷新的调用:

private void ApplicationBarIconButton_Click(object sender, EventArgs e)
{
    ItemViewModel item = App.ViewModel.Items[App.ViewModel.Items.Count - 1];
    item.IsSelected = !item.IsSelected;
    m_isSelectedListView.View.Refresh();
}

...,第二个 Pivot 的 ListBox 会立即更新。这么短的一行代码,却带来了天壤之别!

在我写下这个问题的时间里,我可以做一百件事:-(啊好吧,我想迟到总比不到好——想在这里发布答案,哪怕只是为了避免其他人撕扯他们的头发就像我一样。

Don't you just hate it when that happens, not 5 minutes gone since I posted the question, and I've figured out what the problem is - and it was something quite basic. On the CollectionViewSource object, there is a View property, which has a Refresh() function. Calling this function after a property on an underlying item contained in the ObservableCollection<T> changes, seems to have done it.

Basically, all I had to do was change the CollectionViewSource object into a member variable, and then save it when LoadData() is called:

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    if (!App.ViewModel.IsDataLoaded)
    {
        App.ViewModel.LoadData();
        m_isSelectedListView = this.Resources["IsSelectedCollectionView"] as CollectionViewSource;
        if (m_isSelectedListView != null)
        {
            m_isSelectedListView.Source = App.ViewModel.Items;
        }
    }
}

Then, call Refresh() on the view, after any of the items in the underlying ObservableCollection<T> changes. So in MainPage.xaml.cs, just after changing the last item, add the call to refresh:

private void ApplicationBarIconButton_Click(object sender, EventArgs e)
{
    ItemViewModel item = App.ViewModel.Items[App.ViewModel.Items.Count - 1];
    item.IsSelected = !item.IsSelected;
    m_isSelectedListView.View.Refresh();
}

... and the second Pivot's ListBox is updated instantly. Such a short line of code, a whole world of difference!

In the time it took me to write up that question, there are a hundred things I could've done :-( Ah well, better late than never I guess - thought to post the answer here, if only to save someone else tearing out their hair like I did.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文