作為一個C#程序員,在寫程序時一直以來都使用的微軟那一套控件,用起來特別爽,可是最近公司的一個項目用到了DevExpress框架,不用不知道,一用嚇一跳,不得不承認這個框架確實很強大,效果也很炫,但是用這個框架也遇到了很多讓我頭疼的問題,由於該控件可能使用的人少,在網上能查到的解決辦法實在有限,所以在解決問題時效率低得讓我有些接受不了,所以再此把我遇到的一些問題和解決思路以及解決結果給大家分享一下,該篇博文只針對DevExpress.XtraTreeList.TreeList 樹形控件來說以下簡稱(Dev樹形),let's do it\(≧▽≦)/:
首先羅列出所需功能:
1、樹形的綁定(給數據源對樹形進行綁定)
2、樹形節點的查找(根據用戶輸入的名字進行模糊查詢節點數據:重點在於根據子節點反向遞歸父節點)
3、樹形的右鍵選中節點(在節點上點擊右鍵時就選中節點)
4、根據ID選中節點(該功能着實讓我頭疼了好久)
依次展示出這幾個功能的效果圖:




樹形的綁定:
由於 Dev樹形 在樹形綁定時需要特定格式,那么我們先來展示出 Dev樹形 所要使用到的實體類代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BW.PropertyModel.Class
{
public class TreeListModel
{
#region 字段屬性
private int id;
/// <summary>
/// 節點編號
/// </summary>
public int ID
{
get { return id; }
set { id = value; }
}
private int parentID;
/// <summary>
/// 父節點編號
/// </summary>
/// <value>The parent ID.</value>
public int ParentID
{
get { return parentID; }
set { parentID = value; }
}
private string menuName;
/// <summary>
/// 菜單名稱
/// </summary>
public string MenuName
{
get { return menuName; }
set { menuName = value; }
}
private object tag;
/// <summary>
/// 用於裝節點對象
/// </summary>
public object Tag
{
get { return tag; }
set { tag = value; }
}
#endregion
}
}
解釋一下這四個字段的用途:ID 是獨一無二的,在樹形綁定時可以使用數據庫中你要綁定的那個對象的ID;ParentID用於 Dev樹形 識別節點所屬關系(也就是該節點屬哪個節點);MenuName 用於 Dev樹形 節點的顯示名; Tag用於裝你所需要的任意對象
接下來再附圖給大家展示給 Dev樹形 綁定這幾個字段,
第二張圖是接着第一張未展示完的地方,這8個地方我就不一一解釋了,自己下來仔細看看,他們的含義一目了然的,這里需要注意的一點是:visible這個屬性;接下來說一說數據源的事:數據源要根據數據庫中的數據進行遞歸處理,附上遞歸處理和初始化樹形的代碼:
/// <summary>
/// 加載設備樹形
/// </summary>
public void InitFacilityTree()
{
List<TreeListModel> list = new List<TreeListModel>();
int systemId = 0;
if (this.cmbSystemClassify.EditValue != null)
{
int.TryParse(this.cmbSystemClassify.EditValue.ToString(), out systemId);
}
List<BW.DMSystem.Core.Entity.ModelManageInfo> temp = BLLFactory<BW.DMSystem.Core.BLL.ModelManage>.Instance.GetAll(string.Format("where IsGallery = 0 and SystemID = {0} order by ID asc", systemId)); //查詢數據庫中某個系統的數據
TreeList(0, ref list, temp); //這里傳的0表示 根節點
this.tvFacilityModel.KeyFieldName = "ID"; //這里綁定的ID的值必須是獨一無二的
this.tvFacilityModel.ParentFieldName = "ParentID"; //表示使用parentID進行樹形綁定
this.tvFacilityModel.DataSource = list; //綁定數據源
this.tvFacilityModel.ExpandAll(); //默認展開所有節點
}
/// <summary>
/// 遞歸調用——正向生成樹形所需結構
/// </summary>
/// <param name="parentId">根節點的父編碼默認為0</param>
/// <param name="listData">樹形綁定的數據集合</param>
/// <param name="modelList">某系統的結構數據</param>
private void TreeList(int parentId, ref List<TreeListModel> listData, List<BW.DMSystem.Core.Entity.ModelManageInfo> modelList)
{
List<BW.DMSystem.Core.Entity.ModelManageInfo> tmp = modelList.Where(model => model.ParentCode == parentId).ToList(); //這句話很重要,對數據進行篩選全靠它
foreach (var item in tmp)
{
TreeListModel temp = new TreeListModel();
temp.ID = item.ID;
temp.MenuName = item.Name;
temp.ParentID = parentId;
temp.Tag = item;
listData.Add(temp);
TreeList(item.ID, ref listData, modelList);
}
}
我這里使用的樹形是根據系統分了類的,你如果不需要可以不管 根據注釋應該能猜出個大概 樹形的綁定就算告一段落了
樹形節點的查找:
效果圖是最開始的第二張圖
代碼:
/// <summary>
/// 搜索事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSearch_Click(object sender, EventArgs e)
{
List<TreeListModel> list = new List<TreeListModel>();
List<int> listId = new List<int>(); //這個集合用於存儲已經加入了的父節點ID,因為有些子節點可能同在一個父節點下若重復插入同ID的數據會報錯
string inputStr = this.txtModelName.Text.Trim();
string systemId = string.Empty;
if (this.cmbSystemClassify.EditValue != null)
{
systemId = this.cmbSystemClassify.EditValue.ToString();
}
else
{
systemId = "0";
}
if (string.IsNullOrEmpty(inputStr))
{
//如果為空則查詢所有
List<BW.DMSystem.Core.Entity.ModelManageInfo> temp = BLLFactory<BW.DMSystem.Core.BLL.ModelManage>.Instance.GetAll(string.Format("where IsGallery = 0 and SystemID = '{0}' order by ID asc", systemId));
TreeList(0, ref list, temp);
}
else
{
//查詢出所有數據
List<BW.DMSystem.Core.Entity.ModelManageInfo> allData = BLLFactory<BW.DMSystem.Core.BLL.ModelManage>.Instance.GetAll(string.Format("where IsGallery = 0 and SystemID = '{0}' order by ID asc", systemId));
//查詢出滿足條件的
List<BW.DMSystem.Core.Entity.ModelManageInfo> temp = BLLFactory<BW.DMSystem.Core.BLL.ModelManage>.Instance.GetAll(string.Format("where IsGallery = 0 and IsModel = 1 and SystemID = '{0}' and Name like '%{1}%' order by ID asc", systemId, inputStr));
for (int i = 0; i < temp.Count; i++)
{
TreeListModel currNode = new TreeListModel();
currNode.ID = temp[i].ID;
currNode.MenuName = temp[i].Name;
currNode.ParentID = temp[i].ParentCode;
currNode.Tag = temp[i];
list.Add(currNode);
listId.Add(temp[i].ID);
TreeList_fanxiang(temp[i].ParentCode, ref list, ref listId, allData);
}
}
this.tvFacilityModel.KeyFieldName = "ID";
this.tvFacilityModel.ParentFieldName = "ParentID";
this.tvFacilityModel.DataSource = list;
this.tvFacilityModel.ExpandAll();
}
}
/// <summary>
/// 遞歸調用——反向查找節點的根節點
/// </summary>
/// <param name="currId">根節點的父編碼默認為0</param>
/// <param name="listData">樹形綁定的數據集合</param>
/// <param name="modelList">某系統的結構數據</param>
private void TreeList_fanxiang(int parentId, ref List<TreeListModel> listData, ref List<int> listId, List<BW.DMSystem.Core.Entity.ModelManageInfo> modelList)
{
List<BW.DMSystem.Core.Entity.ModelManageInfo> tmp = modelList.Where(model => model.ID == parentId).ToList(); //正向和反向的最終區別就在於這句話
foreach (var item in tmp)
{
TreeListModel temp = new TreeListModel();
temp.ID = item.ID;
temp.MenuName = item.Name;
temp.ParentID = item.ParentCode;
temp.Tag = item;
if (!listId.Contains(item.ID))
{
listData.Add(temp);
listId.Add(item.ID);
}
TreeList_fanxiang(item.ParentCode, ref listData, ref listId, modelList);
}
}
根據注釋理解代碼的含義,節點查找也告一段落了。
樹形節點的右鍵選中:
在節點上鼠標右鍵點擊時默認是不會選中該節點的,這個功能說簡單不簡單說復雜也不復雜,我就直接附上代碼來
/// <summary>
/// 鼠標右鍵事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tvFacilityModel_MouseUp(object sender, MouseEventArgs e) 需要注意的是 這個事件不要搞錯了
{
DevExpress.XtraTreeList.TreeList tree = sender as DevExpress.XtraTreeList.TreeList;
if ((e.Button == MouseButtons.Right) && (ModifierKeys == Keys.None) && (tvFacilityModel.State == TreeListState.Regular)) //這里的幾個條件只有第一個條件有點用,其他的可有可無
{
Point p = new Point(Cursor.Position.X, Cursor.Position.Y); //獲取到鼠標點擊的坐標位置
TreeListHitInfo hitInfo = tree.CalcHitInfo(e.Location);
if (hitInfo.HitInfoType == HitInfoType.Cell)
{
tree.SetFocusedNode(hitInfo.Node); //這句話就是關鍵,用於選中節點
TreeListNode selectNode = this.tvFacilityModel.FocusedNode;
BW.DMSystem.Core.Entity.ModelManageInfo modelObj = selectNode.GetValue(3) as BW.DMSystem.Core.Entity.ModelManageInfo;
if (modelObj == null)
{
return;
}
if (modelObj.IsModel)
{
this.ModelID = modelObj.ID.ToString();
this.ModelName = modelObj.Name;
popupMenu1.ShowPopup(p);
}
}
}
}
再來看看微軟的右鍵選中節點的代碼:
private void treeView1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
treeView1.SelectedNode = treeView1.GetNodeAt(e.X, e.Y);
}
}
誒,有種說不出的悲傷。。。OK該功能已Over
根據ID選中節點:
這里的思路起始很簡單,只要找出樹形中已經綁定的數據源中某個節點的ID的值和傳遞過來的值一樣就選中就行了,但是就是這樣一個簡單的思路 Dev樹形 卻搞了很多花樣着實讓我蛋疼不已啊,原本想直接遍歷樹形的節點的,但是調試中發現 this.tvFacilityModel.Nodes.Count 這句話居然返回的只有第一級的節點,那怎么辦呢?——又只有用遞歸查找了澀 誒,附上遞歸查找代碼:
/// <summary>
/// 遞歸查找子節點
/// </summary>
/// <param name="node"></param>
/// <param name="ID"></param>
/// <returns></returns>
private TreeListNode nodes(TreeListNode node, string ID)
{
TreeListNode returnNodex = null;
if (node.HasChildren)
{
for (int i = 0; i < node.Nodes.Count; i++)
{
if (node.Nodes[i].GetValue("ID").ToString() == ID)
{
returnNodex = node.Nodes[i];
break;
}
else
{
returnNodex = nodes(node.Nodes[i], ID);
}
}
}
return returnNodex;
}
最后再使用 this.tvFacilityModel.FocusedNode = selectNode; 將返回來的節點設置給 Dev樹形 就OK了。
經過這次對樹形控件的使用,沒有多大的收獲,收獲最大的就是對遞歸這種方式有了更深的認識,也熟練了不少,Dev樹形控件我的痛啊!~~~~(>_<)~~~~
好了,也許我的代碼不夠精簡,目前的技術也就寫的出這樣的代碼,希望廣大朋友不要介意,有什么意見和需要幫助請進行評論或留言,多多溝通有助於發展。
