NFace的简单实现
如果您熟悉JFace,那么你最大的感触是什么?优良的设计框架?精简易用的API?简单方便的数据绑定?
以前做基于Eclipse RCP开发的时候,我被JFace的易用和思想吸引,感觉很优雅。JFace中的UI组件十分丰富,功能很多,今天想谈谈如何在.NET中实现其中的一两个组件,也算是我技术BLOG开篇之作。对于.NET中TreeView的DataBinding,我很不喜欢,于是想到模仿JFace TreeViewer的接口写一个.NET版本的NFace TreeViewer,另外将JFace TableViewer实现为NFace ListViewer。但这里只说说TreeViewer的实现,ListViewer的思想是相同的。
一、首先看一下这两个组件的类图,懒得用建模画UML了,下图用VS2005自带的ClassDiagram工具生成:
1.首先是CTreeViewer的类图
1.1 CTreeNode继承于TreeNode类
成员
|
说明
|
Data
|
用于存放业务数据(通常是实体类的实例),这样,可以方便的在用户点击树上节点时,取得相应业务数据。
|
HasGotChildren
|
是否已经取得过子节点。
|
IsBlankNode
|
是否时空节点,空节点是看不到的节点,仅仅用于生成节点上的展开加号,这样,当某个节点未展开时,其下有一个空节点,那么就会形成加号。
|
NeedRefresh
|
节点是否需要刷新。
|
CTreeNode(bool isBlankNode)
|
用是否空节点构造一个节点
|
CTreeNode(object obj)
|
用业务数据构造一个节点
|
CTreeNode(string text, object obj)
|
用节点显示文字和业务数据构造一个节点
|
1.2 ITreeViewLabelProvider,CTreeViewer的显示提供器
成员
|
说明
|
GetImageIndex(object obj)
|
获取树节点文字前的图像索引号
|
GetLabel(object obj)
|
获取树节点显示的字符串
|
GetSelectedImageIndex(object obj)
|
获取树节点被选中时文字前的图像索引号
|
1.3 ITreeViewContentProvider,CTreeViewer的内容提供器
成员
|
说明
|
GetElements()
|
获根节点对象(可以有多个根节点)
|
GetElements(object obj)
|
获取obj节点下的子节点
|
HasChildren(object obj)
|
判断obj节点下是否有子节点
|
1.4 CTreeViewer类,继承于TreeView
成员
|
说明
|
ContentProvider
|
CTreeViewer的内容提供器
|
LabelProvider
|
CTreeViewer的显示提供器
|
GetChildrenWhenExpand
|
是否在展开树节点是获取其子节点,该属性影响性能,设置为True时,节点展开时只获取当前层的数据;设置为False时,每次展开一个节点不光获取当前层的数据,也同时获取当前层所有节点的子节点的数据(也就是每次展开一个节点获取两层节点数据)
|
ExpandToLevel(int)
|
展开树到指定层
|
ExpandToLevelByThread(int)
|
展开树到指定层(线程中完成)
|
RefreshOnlyOneNode(CTreeNode)
|
刷新某个节点
|
OnAfterExpand(TreeViewEventArgs)
|
重载TreeView的展开后方法,完成获取子节点,构造树的工作
|
ShowTree()
|
根据给定的内容提供器和显示提供器,构造显示树内容
|
2.然后是NFace ListViewer的类图:
对于CListViewer的类图不再作详细解释,类似CTreeViewer。
二、思想
实现IContentProvider和ILabelProvider后,CTreeViewer根据实现该接口的类实例来自动构造一颗树,ContentProvider提供树节点数据(后台业务数据)以及数据之间的关系(父子节点关系),LabelProvider定义如何显示树节点(节点的图标、文字内容)。
三、用法
这里举一个简单的例子,将地区信息显示成一颗树。该例中数据从XML读取:
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<District>
<ID>0</ID>
<Name>中国</Name>
<ParentID>-1</ParentID>
<IsLeave>false</IsLeave>
</District>
<District>
<ID>1</ID>
<Name>美国</Name>
<ParentID>-1</ParentID>
<IsLeave>false</IsLeave>
</District>
<District>
<ID>2</ID>
<Name>日本</Name>
<ParentID>-1</ParentID>
<IsLeave>true</IsLeave>
</District>
<District>
<ID>3</ID>
<Name>韩国</Name>
<ParentID>-1</ParentID>
<IsLeave>true</IsLeave>
</District>
<District>
<ID>4</ID>
<Name>北京</Name>
<ParentID>0</ParentID>
<IsLeave>true</IsLeave>
</District>
<District>
<ID>5</ID>
<Name>上海</Name>
<ParentID>0</ParentID>
<IsLeave>false</IsLeave>
</District>
<District>
<ID>6</ID>
<Name>杭州</Name>
<ParentID>0</ParentID>
<IsLeave>true</IsLeave>
</District>
<District>
<ID>7</ID>
<Name>浦东新区</Name>
<ParentID>5</ParentID>
<IsLeave>false</IsLeave>
</District>
<District>
<ID>8</ID>
<Name>徐汇区</Name>
<ParentID>5</ParentID>
<IsLeave>true</IsLeave>
</District>
<District>
<ID>9</ID>
<Name>杨浦区</Name>
<ParentID>5</ParentID>
<IsLeave>true</IsLeave>
</District>
<District>
<ID>10</ID>
<Name>合庆镇</Name>
<ParentID>7</ParentID>
<IsLeave>true</IsLeave>
</District>
<District>
<ID>11</ID>
<Name>张江镇</Name>
<ParentID>7</ParentID>
<IsLeave>true</IsLeave>
</District>
<District>
<ID>12</ID>
<Name>川沙镇</Name>
<ParentID>7</ParentID>
<IsLeave>true</IsLeave>
</District>
<District>
<ID>13</ID>
<Name>加州</Name>
<ParentID>1</ParentID>
<IsLeave>true</IsLeave>
</District>
</NewDataSet>
简单说明一下:ID是某个地区的ID号,Name是地区名字,ParentID是上级地区的ID号,IsLeave表示是否是叶子地区(其下没有子地区)。
3.1 新建一个WinForm程序,引入IZero.NFace.dll,在窗口上拖放一个CTreeViewer组件,取名为ctv。
3.2 实现一个ContentProvider
class ContentProvider : IZero.NFace.CTreeView.ITreeViewContentProvider
{
private DataSet ds;

public ContentProvider(DataSet ds)
{
this.ds =( ds == null ? new DataSet() : ds); //这里数据集从构造函数传入
}

ITreeViewContentProvider 成员
}
3.3 实现一个LabelProvider
class LabelProvider : IZero.NFace.CTreeView.ITreeViewLabelProvider
{
ITreeViewLabelProvider 成员
}
3.4 在FormLoad事件里,加载XML文件到一个DataSet,以便待会儿传入ContentProvider,并且对CTreeViewer的内容提供器和显示提供器设置提供器。
private void FrmTest_Load(object sender, EventArgs e)
{
DataSet ds = new DataSet();
try
{
ds.ReadXml("TestData.xml");
this.ctv.ContentProvider = new ContentProvider(ds);
this.ctv.LabelProvider = new LabelProvider();
this.ctv.GetChildrenWhenExpand = true; //Default is true
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
}

3.5 在窗口上放一个显示按钮,在其单击事件中,调用CTreeViewer的ShowTree()方法即可。
四、效果图

今天先写到这里,关于CListViewer和CTreeViewer的使用,将会在后续篇幅中用到(可能是一个简单的数据库浏览器)。由于第一次写技术BLOG,经验不足,敬请谅解。
NFace源码和示例程序 下载地址: 目前似乎还不能上传除图片外的资源,不清楚是否支持。如果您想要源码和示例程序,请留下email,我会通过邮件发送。























































































简单说明一下:ID是某个地区的ID号,Name是地区名字,ParentID是上级地区的ID号,IsLeave表示是否是叶子地区(其下没有子地区)。
3.1 新建一个WinForm程序,引入IZero.NFace.dll,在窗口上拖放一个CTreeViewer组件,取名为ctv。
3.2 实现一个ContentProvider











3.3 实现一个LabelProvider





3.4 在FormLoad事件里,加载XML文件到一个DataSet,以便待会儿传入ContentProvider,并且对CTreeViewer的内容提供器和显示提供器设置提供器。

















3.5 在窗口上放一个显示按钮,在其单击事件中,调用CTreeViewer的ShowTree()方法即可。
四、效果图
今天先写到这里,关于CListViewer和CTreeViewer的使用,将会在后续篇幅中用到(可能是一个简单的数据库浏览器)。由于第一次写技术BLOG,经验不足,敬请谅解。
NFace源码和示例程序 下载地址: 目前似乎还不能上传除图片外的资源,不清楚是否支持。如果您想要源码和示例程序,请留下email,我会通过邮件发送。