前言

以前看别人的控件好看只有羡慕的份;以前觉得控件重绘是个很复杂的东西;以前知道MSDN很全面很专业却一直没有好好用起来;

作为初级程序猿,不能原地踏步,来吧,让我们一起把 TreeView 美化一下,每天进步一点点!

要点

1、WinForm自定义控件

2、重绘

3、MSDN使用

基础准备:利用MSDN查找我们需要的资料

1、进入MSDN技术资料库:http://msdn.microsoft.com/library

2、在右上角搜索栏里输入 TreeView

3、找到TreeView类(System.Windows.Forms)这一条【应该是搜索结果里的第二条】,里面详尽的介绍了这个类

这里直接附上地址:http://msdn.microsoft.com/zh-cn/library/system.windows.forms.treeview(v=vs.110).aspx

4、TreeView的方法、属性、事件等可以大概浏览下,知道有这个东西就行,继续往下看,我发现了我们需要的东西:

这里的意思是我们可以完全自定义TreeView的外观有2个要点:

  一个是将DrawMode属性设置为TreeViewDrawMode.Normal以外的值;

  一个是在DrawNode事件里我们可以进行对控件的皮肤修改

5、那DrawNode事件怎么用呢?截图中我们看到末尾的 DrawNode 字样是个超级链接,果断点进去一看究竟,于是发现了如下描述:

6、这段文字很详尽的介绍了DrawNode事件,我们可以总结出3个要点:

  a、TreeView提供2种自定义模式,分别是OwnerDrawText和OwnerDrawAll。

OwnerDrawText:允许我们自定义节点的字体样式

OwnerDrawAll:允许我们自定义节点的所有元素样式,包括字体、背景、图标等

  b、TreeNode有2个区域的概念:分别是【点击测试区域】和【整行区域】(整行区域这个名字自己取的,大家只要知道什么意思就行),来张图更容易理解

可能区域的范围图中不是很精确,但是我们能够大概的知道 【整行区域】 是这个节点这一行所有的面积,而 【点击测试区域】    是这个节点所有表现元素(图标+字体)所占的面积,从名字上我们也大概可以猜到这个【点击测试区域】跟用户鼠标点击的位置和对应的表现行为(单击选中,双击展开等)有关。

  c、在DrawNode事件中,参数 DrawTreeNodeEventArgs.Bounds 我们可以获取到节点的【整行区域】,而 DrawTreeNodeEventArgs.Node.Bounds 我们可以获取到节点的【点击测试区域】

7、我们MSDN网页继续向下看,发现还有一段代码示例如下:

  ps:示例什么的最好了,又直接又快捷,你懂的。而且MSDN里面的代码考虑都能周全,copy下来直接放到新建的程序里稍作调整就可以直接运行看到效果,然后我们可以逐行慢慢分析理解

using System;
using System.Drawing;
using System.Windows.Forms; public class TreeViewOwnerDraw : Form
{
private TreeView myTreeView; // Create a Font object for the node tags.
Font tagFont = new Font("Helvetica", , FontStyle.Bold); public TreeViewOwnerDraw()
{
// Create and initialize the TreeView control.
myTreeView = new TreeView();
myTreeView.Dock = DockStyle.Fill;
myTreeView.BackColor = Color.Tan;
myTreeView.CheckBoxes = true; // Add nodes to the TreeView control.
TreeNode node;
for (int x = ; x < ; ++x)
{
// Add a root node to the TreeView control.
node = myTreeView.Nodes.Add(String.Format("Task {0}", x));
for (int y = ; y < ; ++y)
{
// Add a child node to the root node.
node.Nodes.Add(String.Format("Subtask {0}", y));
}
}
myTreeView.ExpandAll(); // Add tags containing alert messages to a few nodes
// and set the node background color to highlight them.
myTreeView.Nodes[].Nodes[].Tag = "urgent!";
myTreeView.Nodes[].Nodes[].BackColor = Color.Yellow;
myTreeView.SelectedNode = myTreeView.Nodes[].Nodes[];
myTreeView.Nodes[].Nodes[].Tag = "urgent!";
myTreeView.Nodes[].Nodes[].BackColor = Color.Yellow; // Configure the TreeView control for owner-draw and add
// a handler for the DrawNode event.
myTreeView.DrawMode = TreeViewDrawMode.OwnerDrawText;
myTreeView.DrawNode +=
new DrawTreeNodeEventHandler(myTreeView_DrawNode); // Add a handler for the MouseDown event so that a node can be
// selected by clicking the tag text as well as the node text.
myTreeView.MouseDown += new MouseEventHandler(myTreeView_MouseDown); // Initialize the form and add the TreeView control to it.
this.ClientSize = new Size(, );
this.Controls.Add(myTreeView);
} // Clean up any resources being used.
protected override void Dispose(bool disposing)
{
if (disposing)
{
tagFont.Dispose();
}
base.Dispose(disposing);
} [STAThreadAttribute()]
static void Main()
{
Application.Run(new TreeViewOwnerDraw());
} // Draws a node.
private void myTreeView_DrawNode(
object sender, DrawTreeNodeEventArgs e)
{
// Draw the background and node text for a selected node.
if ((e.State & TreeNodeStates.Selected) != )
{
// Draw the background of the selected node. The NodeBounds
// method makes the highlight rectangle large enough to
// include the text of a node tag, if one is present.
e.Graphics.FillRectangle(Brushes.Green, NodeBounds(e.Node)); // Retrieve the node font. If the node font has not been set,
// use the TreeView font.
Font nodeFont = e.Node.NodeFont;
if (nodeFont == null) nodeFont = ((TreeView)sender).Font; // Draw the node text.
e.Graphics.DrawString(e.Node.Text, nodeFont, Brushes.White,
Rectangle.Inflate(e.Bounds, , ));
} // Use the default background and node text.
else
{
e.DrawDefault = true;
} // If a node tag is present, draw its string representation
// to the right of the label text.
if (e.Node.Tag != null)
{
e.Graphics.DrawString(e.Node.Tag.ToString(), tagFont,
Brushes.Yellow, e.Bounds.Right + , e.Bounds.Top);
} // If the node has focus, draw the focus rectangle large, making
// it large enough to include the text of the node tag, if present.
if ((e.State & TreeNodeStates.Focused) != )
{
using (Pen focusPen = new Pen(Color.Black))
{
focusPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
Rectangle focusBounds = NodeBounds(e.Node);
focusBounds.Size = new Size(focusBounds.Width - ,
focusBounds.Height - );
e.Graphics.DrawRectangle(focusPen, focusBounds);
}
}
} // Selects a node that is clicked on its label or tag text.
private void myTreeView_MouseDown(object sender, MouseEventArgs e)
{
TreeNode clickedNode = myTreeView.GetNodeAt(e.X, e.Y);
if (NodeBounds(clickedNode).Contains(e.X, e.Y))
{
myTreeView.SelectedNode = clickedNode;
}
} // Returns the bounds of the specified node, including the region
// occupied by the node label and any node tag displayed.
private Rectangle NodeBounds(TreeNode node)
{
// Set the return value to the normal node bounds.
Rectangle bounds = node.Bounds;
if (node.Tag != null)
{
// Retrieve a Graphics object from the TreeView handle
// and use it to calculate the display width of the tag.
Graphics g = myTreeView.CreateGraphics();
int tagWidth = (int)g.MeasureString
(node.Tag.ToString(), tagFont).Width + ; // Adjust the node bounds using the calculated value.
bounds.Offset(tagWidth/, );
bounds = Rectangle.Inflate(bounds, tagWidth/, );
g.Dispose();
} return bounds; } }

8、这段代码里我发现了几个实用的:

  1、TreeView 的 GetNodeAt 方法支持根据鼠标点击坐标获取对应节点:

    TreeNode clickedNode = myTreeView.GetNodeAt(e.X, e.Y);

  2、TreeView 的 CreateGraphics 方法可以创建当前实例的 Graphics 对象:

    Graphics g = myTreeView.CreateGraphics();

    g.Dispose();

  3、DrawNode事件中的节点状态判断写法:

    例如判断当前节点为选中状态   if ((e.State & TreeNodeStates.Selected) != 0)

  4、描绘文字的方法:

    e.Graphics.DrawString(e.Node.Text, nodeFont, Brushes.White,Rectangle.Inflate(e.Bounds, 2, 0));

  5、描绘背景的方法:

    e.Graphics.DrawRectangle(focusPen, focusBounds);

9、好的,准备工作差不多了,开始我们的皮肤美化之旅吧!

项目创建

步骤一:新建Winfrom项目,并新建一个自定义控件

步骤二:修改MyTreeView类继承TreeView,注意这里需要添加引用 using System.Windows.Forms;

步骤三:根据自己需要修改样式,下面是我的代码:

public partial class BaseTreeView : TreeView
{ Color drawTextColor = Color.FromArgb(, , ); public BaseTreeView()
{
InitializeComponent(); this.DrawMode = TreeViewDrawMode.OwnerDrawAll;
this.FullRowSelect = true;
this.ItemHeight = ;
this.HotTracking = true;
this.ShowLines = true; } protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
base.OnDrawNode(e); //节点背景绘制
if (e.Node.IsSelected)
{
e.Graphics.DrawImage(Resources.tree_Selected, e.Bounds);
}
else if ((e.State & TreeNodeStates.Hot) != )//|| currentMouseMoveNode == e.Node)
{
e.Graphics.DrawImage(Resources.tree_Hover, e.Bounds);
}
else
{
e.Graphics.FillRectangle(Brushes.White, e.Bounds);
} //节点头图标绘制
if (e.Node.IsExpanded)
{
e.Graphics.DrawImage(Resources.tree_NodeExpend, e.Node.Bounds.X - , e.Node.Bounds.Y + );
}
else if (e.Node.IsExpanded == false && e.Node.Nodes.Count > )
{
e.Graphics.DrawImage(Resources.tree_NodeCollaps, e.Node.Bounds.X - , e.Node.Bounds.Y + );
} //文本绘制
using (Font foreFont = new Font(this.Font, FontStyle.Regular))
using (Brush drawTextBrush = new SolidBrush(drawTextColor))
{
e.Graphics.DrawString(e.Node.Text, foreFont, drawTextBrush, e.Node.Bounds.Left + , e.Node.Bounds.Top + );
}
} protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
TreeNode tn = this.GetNodeAt(e.Location);
//调整【点击测试区域】大小,包括图标
Rectangle bounds = new Rectangle(tn.Bounds.Left - , tn.Bounds.Y, tn.Bounds.Width - , tn.Bounds.Height);
if (tn != null && bounds.Contains(e.Location) == false)
{
if (tn.IsExpanded == false)
tn.Expand();
else
tn.Collapse();
}
} protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
TreeNode tn = this.GetNodeAt(e.Location);
this.SelectedNode = tn;
} TreeNode currentNode = null;
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
TreeNode tn = this.GetNodeAt(e.Location);
Graphics g = this.CreateGraphics();
if (currentNode != tn)
{
//绘制当前节点的hover背景
if (tn != null)
OnDrawNode(new DrawTreeNodeEventArgs(g, tn, new Rectangle(, tn.Bounds.Y, this.Width, tn.Bounds.Height), TreeNodeStates.Hot)); //取消之前hover的节点背景
if (currentNode != null)
OnDrawNode(new DrawTreeNodeEventArgs(g, currentNode, new Rectangle(, currentNode.Bounds.Y, this.Width, currentNode.Bounds.Height), TreeNodeStates.Default));
}
currentNode = tn;
g.Dispose();
} protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
//移出控件时取消Hover背景
if (currentNode != null)
{
Graphics g = this.CreateGraphics();
OnDrawNode(new DrawTreeNodeEventArgs(g, currentNode, new Rectangle(, currentNode.Bounds.Y, this.Width, currentNode.Bounds.Height), TreeNodeStates.Default));
}
}
}

    效果如下图:深蓝色为节点选中时的效果,淡蓝色为鼠标停留在节点上的效果

    

说明1:因为默认的TreeView点击非【点击测试区域】时是不触发动作效果的【单击节点选中效果、双击节点展开等效果】

原本想尝试修改节点的【点击测试区域】扩大到【整行区域】,后来没有找到方法,所以只能变通的实现这个效果,因此在我的代码里在其它事件里也进行了处理,不过还好我要的效果是实现了

说明2:demo下载

来吧,给你的Winform列表控件画个妆的更多相关文章

  1. .NET各大平台数据列表控件绑定原理及比较(WebForm、Winform、WPF)

    说说WebForm: 数据列表控件: WebForm 下的列表绑定控件基本就是GridView.DataList.Repeater:当然还有其它DropDownList.ListBox等. 它们的共同 ...

  2. winform窗体控件(全)

    回顾跟补充下除了昨天那常用6个其他的winform窗体控件作用 1:Button:按钮 (1)AutoSize:如果是True的情况下,内容将会撑开:False的话会另起一行 (2)Enabled: ...

  3. DevExpress Winform 常用控件

    Ø  前言 DevExpress 控件的功能比较强大,是全球知名控件开发公司,对于开发 B/S 或 C/S 都非常出色,可以实现很炫且功能强大的效果. DevExpress Winform 常用控件是 ...

  4. UWP开发必备:常用数据列表控件汇总比较

    今天是想通过实例将UWP开发常用的数据列表做汇总比较,作为以后项目开发参考.UWP开发必备知识点总结请参照[UWP开发必备以及常用知识点总结]. 本次主要讨论以下控件: GridView:用于显示数据 ...

  5. 在DevExpress程序中使用Winform分页控件直接录入数据并保存

    一般情况下,我们都倾向于使用一个组织比较好的独立界面来录入或者展示相关的数据,这样处理比较规范,也方便显示比较复杂的数据.不过在一些情况下,我们也可能需要直接在GridView表格上直接录入或者修改数 ...

  6. WPF自定义控件与样式(7)-列表控件DataGrid与ListView自定义样式

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: Dat ...

  7. C#实现WinForm DataGridView控件支持叠加数据绑定

    我们都知道WinForm DataGridView控件支持数据绑定,使用方法很简单,只需将DataSource属性指定到相应的数据源即可,但需注意数据源必须支持IListSource类型,这里说的是支 ...

  8. winform基本控件----按钮

    这次来引用一个我们上课时候老师给的一个实验内容,来说一下winform程序设计中的按钮控件的使用.下面是我们老师给的实验内容. 实验目的: 掌握Winform的开发环境. 掌握窗体的创建和基本方法. ...

  9. WinForm给控件加入hint文字

    本文代码主要是参考别人的,仅为个人记录,方面后续使用~ 效果图: 主要代码在一个Win32Utility类中,代码如下: public static class Win32Utility { [Dll ...

随机推荐

  1. DWZ (JUI) 教程 根据ID刷新 dialog

    reloadDialog:function(dialogId){ var dialog = $("body").data(dialogId); if(dialog){ $.pdia ...

  2. 网站seo新手快速提升自己的技巧

    第一.找自身的问题 大多数从业者都有下面两个严重的问题: 1.过于放大SEO的重要性每个人,都有自大的习惯,地位越NB往往越把自己认知的一切当做真理,其实有可能那只是井口那巴掌大的一片天.在网络营销中 ...

  3. JAVA 模糊查询方法

    当我们需要开发一个方法用来查询数据库的时候,往往会遇到这样一个问题:就是不知道用户到底会输入什么条件,那么怎么样处理sql语句才能让我们开发的方法不管接受到什么样的条件都可以正常工作呢?这时where ...

  4. Hadoop集群错误

    1.Hadoop集群所有的DataNode都启动不了解决办法 删除从节点.../usr/hadoop/tmp/dfs/ 下内容,再重新格式化namenode

  5. 8款耀眼的jQuery/HTML5焦点图滑块插件

    1.HTML5/CSS3超酷焦点图特效 带前后翻页按钮 今天要分享的这款HTML5/CSS3焦点图插件切换效果比较简单,但是外观和功能却十分强大.该CSS3焦点图在切换图片时,图片以淡入淡出的方式缩小 ...

  6. java基础-注解Annotation原理和用法

    在很多java代码中都可以看到诸如@Override.@Deprecated.@SuppressWarnings这样的字符,这些就是注解Annotation.注解最早在jdk5中被引入,现在已经成为j ...

  7. 匹配一级分类和二级分类 名字和url 里面有玄机

    1:要匹配的html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http:// ...

  8. 腾讯微博OAuth2.0 .NET4.0 SDK 发布以及网站腾讯微博登陆示例代码(原创)

    1.使用简单方便,包含详细注释: 2.暂时只支持xml格式字符串的转换,建议接口使用xml参数:3.QweiboSDK.Controllers命名空间下已包含所有API接口:4.只需调用到Qweibo ...

  9. PHP 超级全局变量

    超级全局变量在PHP 4.1.0之后被启用, 是PHP系统中自带的变量,在一个脚本的全部作用域中都可用. PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们在一个脚本的全部作 ...

  10. rsync参数详解、利用ssh、rsync 实现数据的定时同步

    rsync 简介 rsync(remote synchronize)是一个远程数据同步工具,可通过 LAN/WAN 快速同步多台主机之间的文 件.也可以使用 rsync 同步本