You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF:不删除视图实现ItemsControl的ObservableCollection筛选

解决方案:通过ViewModel属性控制视图显隐,避免删除集合项

不需要修改ItemsSource集合,而是给每个子项ViewModel添加显隐控制属性,结合ItemTemplate和自定义面板的逻辑实现需求,步骤如下:

  1. 给ItemViewModel添加显隐属性
    在ItemViewModel中新增IsVisible布尔属性并实现INotifyPropertyChanged,用来标记当前项是否需要显示:
public class ItemViewModel : INotifyPropertyChanged
{
    private bool _isVisible = true;
    public bool IsVisible
    {
        get => _isVisible;
        set
        {
            _isVisible = value;
            OnPropertyChanged();
        }
    }

    // 原有属性(如ID)和INotifyPropertyChanged实现
    public int ID { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
  1. 主ViewModel实现双击切换逻辑
    在绑定ObsvColl的主ViewModel中,添加选中项管理和切换显隐的命令,双击时切换所有项的IsVisible状态:
public class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<ItemViewModel> ObsvColl { get; set; }

    private ItemViewModel _selectedItem;
    public ItemViewModel SelectedItem
    {
        get => _selectedItem;
        set
        {
            _selectedItem = value;
            OnPropertyChanged();
        }
    }

    private bool _isMaximized;
    public ICommand ToggleMaximizeCommand { get; }

    public MainViewModel()
    {
        ObsvColl = new ObservableCollection<ItemViewModel>();
        ToggleMaximizeCommand = new RelayCommand(ToggleMaximize);
        // 初始化集合逻辑...
    }

    private void ToggleMaximize()
    {
        if (SelectedItem == null) return;

        _isMaximized = !_isMaximized;
        foreach (var item in ObsvColl)
        {
            item.IsVisible = _isMaximized ? item == SelectedItem : true;
        }
    }

    // INotifyPropertyChanged实现
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

注:RelayCommand是WPF中常见的命令实现类,你可以用自己的命令基类替代。

  1. 修改XAML绑定显隐和命令
    在ItemsControl的ItemTemplate中,将控件的可见性绑定到IsVisible属性,同时绑定双击命令到主ViewModel的切换命令:
<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVis" />
</Window.Resources>

<ItemsControl Grid.Row="1" ItemsSource="{Binding ObsvColl}" Background="Black"
              SelectedItem="{Binding SelectedItem}">
    <!-- 原有ItemsPanel设置 -->
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Layout:CustomPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="{x:Type VM:ItemViewModel}">
            <Border BorderThickness="3"
                    Visibility="{Binding IsVisible, Converter={StaticResource BoolToVis}}"
                    IsHitTestVisible="{Binding IsVisible}">
                <DMC:UserControl 
                    Center="{Binding DataContext.Center, Source={x:Reference Name=SummaryView}}"
                    ID="{Binding ID}" 
                    ClipToBounds="True">
                    <DMC:UserControl.InputBindings>
                        <MouseBinding MouseAction="LeftDoubleClick"
                                      Command="{Binding DataContext.ToggleMaximizeCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                                      CommandParameter="{Binding}" />
                    </DMC:UserControl.InputBindings>
                </DMC:UserControl>
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
  • IsHitTestVisible绑定是为了让隐藏的项不响应鼠标交互,避免误操作。
  1. 修正CustomPanel的布局逻辑
    如果你的CustomPanel在元素设置为Visibility.Collapsed时仍会预留空间,需要修改面板的测量和排列逻辑,跳过不可见元素:
public class CustomPanel : Panel
{
    protected override Size MeasureOverride(Size availableSize)
    {
        Size totalSize = new Size();
        foreach (UIElement child in Children)
        {
            if (child.Visibility != Visibility.Visible) continue;
            child.Measure(availableSize);
            // 你的原有测量逻辑...
        }
        return totalSize;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        foreach (UIElement child in Children)
        {
            if (child.Visibility != Visibility.Visible) continue;
            // 你的原有排列逻辑,比如设置元素位置和大小
            child.Arrange(new Rect(/* 目标位置和尺寸 */));
        }
        return finalSize;
    }
}

这样处理后,双击操作只会修改子项ViewModel的显隐属性,不会删除集合项,视图不会被销毁,恢复时直接将所有项的IsVisible设为true即可,完全避免了重渲染的问题,同时也不会留下空白布局。

内容的提问来源于stack exchange,提问作者Harxish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 22:43:23