从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

上节我们实现了Combox的显示,为了更好地用户体验,我们需要让所属分类Combox实现类似树形显示,首先需要实现排序算法。

一、排序算法

这块是场硬仗,说实话我个人也不大愿意做这种编码,可能是因为我数学基础和数据结构都很差,没什么理论基础,只能硬来,对做超出自己能力范围内的事情就比较费脑子了。

这段排序算法算上注释我写了将近150行。之前是不大想详细讲了,直接放在前一节,但是篇幅还是太长了,索性就单独再开一节,详细讲下过程。

先明确下大概需求:

一组列表数据,通过Id和ParentId建立起了层次关系,现在需要将他们按层次关系排好序,同一节点需要按ShowOrder来排序。

我先说下大体思路:

1)把根级节点提取出来,按ShowOrder排序;

2)把一级节点提出来,按ShowOrder排序;遍历每个一级节点,找到它们的父节点,然后插入到下方;

3)切换到二级节点,重复2)步骤,直到所有级别的节点全部执行完毕,就完成排序了;

所以代码的执行步骤大体如下:

1)计算所有节点的深度,按深度排序,深度相同的按ShowOrder排序;

2)得到最大深度,以深度建立循环体,依次执行,直到最大深度执行完毕;

3)执行每个节点,把它们分别插入到相应的位置。

这个步骤还有个细节需要注意。在插入时需要建立一个缓存机制,如果没有缓存机制,每个节点都插入同级节点,已经按ShowOrder排好的顺序可能就乱了。建立缓存机制后,相同ParentId的先统一加到缓存,当ParentId切换以后,再把缓存里的所有节点一次性插入到相应的位置,这样可以保证这些节点仍然可以按ShowOrder的顺序排好。

这里面我们需要用到一个深度的值,它是跟随每个Category实例的,后续在做层次显示的时候也需要这个值,所以我在Model.Category中又新加了一个Depth的类变量,它不需要参与数据库存储。

排序的代码:

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

在Model.Category中我声明了Sort函数,定义成了static,意思是不需要实例化Model.Category就可以访问该函数。

代码中调用的函数我在下面会单独解释,这段代码有个新的知识点需要再来介绍下:

categories.Sort((x, y) => {

if (x.Depth > y.Depth) return 1;

if (x.Depth < y.Depth) return -1;

return x.ShowOrder - y.ShowOrder;

} );

这段代码C#初学者看到了应该会有点懵,categories.Sort还好理解,就是调用List中的Sort函数执行排序嘛,后面的那一堆还带个=>的是什么鬼?

这种在c#中被称为Lamda表达式,如果你对javascript的匿名函数有了解的话,这个就很好理解了。这个其实就是C#中的匿名函数:

(x, y),x和y就是匿名函数的两个参数名

=>{...} 这个就是声明函数体,...中就是你要编写的函数体内的代码。

其实上面的写法等价于:

categories.Sort(comp_func);

...

static int comp_func(Category x, Category y)

{

if (x.Depth > y.Depth) return 1;

if (x.Depth < y.Depth) return -1;

return x.ShowOrder - y.ShowOrder;

}

Lamda表达式的好处是不需要额外声明一个函数名称,临时用下而已。不过说实话,我个人不大喜欢这种写法,语言的可读性较差,一切为了教学:)同学们可以自行选择。

代码的执行逻辑教程上面有写,代码中我也写了备注,大家认真看下应该就能明白了。


再分别说下其他的函数:

count_depth函数,计算每个节点的深度,并保存在类变量中。

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

get_index_by_parentId函数,获取当前节点在指定节点列表中的序号。

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

detach_category_current_depth函数,提取指定深度的所有类别

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

insert_into_categories函数,将提取出来的分类列表按顺序插入到目标列表

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

上述代码看起来可能比较乱,没有任何排序算法做基础,如有雷同纯属巧合。我对算法这块实在不擅长,能达到目的我就满足了。

排序完成后,显示效果是这样的:

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

如果对自己要求不高,这样也可以凑合用,但是还没有层次关系,与我们的期望有差距。

我们期望的是能够带一点层次关系,比如二级较一级要向右空几个空格,三级较二级再空几个,要想实现这样的效果,就需要对ComboBox进行重绘了。男人就是要对自己狠一点。

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

操作方法如下:

cbxParent控件:

属性窗口DrawMode设置成OwnerDrawFixed

事件窗口响应DrawItem事件

运行后,你会发现,下拉框什么都不见了,因为已经开启了自绘控件模式,每一项都需要程序来绘制。

那么如何进行自绘呢?

先上代码:

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

逐行解释:

绘制背景:

e.DrawBackground();

绘制选中焦点矩形:

e.DrawFocusRectangle();

创建画刷,用来输出文字:

Brush brush = new SolidBrush(e.ForeColor);

根据深度增加空格数量:

string text = "";

for (int i = 1; i <= category.Depth; i++)

{

text += " ";

}

text += category.Name;

如果有子节点,则字体用粗体显示:

Font font = null;

if (category.HasChild)

{

font = new Font(e.Font.FontFamily, e.Font.Size, FontStyle.Bold);

}

else

{

font = new Font(e.Font.FontFamily, e.Font.Size);

}

指定位置、画刷、字体等输出文字:

e.Graphics.DrawString(text, font, brush, e.Bounds.X, e.Bounds.Y + 3);

销毁画刷和字体:

brush.Dispose();

font.Dispose();

代码基本上一看就明白,最后销毁这里需要说明下。

我们使用C#编码,如果声明的是内存资源,那么.net会自动在适合的时候进行内存回收,我们不需要像C和C++一样去主动管理内存回收,这也是C#编码效率比C/C++高的重要原因之一,以前因为C/C++内存泄露问题,经常要调好几天的Bug才能把泄漏点找出来。

但这不意味着我们用c#了就随便声明不用再去考虑回收了,除了内存资源以外,还有很多系统资源都是有限的,比较常见的有Gdi资源、位图资源、网络资源、文件资源,这些系统资源都是有限的,使用完成后需要手动回收,如果不回收,就会造成系统资源耗尽而导致程序崩溃。

这里说的Gdi,英文全称Graphics Device Interface,直译就是图形设备接口,专门用于windows程序图形图像显示的,我们看到的这些标准windows控件,都是通过Windows系统底层的GDI函数绘制出来的。

在C#中,我们用到的画笔Pen、画刷Brush、字体Font等等这些都属于Gdi对象。我们通过系统自带的任务管理器就可以获知程序用到了多少个Gdi对象。

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

这些都需要在使用完毕之后及时地回收。

上面的代码其实还可以优化的。Gdi对象不需要每次都创建,在CategoryManagedForm类中声明Brush和Font的变量,如果为空就创建,不为空就继续使用,这样就只是在第一次使用时创建。窗体关闭时再回收,然后置空。很多效率优化都是基于此,频繁处理的代码越少越好,用空间换时间。只不过目前这点调用量还不需要如此,而且用户操作也不可能像程序执行一样频繁,优化就先不做了。

最终运行的效果:

从零开始系列,用C#做软件产品:私人日记(十)Combox树形显示

分类管理界面的技术难点我们现在已经攻克了,再接下来就是实现右键分类菜单与分类管理界面的数据联动,我们下节继续。

----------------------------------------------------

本教程尽量保证1-2天一更,项目源码已作为开源项目加入到Git,代码内容会随教程实时更新,大家有兴趣的话可以关注我,以获得最及时的更新。私信:私人日记 可以来获取Git的链接。

C#基本语法大家在头条搜索“菜鸟c#”,个人感觉这个网站还可以。

大家阅读过程中有哪些看不懂或未尽兴的地方,可以在评论区留言,我会先记下来在后续的教程中找机会再说。

教程有帮助的话请大家帮忙关注、转发、扩散,能不能开专栏还需要你们的支持!

展开阅读全文

页面更新:2024-03-16

标签:节点   软件产品   缓存   控件   算法   函数   深度   层次   私人   字体   声明   内存   代码   关系   程序   教程   日记   系列   资源   科技

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top