Ok, esta pregunta está relacionada con Windows Phone 7/Silverlight (herramientas actualizadas de WP7, septiembre de 2010), filtrando específicamente un subyacente ObservableCollection<T>
.¿Cómo actualizar automáticamente el filtro y/o el orden de clasificación en CollectionViewSource, cuando cambia la propiedad de un elemento individual?
En la limpieza con la aplicación de control de pivote de la plantilla WP7, me he encontrado con un problema por el cual cambiar un elemento subyacente en un ObservableCollection<T>
, no da como resultado la actualización del ListBox en pantalla. Básicamente, la aplicación de muestra tiene dos pivotes, el primero directamente ligado al ObservableCollection<T>
subyacente, y el segundo a un CollectionViewSource
(es decir, que representa una vista filtrada en el ObservableCollection<T>
subyacente).
Los elementos subyacentes que se están agregando a la ObservableCollection<T>
implementar INotifyPropertyChanged
, así:
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));
}
}
}
Luego, en la clase principal, una colección de datos se inventó (lista reducida por razones de brevedad, también tenga en cuenta que a diferencia de otros artículos, tres de los LoadData() entradas han 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));
}
}
}
en el archivo MainPage.xaml, el primer pivote tiene su ItemSource
basada directamente en la lista ObservableCollection<T>
. Dentro del segundo Pivot, el ListBox en pantalla tiene su propiedad ItemSource
establecida en CollectionViewSource
, cuya fuente subyacente se basa en el ObservableCollection<T>
poblado en LoadData()
arriba.
<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>
Tenga en cuenta que en los MainPage.xaml.cs, el atributo Filter
en el CollectionViewSource
en la sección Resources
arriba se asigna un controlador de filtro, que tamiza a través de esos elementos que tienen IsSelected
establecido en verdadero:
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;
}
}
También tenga en cuenta que inmediatamente después de la carga Por encima de los datos, obtengo el CollectionViewSource
y configuro su fuente de datos como la lista ObservableCollection<T>
, para que haya datos base sobre los que pueda realizarse el filtrado.
Cuando se carga la aplicación, se muestran los datos como se esperaba, con esos elementos en el ObservableCollection<T>
IsSelected
cierto, se está visualizando en el segundo pivote que tienen:
Se dará cuenta de que yo Descomentó los iconos de la barra de aplicaciones, el primero de los cuales alterna la propiedad IsSelected
del último elemento en el ObservableCollection<T>
al hacer clic (consulte la última función en MainPage.xaml.cs).
Aquí está el quid de mi pregunta - al hacer clic en el icono de la barra de aplicación, que se puede ver cuando el último elemento de la lista tiene su IsSelected
propiedad se establece en true, howoever el segundo pivote no muestra este elemento cambiado . Puedo ver que el controlador NotifyPropertyChanged()
se está disparando en el elemento, sin embargo, la colección no está recogiendo este hecho, y por lo tanto, el cuadro de lista en el Pivote 2 no cambia para reflejar el hecho de que debe haber un nuevo elemento agregado a la colección .
Estoy bastante seguro de que me estoy perdiendo algo bastante fundamental/básico aquí, pero en su defecto, ¿alguien sabe la mejor manera de obtener la colección y los elementos subyacentes para jugar felizmente juntos?
Supongo que este problema también se aplica tanto a la clasificación como al filtrado ((en el sentido de que si CollectionViewSource
se basa en la clasificación, cuando cambia la propiedad de un elemento que se utiliza en la clasificación, el orden de la colección debería reflejar esto también)
Estoy teniendo el mismo problema.Esperamos que la vista se actualice de forma dinámica en función de los cambios en la colección subyacente, pero no es así. Así que este es realmente un problema de que el software no hace lo que esperamos de forma natural y/o que la documentación de MSDN no está completa. – JustinM