在一些字典绑定中,往往为了方便展示详细数据,需要把一些结构树展现在树列表TreeList控件中或者下拉列表的树形控件TreeListLookUpEdit控件中,为了快速的处理数据的绑定操作,比较每次使用涉及太多细节的操作,我们可以把相关的数据绑定操作,放在一些辅助类的扩展函数中进行处理,这样可以更方便的,更简洁的处理数据绑定操作,本篇随笔介绍TreeList控件和TreeListLookUpEdit控件在扩展函数中的处理操作。
TreeList本身就是一个树形数据的展示控件,可以展示常规的二维表,也可以展示具有嵌套关系的二维表,数据源可以是多种方式的,支持Datable的数据源的嵌套展示。
单个列信息的树形列表展示界面效果:

类似GridView的嵌套列表展示的TreeList界面效果

这些界面都比较常见,也是我们经常碰到的处理效果,但是TreeList的界面设置有很多特性,如果每次拷贝这些代码,需要很多,也不便于维护,因此我们建立一些扩展函数来处理界面元素的绑定,就非常必要。

本篇随笔介绍基于TreeList和TreeListLookUpEdit控件的绑定即可。两个类型控件的数据源,都可以是DataTable类型,也可以是IList集合类型,如下所示是基于SQLSugar开发框架,返回的数据结构是IList类型的。

所以一般树形列表的绑定操作,提供一个方法来获取数据并绑定即可。
/// <summary>
/// 绑定树的数据源
/// </summary>
private async void BindTree()
{
var list = await BLLFactory<IDictTypeService>.Instance.GetAllAsync();
this.tree.DataSource = list?.Items;
this.tree.ExpandAll();
}
如果使用原生代码初始化树列表,那么代码如下所示。
//使用原生代码处理
//添加显示列
this.tree.Columns.Add(new TreeListColumn{ FieldName= "Id", Caption= "ID"});//增加一个隐藏的字段,存储需要的ID
this.tree.Columns.Add(new TreeListColumn{ FieldName= "Name", Caption= "字典类型名称", Width=160, VisibleIndex =0});
//设置树控件的层次关系及属性
tree.KeyFieldName = "Id";
tree.ParentFieldName = "PID";
this.tree.OptionsBehavior.Editable = false;
this.tree.OptionsView.ShowColumns = false;
this.tree.OptionsView.ShowCheckBoxes = false;
this.tree.OptionsView.EnableAppearanceOddRow = true;
this.tree.OptionsView.EnableAppearanceEvenRow = true;
而实现查询过滤的操作,还需要另外处理代码,我们看看大概的代码如下。
/// <summary>
/// 实现树节点的过滤查询
/// </summary>
private void InitSearchControl()
{
this.searchControl1.Client = this.tree;
this.tree.FilterNode += (object sender, FilterNodeEventArgs e) =>
{
if (tree.DataSource == null)
return;
string nodeText = e.Node.GetDisplayText("Name");//参数填写FieldName
if (string.IsNullOrWhiteSpace(nodeText))
return;
bool isExist = nodeText.IndexOf(searchControl1.Text, StringComparison.OrdinalIgnoreCase) >= 0;
if (isExist)
{
var node = e.Node.ParentNode;
while (node != null)
{
if (!node.Visible)
{
node.Visible = true;
node = node.ParentNode;
}
else
break;
}
}
e.Node.Visible = isExist;
e.Handled = true;
};
}
这些是比较常见的操作,我们把它封装为扩展函数,然后根据特性传入对应参数实现即可。
最后简单的三行代码来简单处理,可以达到同样的效果就可以了。
//控件扩展函数封装处理
this.tree.CreateColumn("Name", "字典类型名称", 160, true);
this.tree.InitTree("Id", "PID", "-1", false, false);
this.tree.InitSearchControl(this.searchControl1, "Name");
我们扩展方法放在一个单独的文件中,标注为静态类即可。
/// <summary>
/// TreeList控件的扩展函数
/// </summary>
public static class TreeList_Extension
其中提供几个对TreeList 常见的封装处理方法就可以了。

对于一些不常见的属性,我们保留它即可,如下界面代码是对TreeList绑定展示多个列的处理操作。
/// <summary>
/// 初始化TreeList控件,展现嵌套的列表。
/// </summary>
private void InitControl()
{
this.tree.Columns.Clear();//控件扩展函数封装处理
this.tree.CreateColumn("Name", "机构名称", 160, true);
this.tree.CreateColumn("HandNo", "机构编码", 80, true);
this.tree.CreateColumn("Category", "机构分类", 80, true);
this.tree.CreateColumn("Address", "机构地址", 160, true);
this.tree.CreateColumn("InnerPhone", "内线电话", 80, true);
this.tree.CreateColumn("OuterPhone", "外线电话", 80, true);
this.tree.CreateColumn("SortCode", "排序码", 80, true);
this.tree.InitTree("Id", "PID", null, true, true);
this.tree.OptionsView.RowImagesShowMode = RowImagesShowMode.InCell;//紧凑型图标
this.tree.ExpandAll();
// 列过滤处理
this.tree.OptionsView.ShowAutoFilterRow = true;//显示过滤行
this.tree.OptionsBehavior.EnableFiltering = true;//开启过滤功能
//初始化树节点选择事件
this.tree.FocusedNodeChanged += delegate (object sender, FocusedNodeChangedEventArgs e)
{
this.FocusedNodeChanged();
};
//树节点双击处理事件
this.tree.DoubleClick += (s, e) =>
{
if (this.tree.FocusedNode != null)
{
string ID = string.Concat(this.tree.FocusedNode.GetValue("Id"));
MessageDxUtil.ShowTips("Id=" + ID);
}
};
//编辑记录失去焦点后校验处理
this.tree.ValidateNode += (s, e) =>
{
Console.WriteLine(this.tree.FocusedNode.GetValue("Name"));
};
}
实现类似GridView的嵌套列表展示的TreeList界面效果如下所示。

在一些参考的列表中,我们往往需要展示更丰富一点的列表内容,如下所示。


如果有嵌套列表的,展示嵌套列表的处理

对于下拉的树形列表,虽然这个控件比TreeList更复杂一些,它是下拉列表和TreeList的整合体,不过我们也可以用类似的扩展函数方法,来简单的实现数据的绑定展示。
如对于常规的数据绑定,我们大概的代码如下所示。
//TreeListLookupEdit数据绑定
//this.txtProjectList3.Properties.TreeList.OptionsView.ShowCheckBoxes = true;
this.txtProjectList3.Properties.DataSource = list;
this.txtProjectList3.Properties.ValueMember = "Value";
this.txtProjectList3.Properties.DisplayMember = "Text";
this.txtProjectList3.Properties.TreeList.Columns.Clear();
for (int i = 0; i < columns.Count; i++)
{
this.txtProjectList3.Properties.TreeList.CreateColumn(columns[i].FieldName, columns[i].Caption,
columns[i].Width, true);
}
this.txtProjectList3.Properties.TreeList.InitTree(null, null, null, true, true);
this.txtProjectList3.Properties.ImmediatePopup = true;
this.txtProjectList3.Properties.TextEditStyle = TextEditStyles.Standard;
this.txtProjectList3.Properties.PopupWidthMode = DevExpress.XtraEditors.PopupWidthMode.ContentWidth;
this.txtProjectList3.Properties.PopupFormSize = new System.Drawing.Size(this.txtProjectList3.Width, 300);
this.txtProjectList3.Properties.TreeList.IndicatorWidth = 40;
this.txtProjectList3.Properties.TreeList.CustomDrawNodeIndicator += (s, ee) =>
{
if (ee.IsNodeIndicator)
{
var index = ee.Node.TreeList.GetVisibleIndexByNode(ee.Node);
ee.Info.DisplayText = (index + 1).ToString();
}
};
对于常规的列表绑定,我们可以用简单的一个扩展函数实现,如下所示。
//常规类别绑定
this.txtProjectList4.BindDictItems(list, "Text", "Value", true, columns.ToArray());
就可以实现常规的界面效果处理。

对于树形列表,我们需要设置属性的ID和PID,以及一些显示的列属性,那么也可以增加更多的参数来实现。
var dictTypeColumns = new List<LookUpColumnInfo>()
{
new LookUpColumnInfo("Id", "Id"),
new LookUpColumnInfo("Name", "字典类别名称")
};
treeListLookUp.BindDictItems(result.Items, "Name", "Id", true, false, "Id", "PID", null, true, true, true, false, dictTypeColumns.ToArray());
因此嵌套列表就可以正常的展示出层次关系了

因此我们把扩展方法,放到静态类里面就可以了,方法封装如下所示
/// <summary>
/// 绑定TreeListLookUpEdit控件的数据源(完整版)
/// </summary>
/// <param name="lookup">控件对象</param>
/// <param name="dataSource">数据源</param>
/// <param name="displayMember">显示字段</param>
/// <param name="valueMember">值字段</param>
/// <param name="showRowIndicator">是否显示序号</param>
/// <param name="showCheckbox">是否显示复选框</param>
/// <param name="keyFieldName">设置父子递归关系字段-子字段,不指定则使用valueMember</param>
/// <param name="parentFieldName">设置父子递归关系字段-父字段,不指定则不嵌套展示</param>
/// <param name="rootValue">根节点的值</param>
/// <param name="editable">树节点是否可以编辑</param>
/// <param name="showColumnHeader">是否显示列头</param>
/// <param name="oddEvenRowColor">是否奇偶行不同颜色</param>
/// <param name="allowDrop">是否运行拖动列</param>
/// <param name="lookUpColumnInfos">显示的列</param>
/// <returns></returns>
public static object BindDictItems(this TreeListLookUpEdit lookup, object dataSource, string displayMember, string valueMember, bool showRowIndicator = true,
bool showCheckbox = false, string keyFieldName = null, string parentFieldName = null, string rootValue = null, bool editable = true,
bool showColumnHeader = false, bool oddEvenRowColor = true, bool allowDrop = false,
params LookUpColumnInfo[] lookUpColumnInfos)
{
lookup.Properties.DataSource = dataSource;
lookup.Properties.DisplayMember = displayMember;
lookup.Properties.ValueMember = valueMember;
lookup.Properties.TreeList.OptionsView.ShowCheckBoxes = showCheckbox;
lookup.Properties.TreeList.Columns.Clear();
for (int i = 0; i < lookUpColumnInfos.Length; i++)
{
lookup.Properties.TreeList.CreateColumn(lookUpColumnInfos[i].FieldName, lookUpColumnInfos[i].Caption,
lookUpColumnInfos[i].Width, true);
}
//初始化树的样式和特性
//keyFieldName = !string.IsNullOrWhiteSpace(keyFieldName) ? keyFieldName : valueMember;//如果不指定,采用valueMember
lookup.Properties.TreeList.InitTree(keyFieldName, parentFieldName, rootValue, editable, showColumnHeader, oddEvenRowColor, allowDrop);
lookup.Properties.PopupFormSize = new System.Drawing.Size(lookup.Width, 300);
lookup.Properties.ImmediatePopup = true;
lookup.Properties.TextEditStyle = TextEditStyles.Standard;
if (showRowIndicator)
{
lookup.Properties.TreeList.IndicatorWidth = 40;
//重写序号显示,默认不显示数值
lookup.Properties.TreeList.CustomDrawNodeIndicator += (s, ee) =>
{
if (ee.IsNodeIndicator)
{
var index = ee.Node.TreeList.GetVisibleIndexByNode(ee.Node);
ee.Info.DisplayText = (index + 1).ToString();
}
};
}
return dataSource;
}
通过扩展方法的方式,可以简化界面的处理代码,同时利于我们在项目开发的时候,快速的实现相关的效果,而不需要过多的中断查找相关的界面控件属性。
有几篇类似的文章,可供参考:
《在Winform开发中,我们使用的几种下拉列表展示字典数据的方式》
《在Winform开发框架中下拉列表绑定字典以及使用缓存提高界面显示速度》
《在各种开发项目中使用公用类库的扩展方法,通过上下文方式快速调用处理函数》
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po