Ok, quindi questa domanda è relativa a Windows Phone 7/Silverlight (strumenti WP7 aggiornati, settembre 2010), in particolare il filtro di uno ObservableCollection<T>
sottostante.Come aggiornare automaticamente il filtro e/o l'ordinamento su CollectionViewSource, quando la proprietà di un singolo oggetto cambia?
In chat con l'applicazione di controllo Pivot modello WP7, ho riscontrato un problema in cui la modifica di un elemento sottostante in un ObservableCollection<T>
non comporta l'aggiornamento del ListBox su schermo. Fondamentalmente, l'app di esempio ha due perni, il primo direttamente legato allo ObservableCollection<T>
sottostante e il secondo associato a uno CollectionViewSource
(vale a dire, rappresenta una vista filtrata sul sottostante ObservableCollection<T>
).
Gli elementi sottostanti che vengono aggiunti al ObservableCollection<T>
attuare INotifyPropertyChanged
, in questo modo:
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));
}
}
}
Poi, nella classe principale, una raccolta di dati viene inventata (lista ridotta per brevità, si noti anche che a differenza di altri elementi, tre dei LoadData() le voci sono 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));
}
}
}
Nel file MainPage.xaml, il primo pivot ha il suo ItemSource
basato direttamente sulla lista ObservableCollection<T>
. All'interno del secondo Pivot, il ListBox visualizzato sullo schermo ha la proprietà ItemSource
impostata su CollectionViewSource
, la cui origine sottostante è basata sullo ObservableCollection<T>
popolato in LoadData()
precedente.
<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>
noti che nelle MainPage.xaml.cs, l'attributo Filter
sul CollectionViewSource
nella sezione Resources
sopra è assegnato un gestore filtro, che setaccia attraverso quegli elementi che hanno IsSelected
impostato su 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;
}
}
Si noti inoltre che immediatamente dopo il caricamento g sui dati, ottengo il CollectionViewSource
e imposta la sua origine dati come lista ObservableCollection<T>
, in modo che ci siano dati di base su cui può avvenire il filtraggio.
Quando carichi applicativi, i dati vengono visualizzati come previsto, con quegli elementi della ObservableCollection<T>
che hanno IsSelected
vero, essere visualizzato nella seconda Pivot:
Avrete notato che ho Sono state eliminate le icone della barra delle applicazioni, la prima delle quali attiva la proprietà IsSelected
dell'ultimo elemento nello ObservableCollection<T>
quando si fa clic (vedere l'ultima funzione in MainPage.xaml.cs).
Qui è il nocciolo della mia domanda - quando clicco l'icona della barra del caso, posso vedere quando l'ultimo elemento della lista ha la proprietà IsSelected
impostata su true, howoever il secondo pivot non visualizza questa voce modificata . Posso vedere che il gestore NotifyPropertyChanged()
è stato attivato sull'elemento, tuttavia la raccolta non sta rilevando questo fatto e quindi la casella di riepilogo in Pivot 2 non cambia per riflettere il fatto che dovrebbe essere aggiunto un nuovo elemento alla raccolta .
Sono quasi certo che mi manca qualcosa di fondamentale/fondamentale qui, ma in caso contrario, qualcuno sa qual è il modo migliore per ottenere la raccolta e gli elementi sottostanti per giocare felicemente insieme?
Suppongo che questo problema si applichi anche all'ordinamento e al filtraggio ((nel senso che se un CollectionViewSource
è basato sull'ordinamento, allora quando cambia una proprietà di un oggetto che viene utilizzato nell'ordinamento, l'ordinamento del la raccolta dovrebbe riflettere anche questo))
Ho lo stesso problema.Ci aspettiamo che la vista si aggiorni dinamicamente in base alle modifiche alla raccolta sottostante, ma non lo fa. Quindi questo è davvero un problema del software che non fa ciò che ci aspettiamo naturalmente e/o che la documentazione MSDN non sia completa. – JustinM