Pat*_*nte 11 collections silverlight filter collectionviewsource windows-phone-7
好的,所以这个问题与Windows Phone 7/Silverlight(更新的WP7工具,2010年9月)有关,特别是过滤底层ObservableCollection<T>.
在使用WP7模板Pivot控件应用程序时,我遇到了一个问题,即更改一个底层项目ObservableCollection<T>,不会导致屏幕上的ListBox被更新.基本上,示例应用程序有两个枢轴,第一个直接绑定到底层ObservableCollection<T>,第二个绑定到a CollectionViewSource(即,表示底层的过滤视图ObservableCollection<T>).
正在添加到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));
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在主类中,编制一个数据集合(为简洁起见减少了列表,还要注意,与其他项目不同,三个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));
}
}
}
Run Code Online (Sandbox Code Playgroud)
在MainPage.xaml文件中,第一个Pivot ItemSource直接基于ObservableCollection<T>列表.在第二个Pivot中,屏幕上的ListBox将其ItemSource属性设置为a 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>
Run Code Online (Sandbox Code Playgroud)
需要注意的是在MainPage.xaml.cs中,Filter该属性CollectionViewSource在Resources部分上述分配一个过滤器处理程序,它通过了这些项目进行筛选,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;
}
}
Run Code Online (Sandbox Code Playgroud)
还要注意,在加载数据之后,我立即获取CollectionViewSource并将其数据源设置为ObservableCollection<T>列表,以便存在可以进行过滤的基础数据.
当应用程序加载,如预期所显示的数据,随着这些项目ObservableCollection<T>已IsSelected真实,显示在第二个支点:

你会注意到我已经取消注释了应用栏图标,第一个触发了点击时IsSelected最后一项的属性ObservableCollection<T>(参见MainPage.xaml.cs中的最后一个函数).
这是我的问题的关键 - 当我单击适用的条形图标时,我可以看到列表中的最后一个项目的IsSelected属性设置为true,无论第二个Pivot没有显示此更改的项目.我可以看到NotifyPropertyChanged()处理程序正在对项目进行触发,但是集合没有发现这一事实,因此数据透视表2中的列表框不会改变以反映应该有一个新项目添加到集合中的事实.
我很确定我在这里遗漏了一些非常基本/基本的东西,但如果没有这个,有没有人知道获得该系列的最佳方式,它的基本项目可以快乐地一起玩?
我想这个问题也适用于排序和过滤((在某种意义上,如果a CollectionViewSource基于排序,那么当排序中使用的项的属性发生变化时,集合的排序顺序应该反映为好))
当这种情况发生时,你难道不讨厌它吗?自从我发布问题以来不到 5 分钟,我就已经弄清楚了问题所在 - 这是非常基本的事情。物体上CollectionViewSource有一个View属性,它有一个Refresh()功能。在更改中包含的基础项的属性之后调用此函数ObservableCollection<T>似乎已经完成了。
基本上,我所要做的就是将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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,Refresh()在底层中的任何项目发生ObservableCollection<T>更改后调用视图。因此,在 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();
}
Run Code Online (Sandbox Code Playgroud)
...第二个 Pivot 的 ListBox 会立即更新。这么短的一行代码,却带来了天壤之别!
在我写下这个问题的时间里,我可以做一百件事:-(啊好吧,我想迟到总比不到好——想在这里发布答案,哪怕只是为了避免其他人撕扯他们的头发就像我一样。
| 归档时间: |
|
| 查看次数: |
5825 次 |
| 最近记录: |