RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1137568
Accepted
aepot
aepot
Asked:2020-06-07 07:05:33 +0000 UTC2020-06-07 07:05:33 +0000 UTC 2020-06-07 07:05:33 +0000 UTC

TreeView - 按节点类型进行多项选择

  • 772

前言

我正在为我解决一个复杂的数据可视化问题,项目很大并且需要重构,我被卡住了。在我为此功能做出架构决策之前,我无法重构。因此,我创建了一个单独的演示项目来重现我所需要的。

多选并不容易TreeView,所以 github 上的现成解决方案不适合我,此外,我需要一个特殊的可视化,我已经能够在 XAML 标记中实现。唯一的问题是逻辑。

一个任务

  • 给定一棵元素树,每个节点都有自己的 ID 和类型。
  • TreeView在可视化树中显示这些节点。可以选择(活动)或不选择(不活动)节点。
  • 默认选择顶级节点。
  • 每种类型一次只能选择一个节点。- 卡在这里
  • 应始终选择恒定数量的节点,因为树中存在许多类型的节点 - 选择了这么多。
  • 另外,应形成具有所选 ID 的集合。基于这个集合,我将用内容填充另一个控件。

为了可见性,这里是 INPC 接口的实现

public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

数据

public enum TreeNodeType
{
    Red,
    Green,
    Blue
}

public class TreeItem : NotifyPropertyChanged
{
    private TreeNodeType _nodeType;
    private int _nodeId;
    private ObservableCollection<TreeItem> _items;
    private bool _selected;
    private bool _active;

    public int NodeId
    {
        get => _nodeId;
        set
        {
            _nodeId = value;
            OnPropertyChanged();
        }
    }
    public TreeNodeType NodeType
    {
        get => _nodeType;
        set
        {
            _nodeType = value;
            OnPropertyChanged();
        }
    }
    public ObservableCollection<TreeItem> Items // TreeViewItem.ItemsSource
    {
        get => _items;
        set
        {
            _items = value;
            OnPropertyChanged();
        }
    }
    public bool Selected // TreeViewItem.IsSelected
    {
        get => _selected;
        set 
        {
            _selected = value;
            Active = value;
            OnPropertyChanged();
        }
    }
    public bool Active // моя попытка выделять элемент
    {
        get => _active;
        set
        {
            if (value) _active = !_active;
            OnPropertyChanged();
        }
    }
}

查看模型

public class MainViewModel : NotifyPropertyChanged
{
    private ObservableCollection<TreeItem> _treeItems;
    private ObservableCollection<int> _selectedItems;

    public ObservableCollection<TreeItem> TreeItems
    {
        get => _treeItems;
        set
        {
            _treeItems = value;
            OnPropertyChanged();
        }
    }

    public ObservableCollection<int> SelectedItems // сюда хочу записать айдищшники выбранных нод
    {
        get => _selectedItems;
        set
        {
            _selectedItems = value;
            OnPropertyChanged();
        }
    }

    public MainViewModel()
    {
        // тестовые данные
        TreeItems = new ObservableCollection<TreeItem>
        {
            new TreeItem
            {
                NodeId = 0,
                NodeType = TreeNodeType.Red,
                Items = new ObservableCollection<TreeItem>
                {
                    new TreeItem
                    {
                        NodeId = 1,
                        NodeType = TreeNodeType.Red,
                        Items = new ObservableCollection<TreeItem>
                        {
                            new TreeItem { NodeId = 2, NodeType = TreeNodeType.Green },
                            new TreeItem { NodeId = 3, NodeType = TreeNodeType.Red }
                        }
                    },
                    new TreeItem { NodeId = 4, NodeType = TreeNodeType.Red }
                },
                Selected = true
            },
            new TreeItem
            {
                NodeId = 5,
                NodeType = TreeNodeType.Blue,
                Items = new ObservableCollection<TreeItem>
                {
                    new TreeItem
                    {
                        NodeId = 6,
                        NodeType = TreeNodeType.Blue,
                        Items = new ObservableCollection<TreeItem>
                        {
                            new TreeItem { NodeId = 7, NodeType = TreeNodeType.Blue }
                        }
                    },
                    new TreeItem { NodeId = 8, NodeType = TreeNodeType.Green }
                },
                Selected = true
            },
            new TreeItem
            {
                NodeId = 9,
                NodeType = TreeNodeType.Green,
                Selected = true
            }
        };

        SelectedItems = new ObservableCollection<int>();
    }
}

标记

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <TreeView ItemsSource="{Binding TreeItems}">
        <TreeView.Resources>
            <Style TargetType="{x:Type TreeView}">
                <Setter Property="OverridesDefaultStyle" Value="True" />
                <Setter Property="SnapsToDevicePixels" Value="True" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type TreeView}">
                            <ItemsPresenter/>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            <Style TargetType="TreeViewItem">
                <Setter Property="ItemsSource" Value="{Binding Items}"/>
                <Setter Property="IsSelected" Value="{Binding Selected}"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <StackPanel>
                                <StackPanel Margin="2" >
                                    <Border BorderThickness="1" HorizontalAlignment="Center" Margin="2">
                                        <Border.Style>
                                            <Style TargetType="{x:Type Border}">
                                                <Style.Triggers>
                                                    <DataTrigger Binding="{Binding Active}" Value="True">
                                                        <Setter Property="BorderBrush" Value="Black"/>
                                                    </DataTrigger>
                                                </Style.Triggers>
                                            </Style>
                                        </Border.Style>
                                        <Grid>
                                            <Rectangle Width="30" Height="30" Margin="2">
                                                <Rectangle.Style>
                                                    <Style TargetType="Rectangle">
                                                        <Style.Triggers>
                                                            <DataTrigger Binding="{Binding NodeType}" Value="Red">
                                                                <Setter Property="Fill" Value="Red"/>
                                                            </DataTrigger>
                                                            <DataTrigger Binding="{Binding NodeType}" Value="Green">
                                                                <Setter Property="Fill" Value="Green"/>
                                                            </DataTrigger>
                                                            <DataTrigger Binding="{Binding NodeType}" Value="Blue">
                                                                <Setter Property="Fill" Value="Blue"/>
                                                            </DataTrigger>
                                                        </Style.Triggers>
                                                    </Style>
                                                </Rectangle.Style>
                                            </Rectangle>
                                            <TextBlock Text="{Binding NodeId}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
                                        </Grid>
                                    </Border>
                                </StackPanel>
                                <ItemsPresenter/>
                            </StackPanel>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="ItemsPanel">
                    <Setter.Value>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal"/>
                        </ItemsPanelTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </TreeView.Resources>
        <TreeView.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </TreeView.ItemsPanel>
    </TreeView>
    <ItemsControl Grid.Column="1" ItemsSource="{Binding SelectedItems}"/>
</Grid>

在此处输入图像描述

告诉我在哪里挖?如何找到先前选择的相同类型的节点,取消选择它,然后才选择当前的?

c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    tym32167
    2020-06-07T10:16:32Z2020-06-07T10:16:32Z

    您对这个 Selected vs Active 做了一些事情,根据带有标记的良好逻辑,您需要在某处取出模型并对其进行测试。我刚刚让你的例子工作。我做了什么:

    在主模型中添加了几个方法

        public void Deactive(TreeNodeType type)
        {
            if (TreeItems != null)
                foreach (var treeItem in TreeItems)
                    Deactive(treeItem, type);
        }
    
        private void Deactive(TreeItem node, TreeNodeType type)
        {
            if (node.Active && node.NodeType == type)
            {
                node.Active = false;
            }
    
            if (node.Items != null)
                foreach (var item in node.Items)
                    Deactive(item, type);
        }
    

    在节点中添加了指向主模型的链接

    public class TreeItem : NotifyPropertyChanged
    {
        ....
        public MainViewModel MainViewModel { get; set; }
    

    略微修正了 Selected/Active 的逻辑

        public bool Selected // TreeViewItem.IsSelected
        {
            get => _selected;
            set
            {
                _selected = value;
                if (value)
                    Active = value;
                OnPropertyChanged();
            }
        }
        public bool Active // моя попытка выделять элемент
        {
            get => _active;
            set
            {
                if (value) MainViewModel.Deactive(NodeType);
                _active = value;
                OnPropertyChanged();
            }
        }
    

    从中产生了什么

    ...

    抛出我在Github上铆接的东西。但我再说一遍,我会在模型的某个地方取出这样的逻辑,否则 5 年内所有这些哈希值都会适得其反。

    • 2

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何从列表中打印最大元素(str 类型)的长度?

    • 2 个回答
  • Marko Smith

    如何在 PyQT5 中清除 QFrame 的内容

    • 1 个回答
  • Marko Smith

    如何将具有特定字符的字符串拆分为两个不同的列表?

    • 2 个回答
  • Marko Smith

    导航栏活动元素

    • 1 个回答
  • Marko Smith

    是否可以将文本放入数组中?[关闭]

    • 1 个回答
  • Marko Smith

    如何一次用多个分隔符拆分字符串?

    • 1 个回答
  • Marko Smith

    如何通过 ClassPath 创建 InputStream?

    • 2 个回答
  • Marko Smith

    在一个查询中连接多个表

    • 1 个回答
  • Marko Smith

    对列表列表中的所有值求和

    • 3 个回答
  • Marko Smith

    如何对齐 string.Format 中的列?

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5