個人理解,開發應用程序的目的,不論是B/S或是C/S結構類型,無非就是實現可供用戶進行查、增、改、刪,其中查詢用到最多,開發設計的場景也最為復雜,包括但不限于:表格記錄查詢、報表查詢、導出文件查詢等等,其次就是增加、更改、刪除,我這里統稱為編輯,而編輯在開發設計中的場景就顯得較為通用,大多都采用編輯組件(文本框、下拉框、選擇框、數字框等)來供用戶進行編輯操作,由于查詢的開發設計場景需與實際的數據及客戶要求來進行的,所以在此不作討論,本文主要講述的是如何利用FlowLayoutPanel及我(夢在旅途)自定義的編輯控件來實現快速構建C/S版的編輯表單頁面。
有經歷過開發ASP.NET MVC網站的人都知道,在構建編輯頁面時,我們可以通過HtmlHelper擴展類相關方法來快速生成相應的編輯控件,如:Html.LabelFor,Html.TextBoxFor等等,而在Winform 項目中卻并沒有類似的方法可以讓我們快速構建編輯表單頁面,所以為了減輕我們經常遇到,又比較耗時,且沒有任何技術含量的開發工作量,我開發了類似的編輯控件系列,包括:CTextBox【文本框】、CPictureBox【圖片框】、CNumberBox【數字框】、CDropDownBox【下拉框】、CDisplayBox【顯示框】、CCheckBox【勾選框】,這些控件都一些共有特性,如:Label:顯示編輯項目名稱,Value:控件的值,ValueFor<TEntity>:快速設置控件相關屬性的方法,這些共有特性是實現快速構造編輯控件的關鍵,當然每種編輯控件還有自己獨特的屬性,如:CTextBox可以設置是否顯示按鈕,是否只讀,是否多行等,CNumberBox可以設置小數位、最大值與最小值等,由于這些控件的代碼加起來可能比較多,我這里僅以CTextBox為例來進行分析與說明,CTextBox是屬性最多的控件。
首先還是看一下CTextBox控件的界面設計,如下圖:
從圖中可以看出,CTextBox控件由一個TableLayouPanel、一個Label、一個TextBox、一個Button共同組成:
TableLayouPanel作為控件的頂層容器,其DOCK屬性設置為Fill,實現占據整個控件,另外分別設有一行三列,第一列寬度設為自動調整,第二列寬度設為100%,第三列寬度設為自動調整,如下圖:
TableLayouPanel這樣設計的目的是:實現第一列的寬度依據LABEL的內容自動調整,第二列的寬度為TextBox的寬度,第三列的寬度為Button的寬度,這樣一來,我們在改變CTextBox的整個大小時,確保布局一致性(即:在CTextBox總寬度內,TextBox的寬度是CTextBox總寬度-LABEL與Button的實際寬度)
Label控件作為顯示編輯項目名稱部份,它的屬性AutoSize設為True,Anchor設為Left,設計目的:實現在第一列中向左居中,且寬度依據實際內容而改變
TextBox控件作為可供編輯內容部份,它的屬性設Anchor設為Top, Bottom, Left, Right,設計目的:實現 TextBox控件占據整個第二列,即若控件的寬度與高度改變,TextBox控件也會跟著改變,這個很重要哦!
Button控件為可選部份,若需要用到控件的值來源于其它選項(如:瀏覽文件、保存文件等),則可將其CTextBox.DispalyOpenButton設為True即可,同樣,它的屬性Anchor設為Left,并調整好固定的寬度與高度,設計目的:實現在第三列中向左居中,且寬度不變
實現的整體效果如下圖示:
我這里用紅線框出來了,大家看出效果來了嗎?也就是我直接拖放了三個CTextBox控件,然后分別將LABEL的內容設置為不相同,除非手動改變控件寬度,否則每個控件寬度均相同,這樣再結合FlowLayoutPanel控件的流式布局(即:每個控件按照順序依次排列,若寬度與高度發生變化,控件的排列就會改變),就能很好的快速構建編輯頁面,看看最終用在我的項目中的效果吧,有沒有覺得不錯呢:
這是改變寬度前的布局,有5列,我有紅框標出來了,都是對齊的
這是我縮小寬度后的布局,有2列,我有紅框標出來了,仍然都是對齊的
以上布局效果是不需要編寫任何代碼的,代碼部份僅是設置相應的屬性與方法,如下:
CTextBox控件源碼:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using System.Linq.Expressions; using Zwj.TEMS.Common; namespace TEMS.Controls { public partial class CTextBox : UserControl,IZwjDefControl { [Description("當點擊按鈕時觸發該事件")] public event EventHandler OnOpen; public CTextBox() { InitializeComponent(); this.DispalyOpenButton = false; } [Browsable(true)] [Description("設置文本框的值")] public string Value { get { return textBox1.Text; } set { textBox1.Text = value; } } [Browsable(true)] [Description("設置標簽的值")] public string Label { get { return label1.Text; } set { label1.Text = value; } } [Browsable(true)] [Description("設置是否顯示打開按鈕")] public bool DispalyOpenButton { get { return button1.Visible; } set { button1.Visible = value; textBox1.ReadOnly = button1.Visible; } } [Browsable(true)] [Description("設置是否允許多行")] public bool AllowMultiline { get { return textBox1.Multiline; } set { textBox1.Multiline = value; if (textBox1.Multiline) { textBox1.ScrollBars = ScrollBars.Vertical; } } } public void ValueFor<TEntity>(Expression<Func<TEntity, dynamic>> selectField, string fieldValue, bool displayBtn = false, bool allowMultiline=false) where TEntity : class { var fieldInfo = General.GetPropertyInfo(selectField); this.Label = General.GetDisplayName(fieldInfo); this.Value = fieldValue; this.DispalyOpenButton = displayBtn; this.AllowMultiline = allowMultiline; } private void button1_Click(object sender, EventArgs e) { if (this.OnOpen != null) { this.OnOpen(this, null); } } } }
以下是系統自動生成的源碼:
namespace TEMS.Controls { partial class CTextBox { /// <summary> /// 必需的設計器變量。 /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// 清理所有正在使用的資源。 /// </summary> /// <param name="disposing">如果應釋放托管資源,為 true;否則為 false。</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region 組件設計器生成的代碼 /// <summary> /// 設計器支持所需的方法 - 不要 /// 使用代碼編輯器修改此方法的內容。 /// </summary> private void InitializeComponent() { this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.label1 = new System.Windows.Forms.Label(); this.textBox1 = new System.Windows.Forms.TextBox(); this.button1 = new System.Windows.Forms.Button(); this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel1 // this.tableLayoutPanel1.ColumnCount = 3; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); this.tableLayoutPanel1.Controls.Add(this.textBox1, 1, 0); this.tableLayoutPanel1.Controls.Add(this.button1, 2, 0); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 1; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.Size = new System.Drawing.Size(250, 40); this.tableLayoutPanel1.TabIndex = 0; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Font = new System.Drawing.Font("微軟雅黑", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.label1.Location = new System.Drawing.Point(3, 10); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(44, 20); this.label1.TabIndex = 0; this.label1.Text = "label"; this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // textBox1 // this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.textBox1.Font = new System.Drawing.Font("微軟雅黑", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.textBox1.Location = new System.Drawing.Point(53, 3); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(151, 27); this.textBox1.TabIndex = 1; // // button1 // this.button1.Anchor = System.Windows.Forms.AnchorStyles.None; this.button1.Location = new System.Drawing.Point(210, 8); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(37, 23); this.button1.TabIndex = 2; this.button1.Text = "..."; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // CTextBox // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tableLayoutPanel1); this.Name = "CTextBox"; this.Size = new System.Drawing.Size(250, 40); this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.Label label1; private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.Button button1; } }
代碼就比較簡單了,在此就不再詳細說明了,只是其中用到了兩個我自定義的方法:
General.GetPropertyInfo ---根據LINQ表達式獲取屬性信息;
General.GetDisplayName ---根據屬性信息獲取顯示的名稱;
方法定義代碼如下(我以前的博文及個人網站www.zuowenjun.cn中也有說明):
/// <summary> /// 獲取屬性需要顯示的名稱 /// </summary> /// <param name="p"></param> /// <returns></returns> public static string GetDisplayName(PropertyInfo p) { string displayName = null; DisplayAttribute attr = p.GetAttribute<DisplayAttribute>(); if (attr != null) { displayName = attr.Name; } else { displayName = p.Name; } return displayName; } /// <summary> /// 獲取指定屬性信息(非String類型存在裝箱與拆箱) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="select"></param> /// <returns></returns> public static PropertyInfo GetPropertyInfo<T>(Expression<Func<T, dynamic>> select) { var body = select.Body; if (body.NodeType == ExpressionType.Convert) { var o = (body as UnaryExpression).Operand; return (o as MemberExpression).Member as PropertyInfo; } else if (body.NodeType == ExpressionType.MemberAccess) { return (body as MemberExpression).Member as PropertyInfo; } return null; }
如果覺得可以給個推薦吧,如果覺得有不足的地方,歡迎指出,謝謝!
另外為了方便大家學習與使用系列控件,我已將源碼打包上傳,大家可以點以下鏈接下載:
文章列表