当前位置: 首页 > news >正文

【wpf】Treeview控件的另类展示效果

在这里插入图片描述

✨简述

这里提到Treeview控件,有时它在wpf中的展示效果并不是太能满足我们的需求,而且重写控件也很麻烦,毕竟这个框架已经成型,在它的基础上修改,可施展空间很少;所以,这里主要介绍一种动态创建控件的方式来实现我们想要的效果。特别是像我这样不是以编程为主的开发人员来说,帮助还是挺大的。


Treeview常规效果
在这里插入图片描述


要实现的效果
在这里插入图片描述
那么,为了实现下面这种看着略微有点立体感的效果,我借助了大模型,同时也做了一番比较。
在代码开发方面,我常用DeepSeek、文心一言、通义以及VisualStudio自带的GitHub Copilot,其中GitHub Copilot对整个开发思路的支持还是有限的,我多用它来矫正代码问题以及一些方法的生成。

🎆IDE介绍

开集成开发环境(IDE)中,配合AI实现代码开发,已成为当前主流,能够节省很多开发时间,但前提是开发人员需要有一定基础并且懂得业务需求,不然单凭借大模型的自动生成,是无法满足UI需求的,很多效果都是基于已有模式提供,并不一定是你想要的方式。
网上很多介绍大模型能力的,其实大部分让AI自动生成的效果,并不是自己已经设计好的UI,而更多的是让大模型去发挥自己的设计能力,生成一个比较贴近提示词效果的UI。不过,提示词写的约详细明确,生成效果越好,这也不可否认。

  • 当我要设计一个点击事件时,在注释中输入“点击”俩字,后面自动引出“空白处,取消选中”,这就是GitHub Copilot的基础能力,它会理解我这个.cs代码页,知道缺少哪些功能,再根据我的提示词“点击”,联想生成后面的内容。
    在这里插入图片描述
  • 那么,我们就按照这个思路继续生成,看看它的实现效果
    在这里插入图片描述
  • 其实,当我们Tab生成这个方法之后,代码是报错的,因为它少了一个“}”。当然这是小问题,补全即可。先不管这个方法能否实现我们想要的效果,至少也提供了一个比较符合的答案,特别是在代码格式和生成的最优效果方面,还是蛮不错的,有些代码生成之后,自己都感叹没有想到这种简洁的代码段。

🎟️过程说明

🏆AI模型的支持

DeepSeek

在使用AI模型支持开发的时候,我首先想到用DeepSeek,这也是25年开始到现在讨论最火的模型。不过,在AI发展的阶段,每天每时每刻都可能有超越和被超越的可能,任何单一、封闭的事物都不可能有长足的发展。
那么,DeepSeek的问题在哪呢? 这个平台最大的问题就是上下文支持,同一个对话中,当反复问答几次之后,它的反应都是一如既往的一致:
在这里插入图片描述
在这个过程,我也是用字节新推的TRAE来辅助编程设计,可明显觉得它在wpf(C#)上开发的理解和生成能力上,略显不足,对于新手开发windows应用来说,难度还是挺大额。

文心一言

接下来就转战到文心一言进行提问,它的帮助是明显的,而且还能提供至少三种实现方式供选择,同时它还是一个知错能改的小帮手,但也不是全面的,可能我的提示词过于简单,在基本上重复的问答过后,有些问题就是反复的犯错,无法解决。但很多常规性的代码还是可以帮助实现
在这里插入图片描述
在这里插入图片描述

通义

然后就是通义,它的优点在于有个模块叫“代码模式”
在这里插入图片描述
这个分屏展示的代码模式,其实就是一个变种,一个专门针对代码编程的新模式。在wpf开发中,虽然不能如web展示方式直接实现,但生成代码效果和问题处理上,还是相比其他要专业一些。
在这里插入图片描述

GitHub Copilot

前面IDE有提到,这个帮助也是不错的。

🥏编码过程

  • 首先,我们需要创建一个wpf窗体界面,工程右键添加即可
    在这里插入图片描述
  • 然后在 .xaml中添加一个ScrollViewer容器,为了更好的使用它的滚轮效果,毕竟如果数据超出范围了,也方便查看
<Grid x:Name="MainGrid" Background="LightGray"><ScrollViewer VerticalScrollBarVisibility="Hidden" Background="LightSlateGray"Margin="5,5,0,0" Height="400" Width="780" HorizontalAlignment="Left" VerticalAlignment="Top"><Grid x:Name="DetailsGrid" Margin="5,5,5,5"><!-- 初始时不生成任何行,通过代码动态生成 --></Grid></ScrollViewer>
</Grid>

数据将会在中间青灰色部分展示出来
在这里插入图片描述

  • 接下来就是在容器“DetailsGrid”中动态生成控件了,在此之前需要准备数据。定义一个树节点的类,包含id、label和child,以及一个节点类
/// This class represents a tree node with an ID, label, and a collection of child nodes.
public class Node
{[JsonProperty("id")]public Int64 Id { get; set; }[JsonProperty("label")]public string Label { get; set; }[JsonProperty("children")]public List<Node> Children { get; set; } 
}
/// 节点类
public class RootData
{public List<Node> Data { get; set; }
}
  • 设计一个方法用于获取所有根节点
public static class JsonHelper
{// 方法:获取所有根节点public static List<Node> GetRootNodes(string jsonData){var data = JsonConvert.DeserializeObject<RootData>(jsonData);return data.Data;}
}
  • 开始对数据进行处理,这里我使用了根节点和子节点分开来动态生成数据的方式实现。

private int currentRowIndex = 0; // 全局行索引
private int newLeft = 50; // 全局左边距
/// 数据处理
/// 
private void DataDealing()
{// 处理数据var jsonData = @"{""data"": [{""id"": 1,""label"": ""根节点"",  ""children"": [{""id"": 2,""label"": ""子节点1"", ""children"": [{""id"": 3,""label"": ""子节点1-1""}, {""id"": 4,""label"": ""子节点1-2""  }]}, {""id"": 5,""label"": ""子节点2"", ""children"": [{""id"": 6,""label"": ""子节点2-1"", ""children"": [{""id"": 8,""label"": ""子节点2-1-1"" ,""children"": [{""id"": 12,""label"": ""子节点2-1-1-1"" }, {""id"": 13,""label"": ""子节点2-1-1-2"" }]}, {""id"": 9,""label"": ""子节点2-1-2"" }]}, {""id"": 7,""label"": ""子节点2-2"", ""children"": [{""id"": 10,""label"": ""子节点2-2-1"" }, {""id"": 11,""label"": ""子节点2-2-2"" }]}]}]}]}";// 获取所有根节点var rootNodes = JsonHelper.GetRootNodes(jsonData);foreach (var rootNode in rootNodes){GenerateRootControls(rootNode.Label, DetailsGrid);newLeft += 30; // 增加左边距以缩进子节点currentRowIndex++; // 增加行索引以准备下一个子节点或子节点的子节点foreach (var childNode in rootNode.Children){GenerateChildControls(childNode, DetailsGrid, 0);currentRowIndex++; // 增加行索引以准备下一个子节点或子节点的子节点}}
}
  • 根节点生成方法
/// 生成首节点控件
private void GenerateRootControls(string label, Grid grid)
{RowDefinition rowDef = new RowDefinition{Height = new GridLength(45, GridUnitType.Pixel)};grid.RowDefinitions.Add(rowDef);// 生成Rectangle控件Rectangle rectangle = new Rectangle{Width = 700,Height = 40,HorizontalAlignment = HorizontalAlignment.Left,VerticalAlignment = VerticalAlignment.Center,Fill = new SolidColorBrush(Colors.White),Stroke = new SolidColorBrush(Colors.White),RadiusX = 2,RadiusY = 2,Margin = new Thickness(5, 0, 0, 0)};// 创建一个 DropShadowEffect 对象DropShadowEffect shadowEffect = new DropShadowEffect{Color = Colors.Black,BlurRadius = 5,ShadowDepth = 2,Opacity = 0.5};// 将阴影效果应用到 Rectangle 上rectangle.Effect = shadowEffect;Grid.SetRow(rectangle, currentRowIndex);Grid.SetColumn(rectangle, 0);grid.Children.Add(rectangle);Image image = new Image{Height = 32,Width = 32,VerticalAlignment = VerticalAlignment.Center,HorizontalAlignment = HorizontalAlignment.Left,Source = new BitmapImage(new Uri("/Images/rootNode.png", UriKind.Relative)),Margin = new Thickness(5, 5, 0, 0)};Grid.SetRow(image, currentRowIndex);Grid.SetColumn(image, 0);grid.Children.Add(image);// 生成TextBlock控件: 名称TextBlock textBlockBarcode = new TextBlock{Text = label,VerticalAlignment = VerticalAlignment.Center,HorizontalAlignment = HorizontalAlignment.Left,Foreground = new SolidColorBrush(Colors.Gray),FontSize = 25,Width = 300,Margin = new Thickness(newLeft, 0, 0, 0)};Grid.SetRow(textBlockBarcode, currentRowIndex);Grid.SetColumn(textBlockBarcode, 0);grid.Children.Add(textBlockBarcode);Image image1 = new Image{Height = 32,Width = 32,VerticalAlignment = VerticalAlignment.Center,HorizontalAlignment = HorizontalAlignment.Left,Source = new BitmapImage(new Uri("/Images/down1.png", UriKind.Relative)),Margin = new Thickness(650, 0, 0, 0)};image1.MouseUp += DynamicImageDown_MouseUp;Grid.SetRow(image1, currentRowIndex);Grid.SetColumn(image1, 0);grid.Children.Add(image1);
}
  • 子节点生成方法,子节点需要判断其是否存在子节点,如果没有子节点,则不生成图片及相关操作。
/// 生成子节点控件
private void GenerateChildControls(Node childNode, Grid grid, int level)
{RowDefinition rowDef = new RowDefinition{Height = new GridLength(45, GridUnitType.Pixel)};grid.RowDefinitions.Add(rowDef);// 根据层级调整缩进或其他样式double indent = newLeft + 30 * level; // 每一层增加30像素的缩进// 生成Rectangle控件Rectangle rectangle = new Rectangle{Width = 700,Height = 40,HorizontalAlignment = HorizontalAlignment.Left,VerticalAlignment = VerticalAlignment.Center,Fill = new SolidColorBrush(Colors.White),Stroke = new SolidColorBrush(Colors.White),RadiusX = 2,RadiusY = 2,Margin = new Thickness(5, 0, 0, 0)};// 创建一个 DropShadowEffect 对象DropShadowEffect shadowEffect = new DropShadowEffect{Color = Colors.Black,BlurRadius = 5,ShadowDepth = 2,Opacity = 0.5};// 将阴影效果应用到 Rectangle 上rectangle.Effect = shadowEffect;Grid.SetRow(rectangle, currentRowIndex);Grid.SetColumn(rectangle, 0);grid.Children.Add(rectangle);if (childNode.Children != null){Image image1 = new Image{Height = 32,Width = 32,VerticalAlignment = VerticalAlignment.Center,HorizontalAlignment = HorizontalAlignment.Left,Source = new BitmapImage(new Uri("/Images/down1.png", UriKind.Relative)),Margin = new Thickness(650, 0, 0, 0)};image1.MouseUp += DynamicImageDown_MouseUp;Grid.SetRow(image1, currentRowIndex);Grid.SetColumn(image1, 0);grid.Children.Add(image1);Image image = new Image{Height = 32,Width = 32,VerticalAlignment = VerticalAlignment.Center,HorizontalAlignment = HorizontalAlignment.Left,Source = new BitmapImage(new Uri("/Images/rootNode.png", UriKind.Relative)),Margin = new Thickness(indent - 32, 5, 0, 0)};Grid.SetRow(image, currentRowIndex);Grid.SetColumn(image, 0);grid.Children.Add(image);// 生成TextBlock控件: 名称TextBlock textBlockBarcode = new TextBlock{Text = childNode.Label,VerticalAlignment = VerticalAlignment.Center,HorizontalAlignment = HorizontalAlignment.Left,Foreground = new SolidColorBrush(Colors.Gray),FontSize = 25,Width = 300,Margin = new Thickness(indent, 0, 0, 0)};Grid.SetRow(textBlockBarcode, currentRowIndex);Grid.SetColumn(textBlockBarcode, 0);grid.Children.Add(textBlockBarcode);// 递归调用以生成子节点控件foreach (var child1Node in childNode.Children){currentRowIndex++; // 增加行索引以准备下一个子节点或子节点的子节点GenerateChildControls(child1Node, DetailsGrid, level + 1);}}else{// 绑定事件rectangle.MouseDown += DynamicRectStockOutCode_MouseDown;// 生成TextBlock控件: 名称TextBlock textBlockBarcode = new TextBlock{Text = childNode.Label,VerticalAlignment = VerticalAlignment.Center,HorizontalAlignment = HorizontalAlignment.Left,Foreground = new SolidColorBrush(Colors.Black),FontSize = 25,Width = 300,Margin = new Thickness(indent, 0, 0, 0)};Grid.SetRow(textBlockBarcode, currentRowIndex);Grid.SetColumn(textBlockBarcode, 0);grid.Children.Add(textBlockBarcode);}
}
  • 点击事件方法,这两个方法都是大模型GtiHub Copilot生成的,比较经典的两处分别是
    • bool isFirstChildVisible = childControls[0].Visibility == Visibility.Visible;
    • // 获取当前行的子节点控件 var childControls = DetailsGrid.Children.OfType<UIElement>() .Where(c => Grid.GetRow(c) > currentRow) .ToList();
/// <summary>
/// 动态下拉图片点击事件
private void DynamicImageDown_MouseUp(object sender, MouseButtonEventArgs e)
{// 获取当前行索引int currentRow = Grid.GetRow((UIElement)sender);// 获取当前行的子节点控件var childControls = DetailsGrid.Children.OfType<UIElement>().Where(c => Grid.GetRow(c) > currentRow).ToList();// 隐藏或显示子节点控件,如果当前行的子节点控件是可见的,则隐藏它们,否则显示它们;if (childControls.Count == 0){return; // 如果没有子节点控件,直接返回}// 确定比当前行大的第一个元素是否可见bool isFirstChildVisible = childControls[0].Visibility == Visibility.Visible;// 隐藏或显示子节点控件foreach (var control in childControls){if (isFirstChildVisible){control.Visibility = Visibility.Collapsed;}else{control.Visibility = Visibility.Visible;}}}/// <summary>
/// 最里层节点允许点击变色
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DynamicRectStockOutCode_MouseDown(object sender, MouseButtonEventArgs e)
{// 获取当前行索引int currentRow = Grid.GetRow((UIElement)sender);// 获取当前行的子节点控件var childControls = DetailsGrid.Children.OfType<UIElement>().Where(c => Grid.GetRow(c) == currentRow).ToList();// 改变当前节点的颜色#1A76FF,如果点击了其他节点,则将之前的节点颜色改为白色foreach (var control in DetailsGrid.Children.OfType<UIElement>()){if (Grid.GetRow(control) == currentRow){if (control is Rectangle rectangle){rectangle.Fill = new SolidColorBrush(Colors.Blue);rectangle.Stroke = new SolidColorBrush(Colors.Blue);}}else{if (control is Rectangle rectangle){rectangle.Fill = new SolidColorBrush(Colors.White);rectangle.Stroke = new SolidColorBrush(Colors.White);}}}
}

🎯展示

在这里插入图片描述
至此,就实现了类似Treeview那种点击展开和选取节点的方法,感觉上效果要比重写控件资源简单一些。

欢迎大家批评指正。

相关文章:

  • Spdlog 日志组件的安装及使用
  • Linux:进程间通信->共享内存
  • 封装el-autocomplete,接口调用
  • 蓝桥杯 11. 打印大X
  • 手搓传染病模型(SEIR)
  • 2025年AEJ SCI2区:增强麻雀搜索算法CERL-SSA+工业物联网感知通信,深度解析+性能实测
  • 视觉导航中的滑动窗口
  • C++ RAII
  • 使用 Autofac 实现依赖注入
  • Redis缓存问题的深度解析与解决方案
  • C语言实现迪杰斯特拉算法进行路径规划
  • Java 面向对象:多态详解及各种用法
  • AI实战SEO关键词优化法
  • 昇腾大模型训推平台厂商介绍
  • 【Node.js 】在Windows 下搭建适配 DPlayer 的轻量(简陋)级弹幕后端服务
  • Redis哨兵模式深度解析:实现高可用与自动故障转移的终极指南
  • 双指针算法(2)——复写零
  • 天梯——现代战争
  • 基于STM32、HAL库的ADS1115模数转换器ADC驱动程序设计
  • AntBio: 2025 AACR Meeting - Charting New Oncology Frontiers Together
  • 诗词文赋俱当歌,听一听古诗词中的音乐性
  • 事关稳就业稳经济,10张海报看懂这场发布会的政策信号
  • 持续更新丨伊朗港口爆炸事件已致406人受伤
  • 印度媒体称印巴在克什米尔再次交火
  • 罗马教皇方济各葬礼在梵蒂冈举行
  • 世联行:2024年营业收入下降27%,核心目标为“全面消除亏损公司和亏损项目”