gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 22:54:56 27 4
gpt4 key购买 nike

好的,所以这个问题与 Windows Phone 7/Silverlight(更新的 WP7 工具,2010 年 9 月)有关,专门过滤底层 ObservableCollection<T> .

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

正在添加到 ObservableCollection<T> 的基础项目实现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 文件中,第一个 Pivot 的 ItemSource直接基于 ObservableCollection<T>列表。在第二个 Pivot 中,屏幕上的 ListBox 有它的 ItemSource属性设置为 CollectionViewSource ,其基础来源基于 ObservableCollection<T>已填写 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 中, Filter CollectionViewSource 上的属性在 Resources上面的部分被分配了一个过滤器处理程序,它筛选那些具有 IsSelected 的项目。设置为真:
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>列表,以便有可以进行过滤的基础数据。

当应用程序加载时,数据按预期显示,这些项目在 ObservableCollection<T> 中。其中有 IsSelected true,显示在第二个 Pivot 中:

alt text
alt text

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

这是我问题的症结 - 当我单击适用的条形图标时,我可以看到列表中的最后一项何时具有 IsSelected属性设置为 true,但是第二个 Pivot 不显示此更改的项目。我可以看到 NotifyPropertyChanged()处理程序在项目上被触发,但是集合没有选择这个事实,因此 Pivot 2 中的列表框不会更改以反射(reflect)应该有一个新项目添加到集合中的事实。

我很确定我在这里遗漏了一些非常基本/基本的东西,但是如果做不到这一点,有没有人知道获得该系列的最佳方法,并且它是一起愉快玩耍的基础项目?

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

最佳答案

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

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

因此,我没有调用 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);
}

}
}

希望这会有所帮助!

关于silverlight - 当单个项目的属性更改时,如何自动更新 CollectionViewSource 上的过滤器和/或排序顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3771556/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com