具有实时成形/分组的ListBox-如何在项目重新分组时保持选择
本文关键字:项目 选择 ListBox- 实时 | 更新日期: 2023-09-27 18:20:07
我的视图模型中有一个ObservableCollection
,视图中有一个子CollectionViewSource
和ListBox
。
ListBox
与CollectionViewSource
结合。CollectionViewSource
绑定到ObservableCollection
,对项目进行排序并将它们分组。我通过CollectionViewSource
上的IsLiveGroupingRequested
和IsLiveSortingRequested
属性启用了实时排序和实时分组,因此每当底层视图模型对象发生更改时,它们都会在ListBox
中重新排序和分组。这一切都很好。
这个问题与选择有关。如果我在ListBox
中选择了一个项目,然后由于视图模型对象以某种方式发生了更改而对其重新分组,则当该项目移动到新组时,该项目将被取消选择。
重新分组所选项目时,如何保留所选内容
下面是一个简单的XAML示例,显示了这个问题。如果AllItems中某个对象的Category属性发生更改,则由于实时成形,该项目将正确地重新分组。但是,如果该项目已被选中,它将变为未被选中。
<Grid>
<Grid.Resources>
<CollectionViewSource x:Key="MyItems" Source="{Binding AllItems}" IsLiveGroupingRequested="True" IsLiveSortingRequested="True">
<CollectionViewSource.SortDescriptions>
<componentModel:SortDescription PropertyName="Category" />
<componentModel:SortDescription PropertyName="Name" />
</CollectionViewSource.SortDescriptions>
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Grid.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource MyItems}}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
</Grid>
目前没有简单的解决方案。
我可以看到两种解决方案:
1) 用户手动停止实时更新。允许使用跳跃数据是很容易出错的。
示例:MS 中WCF日志查看器中的暂停按钮
2) 在开始更新数据之前,请记住所选项目。当更新完成时,只返回选择。
示例:如何防止WPF数据网格在更新项目时取消选择所选项目?
在我的情况下,我很少需要在视图模型中将所选项目设置为null。因此,我使用以下变通方法。请注意,必须调用不同的方法才能将所选值设置为null的尴尬方式。
private ItemViewModel selectedItem;
private ItemViewModel prevSelectedItem;
/// <summary>
/// Gets or sets the selected item. Use <see cref="ClearSelectedItem"/> to set it to null.
/// </summary>
public ItemViewModel SelectedItem
{
get => selectedItem;
set
{
if (!Equals(selectedItem, value))
{
prevSelectedItem = selectedItem;
selectedItem = value;
RaisePropertyChanged();
if (value == null)
{
// Ignore null set by the live grouping/sorting/filtering in the CollectionViewSource
System.Windows.Application.Current.MainWindow.Dispatcher.BeginInvoke(
new Action(() =>
{
SelectedItem = prevSelectedItem;
}));
}
}
}
}
/// <summary>
/// Sets <see cref="SelectedItem"/> to null.
/// </summary>
public void ClearSelectedItem()
{
selectedItem = null;
RaisePropertyChanged(nameof(SelectedItem));
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises a <see cref="PropertyChanged"/> event for the specified property name.
/// </summary>
/// <param name="propertyName">The name of the property to notify.</param>
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}