在我們一些非標的用戶界面中,往往需要自定義用戶控件界面,從而實現不同的內容展示和處理規則,本文介紹使用Winform開發自定義用戶控件,以及實現相關自定義事件的處理。
PS:給大家推薦一個C#開發可以用到的界面組件——DevExpress WinForms,它能完美構建流暢、美觀且易於使用的應用程序,無論是Office風格的界面,還是分析處理大批量的業務數據,它都能輕鬆勝任!
用戶控件的界面分析
對於比較規範的界面,需要進行一定的分析,以便從中找到對應的規則,逐步細化爲自定義用戶控件的方式,例如對於由下面多個集合組成的界面內容。
我們截取其中之一,也就是由這些內容多個組合而成,集合可以通過佈局TableLayoutPanel(表格佈局)或者FlowLayoutPanel(順序流佈局)來添加即可。
而其中之一的內容,不同的顏色方格又可以定義爲一個用戶控件,因此最終有多個小方格組成的用戶控件的。
而單個用戶控件,可能承載不同的內容,我們可以定義更多的接口屬性以及一些事件來處理相關的邏輯。
甚至還可以在一個單元格里面放置更多的內容,如放置一些特殊的標籤來展示信息。
自定義用戶控件的接口和實現
爲了使用戶控件更加規範化,我們可以定義一個接口,聲明相關的屬性和處理方法,如下代碼所示。
/// <summary> /// 自定義控件的接口 /// </summary> public interface INumber { /// <summary> /// 數字 /// </summary> string Number { get; set; } /// <summary> /// 數值顏色 /// </summary> Color Color { get; set; } /// <summary> /// 顯示文本 /// </summary> string Animal { get; set; } /// <summary> /// 顯示文本 /// </summary> string WuHan { get; set; } /// <summary> /// 設置選中的內容的處理 /// </summary> /// <param name="data">事件數據</param> void SetSelected(ClickEventData data); }
然後我們創建一個用戶控件,並命名爲NumberItem,並使它繼承前面定義的接口 INumber ,實現相關的屬性和事件,如下代碼所示。
/// <summary> /// 自定義用戶控件 /// </summary> public partial class NumberItem : UserControl, INumber { /// <summary> /// 數字 /// </summary> public string Number { get; set; } /// <summary> /// 顏色 /// </summary> public Color Color { get; set; } /// <summary> /// 顯示文本 /// </summary> public string Animal { get; set; } /// <summary> /// 顯示文本 /// </summary> public string WuHan { get; set; }
其中處理方法SetSelected先保留爲空,後面繼續完善。
/// <summary> /// 設置選中的數值 /// </summary> /// <param name="data">傳遞的數據</param> public void SetSelected(ClickEventData data) { }
由於自定義控件,我們需要跟蹤用戶的單擊處理,並且需要把這個邏輯逐步推動到頂級界面上去進行處理,因此需要定義一個事件信息,如下所示。
/// <summary> /// 事件處理 /// </summary> public EventHandler<ClickEventData> ClickEventHandler { get; set; }
其中ClickEventData是我們定義的一個數據,用來承載用戶單擊的類型和值內容的信息結構,如下代碼所示。
/// <summary> /// 對自定義控件觸發的事件信息 /// </summary> public class ClickEventData { /// <summary> /// 事件觸發類型 /// </summary> public ClickEventType ClickEventType { get; set; } = ClickEventType.Number; /// <summary> /// 傳遞值 /// </summary> public string Value { get; set; } public ClickEventData() { } /// <summary> /// 參數化構造 /// </summary> /// <param name="clickEventType">事件觸發類型</param> /// <param name="value">傳遞值</param> public ClickEventData(ClickEventType clickEventType, string value) { ClickEventType = clickEventType; Value = value; } }
再創建一個整合多個號碼數值的一個自定義控件,它也是一個完整的單元之一,我們命名爲 LotteryItemControl2。
我們相當於把前面的自定義控件,組合爲一個新的用戶控件,形成一個相對完整的部分,這裏提供兩種思路,一種是使用常規的用戶控件,拖動已有的用戶控件組合而成,如下所示。
另一種是利用TableLayoutPanel,動態添加控件進行組合,可以根據預設的TableLayout佈局實現控件的順序添加。
表格的行列定義如下所示:
兩種方式都可以實現類似的效果,我們這裏以第一種爲例實現。
public partial class LotteryItemControl2 : UserControl { /// <summary> /// 事件處理 /// </summary> public EventHandler<ClickEventData> ClickEventHandler { get; set; } /// <summary> /// 第幾期 /// </summary> public string Qi { get; set; } /// <summary> /// 數據列表 /// </summary> public List<string> NumberList { get; set; }
數據列表就是展示在自定義控件的數字。在控件中定義一個函數 統一處理數據內容的綁定顯示。
/// <summary> /// 綁定數據 /// </summary> public void BindData() { //控件列表,方便統一處理 var controlList = new List<NumberItem> { this.numberItem1, this.numberItem2, this.numberItem3, this.numberItem4, this.numberItem5, this.numberItem6, this.numberItem7 }; this.labelQi.Text = Qi; //設置第幾期 for(int i =0; i < this.NumberList.Count; i++) { var control = controlList[i]; var number = this.NumberList[i]; var shenxiao = LotteryToolHelper.NumberToShenXiaoDict[number]; //"馬"; var wuhan = LotteryToolHelper.NumberToWuhanDict[number];//"土" control.Number = number; control.Animal = shenxiao; control.WuHan = wuhan; var colorStr = LotteryToolHelper.ColorBall[number]; control.Color = LotteryToolHelper.GetColor(colorStr); //item % 2 == 0 ? Color.Red : Color.Green; control.BindData(); control.ClickEventHandler += (s, data) => { if (ClickEventHandler != null) { //傳遞父控件統一處理 ClickEventHandler(s, data); } }; } }
其中該控件也可以設置選中,有具體的子控件調用設置選中的處理規則即可。
/// <summary> /// 遍歷控件,設置選中的數值 /// </summary> /// <param name="data">傳遞信息</param> public void SetSelected(ClickEventData data) { foreach (var control in this.Controls) { if (control is NumberItem item) { item.SetSelected(data); } } }
爲了提高性能,我們一般往往需要設置窗體或者Panel爲雙緩衝DoubleBuffered = true。
在主界面的面板中,我們可以添加一個FlowLayoutPanel 來按順序堆疊用戶控件,具體的實現邏輯就是根據從數據庫獲得的記錄進行展示即可。
var controlList = new List<LotteryItemControl2>(); foreach (var info in list) { var control = new LotteryItemControl2(); control.Qi = info.LineNo.ToString("D2"); var numberList = new List<string>() { info.No1.ToString("D2"), info.No2.ToString("D2"), info.No3.ToString("D2"), info.No4.ToString("D2"), info.No5.ToString("D2"), info.No6.ToString("D2"), info.No7.ToString("D2"), }; control.NumberList = numberList; control.BindData(); control.ClickEventHandler += (s, data) => { //遍歷所有的控件統一處理樣式 foreach (var subCtrl in panel.Controls) { if (subCtrl is LotteryItemControl2 lottery) { lottery.SetSelected(data); } } }; controlList.Add(control); } this.panel.Controls.AddRange(controlList.ToArray());
以上就是相關的處理邏輯,用來組織自定義用戶控件的統一展示處理。
如果需要用戶進行不同條件的數據展示,那麼展示前,就需要重新清空面板中的控件,如下所示。
//清空界面 while (panel.Controls.Count > 0) { var controltoremove = panel.Controls[0]; panel.Controls.RemoveAt(0); controltoremove.Dispose(); } panel.Controls.Clear();
上面代碼記得調用Dispose方法來釋放控件資源。
在最小的自定義控件中,我們可能需要根據一些條件進行一些自定義繪製處理,以突出顯示不同的內容(重點強調選中項目)。
private void NumberItem_Paint(object sender, PaintEventArgs e)
如下是一些特殊的繪製處理內容。
private void NumberItem_Paint(object sender, PaintEventArgs e) { this.BackColor = (this.BorderStyle == BorderStyle.FixedSingle) ? Color.Yellow : Color.Transparent; if (this.BorderStyle == BorderStyle.FixedSingle) { IntPtr hDC = GetWindowDC(this.Handle); Graphics g = Graphics.FromHdc(hDC); ControlPaint.DrawBorder( g, new Rectangle(0, 0, this.Width, this.Height), _borderColor, _borderWidth, ButtonBorderStyle.Solid, _borderColor, _borderWidth, ButtonBorderStyle.Solid, _borderColor, _borderWidth, ButtonBorderStyle.Solid, _borderColor, _borderWidth, ButtonBorderStyle.Solid); g.Dispose(); ReleaseDC(Handle, hDC); } }
最終展示效果如下所示,黃色強調的處理,是選中相同號碼的處理事件結果繪製。
本文轉載自:博客園 - 伍華聰