使用擴充套件函數方式,在Winform介面中快捷的繫結樹形列表TreeList控制元件和TreeListLookUpEdit控制元件

2023-04-25 18:00:36

在一些字典繫結中,往往為了方便展示詳細資料,需要把一些結構樹展現在樹列表TreeList控制元件中或者下拉選單的樹形控制元件TreeListLookUpEdit控制元件中,為了快速的處理資料的繫結操作,比較每次使用涉及太多細節的操作,我們可以把相關的資料繫結操作,放在一些輔助類的擴充套件函數中進行處理,這樣可以更方便的,更簡潔的處理資料繫結操作,本篇隨筆介紹TreeList控制元件和TreeListLookUpEdit控制元件在擴充套件函數中的處理操作。

1、TreeList控制元件的繫結操作

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介面效果如下所示。

 

2、TreeListLookUpEdit控制元件繫結操作

在一些參考的列表中,我們往往需要展示更豐富一點的列表內容,如下所示。

如果有巢狀列表的,展示巢狀列表的處理

對於下拉的樹形列表,雖然這個控制元件比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開發框架中下拉選單系結字典以及使用快取提高介面顯示速度

在各種開發專案中使用公用類庫的擴充套件方法,通過上下文方式快速呼叫處理常式