玩轉控制元件:擴充套件Dev中SimpleButton
何為擴充套件,顧名思義,就是在原有控制元件屬性、事件的基礎上拓展自己需要或實用的屬性、事件等等。或者可以理解為,現有的控制元件已經不能完全滿足我(的需求)了。好的擴充套件會使控制元件更加完善,實用,好用。不好的擴充套件,說白了就是畫蛇添足!好了,跟著博主一起玩轉控制元件吧,看看您在實際專案運用中是否也曾幾何時遇到過這樣的困惑。
本篇拿一個簡單的Dev控制元件為例,件如其名——SimpleButton,一個成熟的軟體,一定會考慮到所有人的操作習慣以及簡潔舒適的介面。做人如是,控制元件如是。
本篇緣起於博主最近削尖腦袋,硬著頭皮看英文文件。那傢伙把我讀的,那場面,那氣勢,真是鑼鼓喧天鞭炮起,紅旗招展,文山文海... 大部分它認識我,我不認識它。最後還得靠多年醞釀的Google大法才能撥開雲霧見青天。偶然間聯想到最近預熱的《手撕ERP》(FuckingERP)之控制元件系列,公眾號私信裡面有很多國外的朋友,拿到原始碼,信誓旦旦的按下F5傻眼了。全是中文字,而且有的還亂碼。
因此,鑑於此,從本篇起,後續所有控制元件系列都會擴充套件一個多語言的屬性功能(俗稱國際化)。多語言功能有很多實現方式,博主之前也實踐過很多方式,比如資原始檔處理、展示介面前呼叫Google翻譯後賦值等等,這些方式有利有弊,本篇博主將採用另外一種方式來實現多語言-資料庫儲存,鍵值對取值的方式。巴拉巴拉巴拉.....
扯遠了扯遠了,迴歸主題,還是來看看博主是怎麼擴充套件按鈕的吧!
Talk is Cheap,Show me the Code!
首先,新建一個使用者控制元件,並讓其繼承Dev原始控制元件SimpleButton
public partial class KzxSimpleButton : SimpleButton, ILayoutControl
後面的ILayoutControl介面,是我用來定義要實現的屬性和事件的,部分程式碼如下:
/// <summary> /// 沒有多語言的情況下的預設顯示標題 /// </summary> string DesigeCaption { get; set; } /// <summary> /// 設計時的可用性 /// </summary> bool DesigeEnabled { get; set; } /// <summary> /// 設計時可見性 /// </summary> bool DesigeVisible { get; set; } /// <summary> /// 控制元件的唯一標識 /// </summary> string Key { get; set; } /// <summary> /// 佈局列號 /// </summary> int LayoutColumn { get; set; } /// <summary> /// 佈局跨的列數 /// </summary> int LayoutColumnSpan { get; set; } /// <summary> /// 佈局行號 /// </summary> int LayoutRow { get; set; } /// <summary> /// 佈局跨的行數 /// </summary> int LayoutRowSpan { get; set; } /// <summary> /// 多語言環境下顯示文字的對應標識 /// </summary> string MessageCode { get; set; } /// <summary> /// 事件外掛資訊表 /// </summary> DataTable PluginInfoTable { get; set; } /// <summary> /// 事件列表 /// </summary> string EventList { get; set; } /// <summary> /// 提示資訊 /// </summary> string ToolTipText { get; set; } /// <summary> /// 提示多語言標識 /// </summary> string ToolTipMessageCode { get; set; } /// <summary> /// 繫結事件 /// </summary> /// <param name="valueControl">控制元件</param> /// <param name="eventInfoTable">事件資訊表</param> void BindingEvent(Control valueControl, DataTable eventInfoTable); /// <summary> /// 設定佈局 /// </summary> void SetLayout(); /// <summary> /// 控制元件被載入後呼叫的方法 /// 此方法在控制元件還原後被視窗呼叫 /// </summary> void KzxControlLoaded(); /// <summary> /// 控制元件事件 /// </summary> event KzxControlOperateEventHandler KzxControlOperate; /// <summary> /// 獲取多語言文字事件 /// </summary> event KzxGetLanguageEventHandler KzxGetLanguage;
然後在剛剛新建的使用者控制元件實現這個介面,部分程式碼如下:
#region 佈局屬性 private int _LayoutRow = 0; /// <summary> /// 佈局行號 /// </summary> [Category("佈局"), Description("LayoutRow,佈局行號"), Browsable(false)] [McDisplayName("LayoutRow")] public int LayoutRow { get { if (this.DesignMode == true) { TableLayoutPanel panel = this.Parent as TableLayoutPanel; if (panel != null) { this._LayoutRow = panel.GetRow(this); } } return this._LayoutRow; } set { this._LayoutRow = value; if (this.DesignMode == true) { TableLayoutPanel panel = this.Parent as TableLayoutPanel; if (panel != null) { panel.SetRow(this, value); } } } } private int _LayoutRowSpan = 1; /// <summary> /// 佈局跨的行數 /// </summary> [Category("佈局"), Description("LayoutRowSpan,佈局跨的行數"), Browsable(false)] [McDisplayName("LayoutRowSpan")] public int LayoutRowSpan { get { TableLayoutPanel panel = this.Parent as TableLayoutPanel; if (panel != null) { panel.SetRowSpan(this, this._LayoutRowSpan); } return this._LayoutRowSpan; } set { this._LayoutRowSpan = value; TableLayoutPanel panel = this.Parent as TableLayoutPanel; if (panel != null) { panel.SetRowSpan(this, value); } } } private int _LayoutColumn = 0; /// <summary> /// 佈局列號 /// </summary> [Category("佈局"), Description("LayoutColumn,佈局列號"), Browsable(false)] [McDisplayName("LayoutColumn")] public int LayoutColumn { get { if (this.DesignMode == true) { TableLayoutPanel panel = this.Parent as TableLayoutPanel; if (panel != null) { this._LayoutColumn = panel.GetColumn(this); } } return this._LayoutColumn; } set { this._LayoutColumn = value; if (this.DesignMode == true) { TableLayoutPanel panel = this.Parent as TableLayoutPanel; if (panel != null) { panel.SetColumn(this, value); } } } } private int _LayoutColumnSpan = 1; /// <summary> /// 佈局跨的列數 /// </summary> [Category("佈局"), Description("LayoutColumnSpan,佈局跨的列數"), Browsable(false)] [McDisplayName("LayoutColumnSpan")] public int LayoutColumnSpan { get { TableLayoutPanel panel = this.Parent as TableLayoutPanel; if (panel != null) { panel.SetColumnSpan(this, this._LayoutColumnSpan); } return this._LayoutColumnSpan; } set { this._LayoutColumnSpan = value; TableLayoutPanel panel = this.Parent as TableLayoutPanel; if (panel != null) { panel.SetColumnSpan(this, value); } } } #endregion
為了效果明顯點,博主就拿ToolTip來舉例,以及部分事件程式碼如下:
private string _MessageCode = "0"; /// <summary> /// 多語言環境下顯示文字的對應標識 /// </summary> [Category("多語言"), Description("MessageCode,多語言環境下顯示文字的對應標識"), Browsable(true)] [McDisplayName("MessageCode")] public virtual string MessageCode { get { return this._MessageCode; } set { this._MessageCode = value; } } private string _DesigeCaption = string.Empty; /// <summary> /// 沒有多語言的情況下的預設顯示標題 /// </summary> [Category("多語言"), Description("DesigeCaption,沒有多語言的情況下的預設顯示標題"), Browsable(true)] [McDisplayName("DesigeCaption")] public virtual string DesigeCaption { get { return this.Text.Trim(); } set { this.Text = value; } }
拿ToolTip舉個例子
#region 方法 private DataTable _PluginInfoTable = KzxBaseControl.CreatePluginDataTable(); [Category("自定義"), Description("PluginInfoTable,事件外掛資訊表"), Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [McDisplayName("PluginInfoTable")] public DataTable PluginInfoTable { get { return this._PluginInfoTable; } set { this._PluginInfoTable = value; BindingEvent(this, this._PluginInfoTable); } } private string _ToolTipText = string.Empty; /// <summary> /// 提示資訊 /// </summary> [Category("汽泡提示"), Description("ToolTipText,提示資訊"), Browsable(true)] [McDisplayName("ToolTipText")] public virtual string ToolTipText { get { return this._ToolTipText; } set { this._ToolTipText = value; } } private string _ToolTipMessageCode = string.Empty; /// <summary> /// 提示多語言標識 /// </summary> [Category("汽泡提示"), Description("ToolTipMessageCode,提示資訊多語言標識"), Browsable(true)] [McDisplayName("ToolTipMessageCode")] public virtual string ToolTipMessageCode { get { return this._ToolTipMessageCode; } set { this._ToolTipMessageCode = value; } } private Color _LabelForeColor = Color.Black; /// <summary> /// 標籤字型顏色 /// </summary> [Category("外觀"), Description("LabelForeColor,標籤字型顏色"), Browsable(true)] [McDisplayName("LabelForeColor")] public Color LabelForeColor { get { return this.ForeColor; } set { this._LabelForeColor = value; this.ForeColor = value; } } /// <summary> /// 控鈕型別 /// </summary> [Category("外觀"), Description("YZButtonStyle,控鈕型別"), Browsable(true)] [McDisplayName("YZButtonStyle")] public DevExpress.XtraEditors.Controls.BorderStyles YZButtonStyle { get { return this.ButtonStyle; } set { this.ButtonStyle = value; } } #endregion
程式碼比較簡單,關鍵屬性和事件也已經添加了詳細備註,就不一一解釋了。使用者控制元件建立好後,我們就可以直接實用拖控制元件大法了。
效果如下:
實現控制元件按鈕,滑鼠懸浮提示框(ToolTip)外掛程式碼也比較簡單,具體如下:
其中ssLoadMsgOrDefault方法,就是博文開頭提到的,資料庫儲存-鍵值對取值的方法,具體如下:
/// <summary> /// 根據Msg_ID取相應語言描述,獲取為空時顯示 emptyDisplayMsg /// </summary> /// <param name="msgID"></param> /// <param name="emptyDisplayMsg"></param> /// <returns></returns> public static string ssLoadMsgOrDefault(string msgID, string emptyDisplayMsg) { if (string.IsNullOrWhiteSpace(msgID)) return emptyDisplayMsg; var msgText = string.Empty; if (SysVar.LanguageList.ContainsKey(msgID)) msgText = SysVar.LanguageList[msgID]; if (string.IsNullOrWhiteSpace(msgText)) msgText = emptyDisplayMsg; return msgText; }
為了方便處理多語言,博主還寫了個小軟體,用來儲存或生成多語言對應關係,這個後續會給大家介紹,好了,讓我們F5來看看具體效果:
最後,由於後續所有重寫/重繪控制元件都在同一個專案使用,而且Dev系統引用檔案較多,壓縮後原始碼檔案仍然很大,如果有需要原始碼的朋友,可以微信公眾號聯絡博主,原始碼可以免費贈予~!有疑問的也可以CALL我一起探討,最最後,如果覺得本篇博文對您或者身邊朋友有幫助的,麻煩點個關注!贈人玫瑰,手留餘香,您的支援就是我寫作最大的動力,感謝您的關注,期待和您一起探討!再會!
&n