CommonTree

时间:2021-08-19
本文章向大家介绍CommonTree,主要包括CommonTree使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

树结构的数据显示,
满足一般的数据加载、查询、刷新、勾选并获取勾选项

xaml

<UserControl x:Class="ZHHT.Themes.Controls.Wpf.CommonTree"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks" 
             d:DesignHeight="450" d:DesignWidth="300">
    <UserControl.Resources>
        <HierarchicalDataTemplate x:Key="ItemNode" ItemsSource="{Binding Children,Mode=TwoWay}">
            <Grid  Background="Transparent">
                <StackPanel MinHeight="25" Orientation="Horizontal" Background="Transparent" 
                        HorizontalAlignment="Left" >
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="25"></ColumnDefinition>
                            <ColumnDefinition Width="auto"></ColumnDefinition>
                            <ColumnDefinition Width="auto"></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <CheckBox Grid.Column="0" IsChecked="{Binding IsCheck, Mode=OneWay}" Tag="{Binding Node}" 
                                  Checked="ItemNode_Checked" Unchecked="ItemNode_Unchecked">
                        </CheckBox>
                        <TextBlock Grid.Column="1" Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="3 0" />
                        <TextBlock Grid.Column="2" Text="{Binding Remark}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="3 0" />
                    </Grid>
                </StackPanel>
            </Grid>
        </HierarchicalDataTemplate>
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBox Grid.Column="0" x:Name="txtSearchContent" KeyDown="TextBoxSearchContent_KeyDown"/>
            <Button Grid.Column="1" Style="{DynamicResource MahApps.Styles.Button.Circle}" Content="{iconPacks:ForkAwesome Kind=Search}" ToolTip="查询" Width="30" Height="30" Margin="5" Click="ButtonSearch_Click"></Button>
            <Button Grid.Column="2" Style="{DynamicResource MahApps.Styles.Button.Circle}" Content="{iconPacks:ForkAwesome Kind=Refresh}" ToolTip="刷新" Width="30" Height="30" Margin="5"  Click="ButtonRefresh_Click"></Button>
        </Grid>
        <Grid Grid.Row="1" Name="grdSerchContent" Visibility="Collapsed">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="150"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Grid.Row="1" Grid.Column="0" x:Name="lblSearch" />
            <Button Grid.Row="1" Grid.Column="1" Style="{DynamicResource ButtonICOStyle}" Content="{iconPacks:Modern Kind=ArrowDown}" Width="30" Height="30" Click="ButtonNext_Click"></Button>
            <Button Grid.Row="1" Grid.Column="2" Style="{DynamicResource ButtonICOStyle}" Content="{iconPacks:Modern Kind=ArrowUp}" Width="30" Height="30"  Click="ButtonPre_Click"></Button>
        </Grid>
        <TreeView Grid.Row="2" Name="treeFrameInfo" ItemsSource="{Binding TreeSource}"
                  ScrollViewer.HorizontalScrollBarVisibility="Auto"
                  BorderThickness="0"
                  ItemTemplate="{DynamicResource ItemNode}" Style="{DynamicResource TreeViewExpanded}"
                  VirtualizingPanel.IsVirtualizing="True"
                  VirtualizingStackPanel.VirtualizationMode ="Standard">
        </TreeView>
    </Grid>
</UserControl>

cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
public partial class CommonTree : UserControl
{
    private FrameTree _treeRoot;
    private int _searchIndex = 0;
    /// <summary>
    /// 查询到的项目列表
    /// </summary>
    private List<FrameTree> _searchListItems;
    /// <summary>
    /// 查询到的项目结点
    /// </summary>
    private List<TreeViewItem> _searchTreeViewItems;
    private TreeViewItemHelper _helper = null;
    private Dictionary<FrameTree, List<FrameTree>> _dicGroupTarget = new Dictionary<FrameTree, List<FrameTree>>();

    /// <summary>
    /// 已经勾选的项目
    /// </summary>
    public List<FrameTree> SelectItems { get; set; }
    /// <summary>
    /// 树的数据源
    /// </summary>
    public List<FrameTree> DataSource
    {
        get => (List<FrameTree>)GetValue(DataSourceProperty);
        set => SetValue(DataSourceProperty, value);
    }
    public static readonly DependencyProperty DataSourceProperty =
        DependencyProperty.Register("DataSource", typeof(List<FrameTree>), typeof(CommonTree), new PropertyMetadata(null, OnDataSourceChanged));
    private static void OnDataSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        CommonTree ctl = (CommonTree)d;
        ctl.OnDataSourceChanged(e.NewValue);
    }
    private void OnDataSourceChanged(object newValue)
    {
        if (newValue is List<FrameTree> list && list.Count > 0)
        {
            _treeRoot = list[0];
            TreeClear();
        }
    }


    /// <summary>
    /// 刷新事件
    /// </summary>
    public event EventHandler TreeRefreshEvent;

    public CommonTree()
    {
        InitializeComponent();
        if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
        {
            return;
        }
        InitOther();
    }

    /// <summary>
    /// 清空树的勾选项
    /// </summary>
    public void TreeClear()
    {
        SelectItems.Clear();
        ChangeCheckState(_treeRoot, false);
    }

    private void TextBoxSearchContent_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == System.Windows.Input.Key.Enter)
        {
            ButtonSearch_Click(null, null);
        }
    }

    private void InitOther()
    {
        SelectItems = new List<FrameTree>();
        _helper = new TreeViewItemHelper();
        _searchListItems = new List<FrameTree>();
        _dicGroupTarget = new Dictionary<FrameTree, List<FrameTree>>();
        _searchTreeViewItems = new List<TreeViewItem>();

        grdSerchContent.Visibility = Visibility.Collapsed;
    }

    private void ButtonSearch_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(txtSearchContent.Text))
            {
                grdSerchContent.Visibility = Visibility.Collapsed;
                return;
            }

            _helper.targetTreeViewItem.Clear();
            _searchListItems.Clear();
            _searchTreeViewItems.Clear();
            _searchIndex = 0;

            TreeViewItem root = treeFrameInfo.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
            _searchListItems = SelectTreeItem(_treeRoot, txtSearchContent.Text);

            for (int i = _searchListItems.Count - 1; i >= 0; i--)
            {
                if (_searchListItems[i].Parent == null)
                {
                    _ = _searchListItems.Remove(_searchListItems[i]);   //这个为空应该是城市.
                }
            }

            IEnumerable<IGrouping<string, FrameTree>> targetTreeGroup = from g in _searchListItems group g by g.Parent.Guid;

            if (_searchListItems != null && _searchListItems.Count > 0)
            {
                _dicGroupTarget.Clear();
                foreach (IGrouping<string, FrameTree> item in targetTreeGroup)
                {
                    //同一个父亲下的节点
                    List<FrameTree> tempFrameTree = _searchListItems.Where(a => a.Parent.Guid == item.Key).ToList();

                    _dicGroupTarget.Add(tempFrameTree[0].Parent, tempFrameTree);
                }

                _ = _helper.GetTreeViewItemsForFrameTree(root, txtSearchContent.Text, _dicGroupTarget);

                _searchTreeViewItems = _helper.targetTreeViewItem;
                if (_searchTreeViewItems != null && _searchTreeViewItems.Count > 0)
                {
                    _searchTreeViewItems[_searchIndex].IsSelected = true;
                    _searchTreeViewItems[_searchIndex].IsExpanded = true;
                    _ = _searchTreeViewItems[_searchIndex].Focus();
                    grdSerchContent.Visibility = Visibility.Visible;
                    lblSearch.Content = string.Format("当前【{0}】,共【{1}】项", _searchIndex + 1, _searchTreeViewItems.Count);
                }
            }
            else
            {
                grdSerchContent.Visibility = Visibility.Visible;
                lblSearch.Content = string.Format("当前【{0}】,共【{1}】项", _searchIndex, _searchTreeViewItems.Count);

            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("搜索发生异常:" + ex.Message.ToString(), ex);
        }
    }

    private void ButtonRefresh_Click(object sender, RoutedEventArgs e)
    {
        txtSearchContent.Clear();
        SelectItems.Clear();
        grdSerchContent.Visibility = Visibility.Collapsed;
        _helper.ScrollToTop(treeFrameInfo);

        TreeRefreshEvent?.Invoke(this, EventArgs.Empty);
    }

    private void ButtonNext_Click(object sender, RoutedEventArgs e)
    {
        if (_searchTreeViewItems != null)
        {
            _searchIndex++;
            _searchIndex = Math.Min(_searchIndex, _searchTreeViewItems.Count - 1);

            if (_searchTreeViewItems.Count > 0)
            {
                if (_searchTreeViewItems[_searchIndex].DataContext.ToString().Contains("DisconnectedItem"))
                {
                    TreeViewItem root = treeFrameInfo.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
                    _helper.targetTreeViewItem.Clear();

                    if (_dicGroupTarget != null && _dicGroupTarget.Count > 0)
                    {
                        //重新虚化
                        _ = _helper.GetTreeViewItemsForFrameTree(root, txtSearchContent.Text, _dicGroupTarget);
                    }

                    _searchTreeViewItems = _helper.targetTreeViewItem;
                }

                _searchTreeViewItems[_searchIndex].IsSelected = true;
                _searchTreeViewItems[_searchIndex].IsExpanded = true;
                _ = _searchTreeViewItems[_searchIndex].Focus();
                grdSerchContent.Visibility = Visibility.Visible;
                lblSearch.Content = string.Format("当前【{0}】,共【{1}】项", _searchIndex + 1, _searchTreeViewItems.Count);
            }
        }
    }

    private void ButtonPre_Click(object sender, RoutedEventArgs e)
    {
        if (_searchTreeViewItems != null)
        {
            _searchIndex--;
            _searchIndex = Math.Max(_searchIndex, 0);

            if (_searchTreeViewItems.Count > 0)
            {
                if (_searchTreeViewItems[_searchIndex].DataContext.ToString().Contains("DisconnectedItem"))
                {
                    TreeViewItem root = treeFrameInfo.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
                    _helper.targetTreeViewItem.Clear();
                    if (_dicGroupTarget != null && _dicGroupTarget.Count > 0)
                    {
                        //重新虚化
                        _ = _helper.GetTreeViewItemsForFrameTree(root, txtSearchContent.Text, _dicGroupTarget);
                    }
                    _searchTreeViewItems = _helper.targetTreeViewItem;
                }

                _searchTreeViewItems[_searchIndex].IsSelected = true;
                _searchTreeViewItems[_searchIndex].IsExpanded = true;
                _ = _searchTreeViewItems[_searchIndex].Focus();
                grdSerchContent.Visibility = Visibility.Visible;
                lblSearch.Content = string.Format("当前【{0}】,共【{1}】项", _searchIndex + 1, _searchTreeViewItems.Count);
            }
        }
    }
    private void Frame_Checked(object sender, RoutedEventArgs e)
    {
        if (sender is CheckBox checkedItem)
        {
            if (checkedItem.Tag is FrameTree node)
            {
                ChangeCheckState(node, true);
            }
        }
    }

    private void Frame_Unchecked(object sender, RoutedEventArgs e)
    {
        if (sender is CheckBox checkedItem)
        {
            if (checkedItem.Tag is FrameTree node)
            {
                ChangeCheckState(node, false);
            }
        }
    }

    private static int _count = 0;
    private void ChangeCheckState(FrameTree node, bool state)
    {
        if (node.IsCheck != state)
        {
            if (state)
            {
                _count++;
                SelectItems.Add(node);
            }
            else
            {
                _count--;
                _ = SelectItems.RemoveAll(x => x.Guid == node.Guid);
            }
            Console.WriteLine($"{_count}: {node.Name}:{node.IsCheck}==>{state}");

            if (node.Children != null)
            {
                foreach (FrameTree item in node.Children)
                {
                    ChangeCheckState(item, state);
                }
            }
        }
        node.IsCheck = state;
    }
    private List<FrameTree> SelectTreeItem(FrameTree node, string name)
    {
        List<FrameTree> selects = new List<FrameTree>();
        if (node.Name.Contains(name))
        {
            selects.Add(node);
        }
        for (int i = 0; i < node.Children.Count; i++)
        {
            FrameTree childrenNode = node.Children[i];
            selects.AddRange(SelectTreeItem(childrenNode, name));
        }
        return selects;
    }

}

其中,结构树的实体FrameTree,树的帮助类TreeViewItemHelper可自己定义实现,

demo

<wesson:CommonTree x:Name="CommonTreeInfo" TreeRefreshEvent="CommonTreeInfo_TreeRefreshEvent"/>

prism

<zhht:CommonTree Grid.Row="1" DataSource="{Binding TreeSource, Mode=TwoWay}" x:Name="CommonTreeInfo">
    <b:Interaction.Triggers>
        <b:EventTrigger EventName="TreeRefreshEvent">
            <b:InvokeCommandAction Command="{Binding CommonTreeRefreshCommand}"/>
        </b:EventTrigger>
    </b:Interaction.Triggers>
</zhht:CommonTree>


// view
private void ButtonClear_Click(object sender, System.Windows.RoutedEventArgs e)
{
    CommonTreeInfo.TreeClear();
}

private void ButtonGetData_Click(object sender, System.Windows.RoutedEventArgs e)
{
    var btn = (Button)sender;
    btn.CommandParameter = CommonTreeInfo.SelectItems;
}

// viewmodel
private List<FrameTree> _treeSource;
/// <summary>
/// 可绑定树的数据列表
/// </summary>
public List<FrameTree> TreeSource
{
    get { return _treeSource; }
    set { SetProperty(ref _treeSource, value); }
}

public DelegateCommand CommonTreeRefreshCommand { get; private set; }

public ctor()
{
    TreeSource = new List<FrameTree>();
    CommonTreeRefreshCommand = new DelegateCommand(CommonTree_Refresh);
}

private async Task DataBindAsync()
{
    var response = // 异步获取数据
    if (response != null)
    {
        var root = new FrameTree();
        root = GetTreeFrameChildren(response, root);
        TreeSource.Clear();
        TreeSource = new List<FrameTree> { root };
    }
}

private FrameTree GetTreeFrameChildren(object dto, FrameTree parentNode)
{
    var node = new FrameTree();
    node.Guid = dto.Guid;
    node.Name = dto.Name;
    node.OriginalSource = dto;
    node.ParentGuid = parentNode.Guid;
    node.Parent = parentNode;
    if (dto.Children == null)
    {
        return node;
    }
    foreach (var item in dto.Children.OrderBy(o => o.Name))
    {
        node.Children.Add(GetTreeFrameChildren(item, node));
    }
    return node;
}

原文地址:https://www.cnblogs.com/wesson2019-blog/p/15161526.html