zoukankan      html  css  js  c++  java
  • CommonTree

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

    xaml

    <UserControl.Resources>
        <BooleanToVisibilityConverter x:Key="Boolean2VisibilityConverter"/>
        <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=TwoWay}" Tag="{Binding Node}" 
                                  PreviewMouseUp="CheckBox_PreviewMouseUp">
                        </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" KeyDown="TextBoxSearchContent_KeyDown" Text="{Binding SearchContent, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
            <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" Visibility="{Binding SearchContentVisible, Converter={StaticResource Boolean2VisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="150"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock 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" VerticalAlignment="Center" />
            <Button Grid.Row="1" Grid.Column="2" Style="{DynamicResource ButtonICOStyle}" Content="{iconPacks:Modern Kind=ArrowUp}" Width="30" Height="30"  Click="ButtonPre_Click" VerticalAlignment="Center" />
        </Grid>
        <TreeView Grid.Row="2" Name="treeFrameInfo" ItemsSource="{Binding TreeSource}"
                  ScrollViewer.HorizontalScrollBarVisibility="Auto"
                  BorderThickness="0"
                  ItemTemplate="{DynamicResource ItemNode}" 
                  VirtualizingStackPanel.IsVirtualizing="True"
                  VirtualizingStackPanel.VirtualizationMode ="Recycling">
            <TreeView.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel/>
                </ItemsPanelTemplate>
            </TreeView.ItemsPanel>
            <TreeView.ItemContainerStyle>
                <Style TargetType="TreeViewItem">
                    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
                </Style>
            </TreeView.ItemContainerStyle>
        </TreeView>
    </Grid>
    
    

    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;
    
        /// <summary>
        /// 已经勾选的项目
        /// </summary>
        public List<FrameTree> SelectItems { get; set; }
    
    
        /// <summary>
        /// 待查询内容
        /// </summary>
        public string SearchContent
        {
            get { return (string)GetValue(SearchContentProperty); }
            set { SetValue(SearchContentProperty, value); }
        }
        public static readonly DependencyProperty SearchContentProperty =
            DependencyProperty.Register("SearchContent", typeof(string), typeof(CommonTree), new PropertyMetadata(null));
    
        /// <summary>
        /// 查询统计结果可见性
        /// </summary>
        public bool SearchContentVisible
        {
            get { return (bool)GetValue(SearchContentVisibleProperty); }
            set { SetValue(SearchContentVisibleProperty, value); }
        }
        public static readonly DependencyProperty SearchContentVisibleProperty =
            DependencyProperty.Register("SearchContentVisible", typeof(bool), typeof(CommonTree), new PropertyMetadata(false));
    
    
        /// <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;
        /// <summary>
        /// 结点勾选通知事件
        /// </summary>
        public event EventHandler TreeCheckChangedEvent;
    
        public CommonTree()
        {
            InitializeComponent();
            if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            {
                return;
            }
            InitOther();
        }
    
        // 参数初始化
        private void InitOther()
        {
            SelectItems = new List<FrameTree>();
            _helper = new TreeViewItemHelper();
            _searchListItems = new List<FrameTree>();
            _searchTreeViewItems = new List<TreeViewItem>();
        }
    
        /// <summary>
        /// 清空树的勾选项
        /// </summary>
        public void TreeClear()
        {
            SelectItems.Clear();
            RecursionCheckState(_treeRoot, false);
    
        }
        /// <summary>
        /// 递归改变模型对象及子对象的勾选状态
        /// </summary>
        /// <param name="node">模型对象</param>
        /// <param name="state">状态值</param>
        private void RecursionCheckState(FrameTree node, bool state)
        {
            if (node == null)
            {
                return;
            }
            node.IsCheck = state;
            // 递归
            if (node.Children != null)
            {
                foreach (FrameTree item in node.Children)
                {
                    RecursionCheckState(item, state);
                }
            }
        }
    
        // 文本框回车查询
        private void TextBoxSearchContent_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Enter)
            {
                ButtonSearch_Click(null, null);
            }
        }
        // 查询
        private void ButtonSearch_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(SearchContent))
                {
                    SearchContentVisible = false;
                    return;
                }
                SearchContentVisible = true;
                _searchIndex = 0;
                _searchListItems.Clear();
                _searchTreeViewItems.Clear();
                SearchFrameTree(_treeRoot, SearchContent, _searchListItems);
    
                if (_searchListItems.Count > 0)
                {
                    _searchTreeViewItems.AddRange(_helper.GetTreeViewItems(treeFrameInfo, _searchListItems));
                    if (_searchTreeViewItems.Count > 0)
                    {
                        var item = _searchTreeViewItems[0];
                        if (!_helper.IsExistVisible(item))
                        {
                            _ = _helper.PositionTreeViewItem(treeFrameInfo, _searchListItems[0]);
                        }
                        Position(item);
                    }
                    lblSearch.Text = $"当前【{_searchIndex + 1}】,共【{_searchTreeViewItems.Count}】项";
                }
                else
                {
                    lblSearch.Text = $"当前【0】,共【0】项";
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("搜索发生异常:" + ex.Message.ToString(), ex);
            }
        }
        // 刷新
        private void ButtonRefresh_Click(object sender, RoutedEventArgs e)
        {
            SearchContent = string.Empty;
            SearchContentVisible = false;
            SelectItems.Clear();
            _helper.ScrollToTop(treeFrameInfo);
            // 事件通知
            TreeRefreshEvent?.Invoke(this, EventArgs.Empty);
        }
        // 下一个
        private void ButtonNext_Click(object sender, RoutedEventArgs e)
        {
            _searchIndex++;
            if (_searchIndex > _searchTreeViewItems.Count - 1)
            {
                _searchIndex = _searchTreeViewItems.Count - 1;
            }
    
            var item = _searchTreeViewItems[_searchIndex];
            if (!_helper.IsExistVisible(item))
            {
                Console.WriteLine($"当前项已不可见!!!");
                _searchTreeViewItems.Clear();
                _searchTreeViewItems.AddRange(_helper.GetTreeViewItems(treeFrameInfo, _searchListItems));
            }
            else if (item.DataContext is FrameTree frame && !frame.Name.Contains(SearchContent))
            {
                Console.WriteLine($"{frame.Name}: {SearchContent} 跑偏了!!!");
                _searchTreeViewItems.Clear();
                _searchTreeViewItems.AddRange(_helper.GetTreeViewItems(treeFrameInfo, _searchListItems));
            }
            Position(_searchTreeViewItems[_searchIndex]);
            SearchContentVisible = true;
            lblSearch.Text = $"当前【{_searchIndex + 1}】,共【{_searchTreeViewItems.Count}】项";
        }
        // 上一个
        private void ButtonPre_Click(object sender, RoutedEventArgs e)
        {
            _searchIndex--;
            if (_searchIndex < 0)
            {
                _searchIndex = 0;
            }
    
            var item = _searchTreeViewItems[_searchIndex];
            if (!_helper.IsExistVisible(_searchTreeViewItems[_searchIndex]))
            {
                Console.WriteLine($"当前项已不可见!!!");
                _searchTreeViewItems.Clear();
                _searchTreeViewItems = _helper.GetTreeViewItems(treeFrameInfo, _searchListItems);
            }
            else if (item.DataContext is FrameTree frame && !frame.Name.Contains(SearchContent))
            {
                Console.WriteLine($"{frame.Name}: {SearchContent} 跑偏了!!!");
                _searchTreeViewItems.Clear();
                _searchTreeViewItems.AddRange(_helper.GetTreeViewItems(treeFrameInfo, _searchListItems));
            }
            Position(_searchTreeViewItems[_searchIndex]);
            SearchContentVisible = true;
            lblSearch.Text = $"当前【{_searchIndex + 1}】,共【{_searchTreeViewItems.Count}】项";
        }
    
        /// <summary>
        /// TreeViewItem定位
        /// </summary>
        /// <param name="item">TreeViewItem</param>
        private void Position(TreeViewItem item)
        {
            if (item == null)
            {
                return;
            }
    #if DEBUG
            Console.WriteLine("
    ------------------------------------------------------");
            var indx = _searchTreeViewItems.IndexOf(item);
            var preIdx = indx - 1;
            if (preIdx >= 0)
            {
                PrintItem(preIdx, _searchTreeViewItems[preIdx]);
            }
            Console.Write("√");
            PrintItem(indx, item);
            var nextIdx = indx + 1;
            if (nextIdx < _searchTreeViewItems.Count)
            {
                PrintItem(nextIdx, _searchTreeViewItems[nextIdx]);
            }
            Console.WriteLine("------------------------------------------------------");
    #endif
    
            // 虚化造成效果不佳,有可能定位不精确,可以来回多切几次
            //item.BringIntoView();
    
            // item.UpdateLayout();
            item.IsSelected = true;
            item.IsExpanded = true;
            _ = item.Focus();
        }
        private void PrintItem(int index, TreeViewItem item)
        {
            if (item.DataContext is FrameTree frame)
            {
                Console.WriteLine($"{index}. {frame.Name}");
            }
            else
            {
                Console.WriteLine($"{index}. {item.DataContext}");
            }
        }
        // 勾选框点击之前的操作
        private void CheckBox_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (sender is CheckBox ckb && ckb.Tag is FrameTree node)
            {
                // 点击之前的状态
                if (ckb.IsChecked.HasValue && ckb.IsChecked.Value)
                {
                    ChangeCheckState(node, false);
                    ckb.IsChecked = true;
                }
                else
                {
                    ChangeCheckState(node, true);
                    ckb.IsChecked = false;
                }
                // 事件通知
                TreeCheckChangedEvent?.Invoke(this, new TreeCheckBoxEventArgs { SelectItems = SelectItems });
            }
        }
    
        private static int _count = 0;
        /// <summary>
        /// 改变模型对象及子对象的勾选状态
        /// </summary>
        /// <param name="node">模型对象</param>
        /// <param name="state">状态值</param>
        private void ChangeCheckState(FrameTree node, bool state)
        {
            if (node == null)
            {
                return;
            }
            // 状态变化了
            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;
        }
        /// <summary>
        /// 查询满足条件的数据模型对象
        /// </summary>
        /// <param name="node">查询起始点</param>
        /// <param name="content">查询内容</param>
        /// <param name="rslt">对象集合</param>
        private void SearchFrameTree(FrameTree node, string content, List<FrameTree> rslt)
        {
            if (node.Name.Contains(content))
            {
                rslt.Add(node);
            }
            foreach (FrameTree child in node.Children)
            {
                SearchFrameTree(child, content, rslt);
            }
        }
    
    }
    

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

    demo

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

    prism

    xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
    
    <wesson:CommonTree DataSource="{Binding TreeSource, Mode=TwoWay}" x:Name="CommonTreeInfo"
                       SearchContent="{Binding PropSetEntity.SearchContent, Mode=TwoWay}"
                       SearchContentVisible="{Binding PropSetEntity.SearchContentVisible, Mode=TwoWay}">
        <b:Interaction.Triggers>
            <b:EventTrigger EventName="TreeRefreshEvent">
                <b:InvokeCommandAction Command="{Binding CommonTreeRefreshCommand}"/>
            </b:EventTrigger>
            <b:EventTrigger EventName="TreeCheckChangedEvent">
                <b:InvokeCommandAction Command="{Binding CommonTreeCheckChangedCommand}" PassEventArgsToCommand="True"/>
            </b:EventTrigger>
        </b:Interaction.Triggers>
    </wesson: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 => _treeSource;
        set => SetProperty(ref _treeSource, value);
    }
    
    public DelegateCommand CommonTreeRefreshCommand { get; private set; }
    public DelegateCommand<object> CommonTreeCheckChangedCommand { get; private set; }
    
    public ctor()
    {
        TreeSource = new List<FrameTree>();
        CommonTreeRefreshCommand = new DelegateCommand(CommonTree_Refresh);
        CommonTreeCheckChangedCommand = new DelegateCommand<object>(CommonTree_CheckChanged);
    }
    
    private void CommonTree_CheckChanged(object parameter)
    {
        if (parameter is TreeCheckBoxEventArgs args)
        {
            // TODO
        }
    }
    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
        {
            Guid = dto.Guid,
            Name = dto.Name,
            Type = dto.Type,
            Parent = parentNode,
            OriginalSource = dto,
        };
        if (dto.Children == null)
        {
            return node;
        }
        foreach (var item in dto.Children.OrderBy(o => o.Name))
        {
            node.Children.Add(GetTreeFrameChildren(item, node));
        }
        return node;
    }
    
  • 相关阅读:
    Linux中rar解压软件
    Linux中rpm和yum安装软件
    查看nova日志
    po编译为mo;django翻译多义性问题解决
    某服務器開端口
    linux環境查找某文件夾下含有某字符串的所有文件
    gerrit +git使用
    ubuntu 暂时使用root权限
    mysql-求中位数方法
    phonecat-angular.js route.js加载启动失败
  • 原文地址:https://www.cnblogs.com/wesson2019-blog/p/15161526.html
Copyright © 2011-2022 走看看