最近一直在做一些技術性的研究和框架改進工作,博客也落下好幾天沒有更新了,也該是時候靜下心 來,總結這段時間的一些技術改進的經驗了。和上一階段的CRM系統開發和技術研究一樣,我都喜歡在一 個項目或者模塊完成後,做一些相關的總結性工作,記錄下前一階段的技術腳印,希望給自己留下一個 腳印快照,同時給讀者了解自己的技術動向外,也有所收獲。本隨筆主要介紹在下拉列表中展示一個列 表,以便實現數據結構的良好展示,並能快速選定所需的節點,這個就是TreeListLookupEdit控件的使 用。
1、界面效果展示
首先我們來看看我的Winform開發框架之權限管理系統模塊改進完善後的主界面,然後在介紹其中用 到的功能點,以及技術實現。
系統用戶信息管理界面如下所示。
其中用戶信息編輯界面如下所示。
其中編輯用戶信息的界面包括了所屬公司、所屬部門、直屬經理三個輸入的內容,為了減少數據量的 顯示,這幾個輸入框是通過級聯的方式進行展示,也就是說,先選定所屬公司,然後在所屬公司中列出 該公司的部門列表,選定部門後,再通過獲取指定部門的人員信息,作為直屬經理的人員展示。
了解這些邏輯關系後,我們來看看所屬公司的列表展示,如下所示。
選定了所屬公司後,所屬部門就根據公司來進行過濾了,如下所示。
通過公司和部門的條件,我們就可以列出有限的人員列表,作為直屬經理的人員列表了,這個列表使 用普通的下拉列表展示即可,在此不再贅述。
以上的樹狀結構的下拉列表,在DevExpress控件中是通過TreeListLookupEdit控件進行實現的。
2、基於DevExpress控件的功能實現及代碼展示
由於這些所屬公司、所屬部門的功能模塊,一般需要不少代碼進行處理,為了更好重用這些模塊,我 們通過用戶控件的封裝方式進行,然後在數據編輯界面上,通過拖動控件方式即可實現布局,並只需要 設置或者訪問某個屬性即可,封裝的用戶界面控件如下所示。
1)所屬公司控件代碼
所屬公司是一個用戶控件,讓其繼承自XtraUserControl即可,為了在選擇內容後觸發值的變化事件 ,我們定義了一個事件EventHandler EditValueChanged,這樣我們在內部控件的值變化的時候,可以通 知外部的界面進行處理。
public partial class CompanyControl : XtraUserControl { /// <summary> /// 選擇的值發生變化的時候 /// </summary> public event EventHandler EditValueChanged; public CompanyControl() { InitializeComponent(); this.txtCompany.EditValueChanged += new EventHandler(txtCompany_EditValueChanged); } void txtCompany_EditValueChanged(object sender, EventArgs e) { if (EditValueChanged != null) { EditValueChanged(sender, e); } }
為了實現列表數據的綁定,以及增加圖標作為區分節點,TreeListLookupEdit控件的數據綁定操作是 這個控件的核心所在。
在下面的代碼邏輯裡面,我們通過獲取公司列表,然後把它轉換為一個DataTable,並根據集團、公 司的層次給予不同的圖標,綁定數據,並設置好控件的KeyFieldName、ParentFieldName、ValueMember 、DisplayMember幾個重要屬性即可,如下所示。
/// <summary> /// 初始化數據 /// </summary> public void Init() { DataTable dt = DataTableHelper.CreateTable("ImageIndex|int,ID,PID,Name"); List<OUInfo> list = BLLFactory<OU>.Instance.GetGroupCompany(); DataRow dr = null; foreach (OUInfo info in list) { dr = dt.NewRow(); dr["ImageIndex"] = Portal.gc.GetImageIndex(info.Category); dr["ID"] = info.ID.ToString(); dr["PID"] = info.PID.ToString(); dr["Name"] = info.Name; dt.Rows.Add(dr); } //設置圖形序號 this.treeListLookUpEdit1TreeList.SelectImageList = this.imageList2; this.treeListLookUpEdit1TreeList.StateImageList = this.imageList2; this.txtCompany.Properties.TreeList.KeyFieldName = "ID"; this.txtCompany.Properties.TreeList.ParentFieldName = "PID"; this.txtCompany.Properties.DataSource = dt; this.txtCompany.Properties.ValueMember = "ID"; this.txtCompany.Properties.DisplayMember = "Name"; }
為了方便編輯界面中,對所屬公司的賦值與獲取操作,我們需要增加一個Text屬性和一個Value屬性 。我們需要重載Text屬性,用來獲取或設置所屬公司的名稱;添加一個Value屬性,用來獲取所屬公司的 ID,如下所示。
/// <summary> /// 公司名稱 /// </summary> public override string Text { get { return this.txtCompany.Text; } set { this.txtCompany.Text = value; } } /// <summary> /// 公司ID /// </summary> public string Value { get { string result = "-1"; if (this.txtCompany.EditValue == null || this.txtCompany.EditValue.ToString() == "0") { result = "-1"; } else { result = this.txtCompany.EditValue.ToString(); } return result; } set { this.txtCompany.EditValue = value; } }
2)所屬部門的控件代碼
所屬部門的代碼邏輯和所屬公司差不多,唯一不同的是,所屬部門需要一個上級的部門標識(公司ID )作為數據的過濾獲取,如下所示。
/// <summary> /// 初始化部門信息 /// </summary> public void Init() { //InitUpperOU DataTable dt = DataTableHelper.CreateTable("ImageIndex|int,ID,PID,Name,HandNo,Category,Address,Note"); DataRow dr = null; if (!string.IsNullOrEmpty(ParentOuID)) { List<OUInfo> list = BLLFactory<OU>.Instance.GetAllOUsByParent(ParentOuID.ToInt32()); foreach (OUInfo info in list) { dr = dt.NewRow(); dr["ImageIndex"] = Portal.gc.GetImageIndex(info.Category); dr["ID"] = info.ID.ToString(); dr["PID"] = info.PID.ToString(); dr["Name"] = info.Name; dr["HandNo"] = info.HandNo; dr["Category"] = info.Category; dr["Address"] = info.Address; dr["Note"] = info.Note; dt.Rows.Add(dr); } } //增加一行空的 dr = dt.NewRow(); dr["ID"] = "0"; //使用0代替-1,避免出現節點的嵌套顯示,因為-1已經作為了一般節點的頂級標識 dr["PID"] = "-1"; dr["Name"] = "無"; dt.Rows.InsertAt(dr, 0); //設置圖形序號 this.treeListLookUpEdit1TreeList.SelectImageList = this.imageList2; this.treeListLookUpEdit1TreeList.StateImageList = this.imageList2; this.txtDept.Properties.TreeList.KeyFieldName = "ID"; this.txtDept.Properties.TreeList.ParentFieldName = "PID"; this.txtDept.Properties.DataSource = dt; this.txtDept.Properties.ValueMember = "ID"; this.txtDept.Properties.DisplayMember = "Name"; }
3)主界面的控件使用
封裝好所屬公司和所屬部門的控件後,我們就可以通過在工具箱裡面拖動控件到主編輯界面裡面去了 ,這樣我們只需要簡單對控件進行設置和賦值即可實現,減少對控件邏輯過多的代碼處理。
數據編輯界面中,控件的使用代碼如下所示。
a)界面賦值操作
this.txtCompany.Value = info.Company_ID;
this.txtDept.Value = info.Dept_ID;
b)界面數據獲取操作
info.Dept_ID = txtDept.Value;
info.DeptName = txtDept.Text;
info.Company_ID = txtCompany.Value;
info.CompanyName = txtCompany.Text;
查看本欄目
c)界面事件的處理操作
由於界面上兩個控件是級聯的關系,因此需要處理他們控件的值發生變化的事件,如下所示。
public partial class FrmEditUser : BaseEditForm { public FrmEditUser() { InitializeComponent(); this.txtCompany.EditValueChanged += new EventHandler(txtCompany_EditValueChanged); this.txtDept.EditValueChanged += new EventHandler(txtDept_EditValueChanged); } void txtCompany_EditValueChanged(object sender, EventArgs e) { if (!string.IsNullOrEmpty(this.txtCompany.Value)) { //賦值給部門控件,並重新初始化部門列表 txtDept.ParentOuID = this.txtCompany.Value; txtDept.Init(); } } void txtDept_EditValueChanged(object sender, EventArgs e) { if (!string.IsNullOrEmpty(txtDept.Value)) { //獲取所屬部門後,就通過部門ID來初始化直屬經理的人員列表 InitManagers(txtDept.Value.ToInt32()); } } .................
3、基於傳統Winform界面的控件實現
上面的介紹內容是基於DevExpress的控件組進行功能實現的,有些人可能會問,我使用傳統樣式的界 面控件,這種效果如何實現呢,其實如果是使用內置的控件,是比較困難實現這個效果的,但是這樣的 界面控件有很多人開發好組件可以利用的,我在做這個模塊的時候,也考慮到了這一點,因此也針對這 些做了一些搜索查詢,發現國外有一個同行封裝的控件做的非常不錯,地址是http://www.brad- smith.info/blog/archives/477,上面的效果基本上沒問題的。這個人的博客和下載的例子裡面,沒有 使用Demo的代碼,我自己根據控件的屬性進行了一個Demo例子的編寫,效果和代碼如下所示。
總體來說,這個控件實現的效果還是非常不錯的,例子代碼如下所示,其節點和TreeView的操作類似 ,通過ComboTreeNode對象進行添加即可。
private void Form1_Load(object sender, EventArgs e) { if (!this.DesignMode) { this.comboTreeBox1.Nodes.Clear(); ComboTreeNode parentNode = new ComboTreeNode(); parentNode.Text = "愛奇迪集團"; parentNode.Nodes.Add("gz", "廣州分公司"); parentNode.Nodes["gz"].ImageIndex = 1;//通過名字引用 parentNode.Nodes["gz"].ExpandedImageIndex = 1;//通過名字引用 ComboTreeNode bjNode = new ComboTreeNode(); bjNode.Name = "bj"; bjNode.Text = "北京分公司"; bjNode.ImageIndex = 1; bjNode.ExpandedImageIndex = 1; bjNode.FontStyle = FontStyle.Bold; bjNode.Nodes.Add("財務部"); bjNode.Nodes.Add("市場部"); bjNode.Nodes.Add("人力資源部"); parentNode.Nodes.Add(bjNode); this.comboTreeBox1.Nodes.Add(parentNode); comboTreeBox1.ExpandAll(); } }
除了這個可以實現傳統界面效果的下拉樹展現外,CodeProject上的 http://www.codeproject.com/Articles/25471/Customizable-ComboBox-Drop-Down這篇文字實現的效果 也還不錯。
不過可能沒有上面的那個效果好一點。
4、總結
以上這些就是關於級聯下拉列表,並在下拉列表裡面展示層次關系的樹形結構的功能實現和核心代碼 的操作,本隨筆介紹了基於DevExpress樣式和傳統樣式兩種方案的實現過程,重點對DevExpress控件中 的TreeListLookupEdit控件的實現進行介紹,希望大家能夠在實際工作中用上。
伍華聰 http://www.iqidi.com