一、簡單回顧
在前兩節中,對Panel和GroupBox控件進行了相關的擴展應用,主要都是設置控件的邊框以及邊框顏色等。本節,繼續對WinForm現有的控件TextBox進行擴展,來滿足實際開發中的需要。
二、TextBox擴展
WinForm現有的TextBox控件功能已然相當強大了,但有些時候仍然不能滿足一些實際開發的需要,本節針對開發中的需求問題,對控件進行了以下擴展:
1、 和以往一樣,設置控件的邊框問題,主要有:邊框顏色、邊框粗細;
2、 控件添加水印文字效果,可以設置水印文字顏色以及字體等;
3、 實現控件對常規輸入項的驗證效果,比如:數字、電話號碼、電子郵件等;對不符合規則的輸入進行錯誤提示。
(I)、邊框擴展
為自定義控件添加邊框顏色和邊框粗細這兩個屬性;然后WinProc方法中重繪邊框,是重新繪制一個矩形遮蓋原有的線條,這里采用的方法和前兩節基本相似。
(II)、水印提示
為控件添加水印效果的提示,方便於用戶的輸入提示,水印文字實際上就是通過TextRenderer的DrawText 方法將提示文字繪制到TextBox文本框中的。
(III)、輸入驗證
當用戶在即時輸入的過程中,就對輸入內容進行規則驗證,判斷輸入是否正確,若不正確,則提示輸入出錯。這里主要采用了正則表達式來進行驗證,在用戶輸入過程中,觸發OnTextChanged函數,然后驗證輸入的文本。
本節中主要可以進行如下驗證:
默認、數字、漢字、郵政編碼、電子郵件、座機電話號碼、中國電話號碼、手機號碼、整數、負整數、浮點數、非負浮點數、正浮點數、非正浮點數、負浮點數、英文字符、大寫英文字符、小寫英文字符、數字和英文字母、數字、英文字母或下划線、URL、QQ、身份證、IP、"2000-2-28 23:29:59"、"2000-2-28"、年份、月份、日、"23:29:59"、"2000-02-29 10:29:39 pm"、"2009年2月28日"。
關鍵代碼如下:
- protectedoverridevoid WndProc(ref Message m)
- {
- base.WndProc(ref m);
- this.BorderStyle = BorderStyle.FixedSingle;
- if (m.Msg == WM_PAINT || m.Msg == WM_NCPAINT)
- {
- if (this.BorderWeight % 2 == 0)
- {
- this.BorderWeight -= 1;
- }
- using (Graphics g = Graphics.FromHwnd(this.Handle))
- {
- using (Pen pen = new Pen(this.BorderColor, this.BorderWeight))
- {
- g.DrawRectangle(pen, 0, 0, Size.Width - 1, Size.Height - 1);
- }
- }
- WmPaint();
- }
- }
- privatevoid WmPaint()
- {
- using (Graphics graphics = Graphics.FromHwnd(base.Handle))
- {
- if (Text.Length == 0 && !string.IsNullOrEmpty(_waterMarkText) && !Focused)
- {
- TextFormatFlags format = TextFormatFlags.EndEllipsis | TextFormatFlags.VerticalCenter;
- if (RightToLeft == RightToLeft.Yes)
- {
- format |= TextFormatFlags.RightToLeft | TextFormatFlags.Right;
- }
- TextRenderer.DrawText(graphics, _waterMarkText, this.WaterMarkFont, base.ClientRectangle, _waterMarkTextColor, format);
- }
- }
- }
- protectedoverridevoid OnTextChanged(System.EventArgs e)
- {
- if (this.Text != " ")
- {
- bool b = Invalid(this.ControlType, this.Text);
- if (!b)
- {
- this.BorderColor = Color.Red;
- }
- else
- {
- this.BorderColor = r;
- }
- }
- else
- {
- WmPaint();
- }
- base.Invalidate();
- }
- privatebool Invalid(RegexType value, string text)
- {
- bool b = false;
- switch (value)
- {
- case RegexType.Custom:
- b = true;
- break;
- case RegexType.Number:
- b = Validation(text, @"^\d+$");
- break;
- case RegexType.CNString:
- b = Validation(text, @"^[\u4e00-\u9fa5]$");
- break;
- case RegexType.Zip:
- b = Validation(text, @"^[1-9]\d{5}$");
- break;
- case RegexType.Email:
- b = Validation(text, @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
- break;
- case RegexType.Phone:
- b = Validation(text, @"^((\(\d{2,3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$");
- break;
- case RegexType.CNPhone:
- b = Validation(text, @"^\d{3}-\d{8}|\d{4}-\d{7}$");
- break;
- case RegexType.Mobile:
- b = Validation(text, @"^((\(\d{2,3}\))|(\d{3}\-))?13\d{9}$");
- break;
- case RegexType.Integer:
- b = Validation(text, @"^-?\d+$");
- break;
- case RegexType.NInteger:
- b = Validation(text, @"^-[0-9]*[1-9][0-9]*$");
- break;
- case RegexType.Float:
- b = Validation(text, @"^(-?\d+)(\.\d+)?$");
- break;
- case RegexType.NNFloat:
- b = Validation(text, @"^\d+(\.\d+)?$");
- break;
- case RegexType.PFloat:
- b = Validation(text, @"^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$");
- break;
- case RegexType.NPFloat:
- b = Validation(text, @"^((-\d+(\.\d+)?)|(0+(\.0+)?))$");
- break;
- case RegexType.NFloat:
- b = Validation(text, @"^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$");
- break;
- case RegexType.ENChar:
- b = Validation(text, @"^[A-Za-z]+$");
- break;
- case RegexType.ENUChar:
- b = Validation(text, @"^[A-Z]+$");
- break;
- case RegexType.ENLChar:
- b = Validation(text, @"^[a-z]+$");
- break;
- case RegexType.MixChar:
- b = Validation(text, @"^[A-Za-z0-9]+$");
- break;
- case RegexType.MixLineChar:
- b = Validation(text, @"^\w+$");
- break;
- case RegexType.Url:
- b = Validation(text, @"^(?:https?|ftp)\:\/\/(?:(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=:]|%[0-9a-f]{2,2})*\@)?(?:((?:(?:[a-z0-9][a-z0-9\-]*[a-z0-9]|[a-z0-9])\.)*(?:[a-z][a-z0-9\-]*[a-z0-9]|[a-z])|(?:\[[^\]]*\]))(?:\:[0-9]*)?)(?:\/(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@]|%[0-9a-f]{2,2})*)*(?:\?(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?(?:\#(?:[a-z0-9\-\._~\!\$\&\'\(\)\*\+\,\;\=\:\@\/\?]|%[0-9a-f]{2,2})*)?$");
- break;
- case RegexType.QQ:
- b = Validation(text, @"^[1-9][0-9]{4,}$");
- break;
- case RegexType.DCard:
- b = Validation(text, @"^((1[1-5])|(2[1-3])|(3[1-7])|(4[1-6])|(5[0-4])|(6[1-5])|71|(8[12])|91)\d{4}((19\d{2}(0[13-9]|1[012])(0[1-9]|[12]\d|30))|(19\d{2}(0[13578]|1[02])31)|(19\d{2}02(0[1-9]|1\d|2[0-8]))|(19([13579][26]|[2468][048]|0[48])0229))\d{3}(\d|X|x)?$");
- break;
- case RegexType.IP:
- b = Validation(text, @"^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$");
- break;
- case RegexType.DateTime:
- b = Validation(text, @"^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-)) (20|21|22|23|[0-1]?\d):[0-5]?\d:[0-5]?\d$");
- break;
- case RegexType.Date:
- b = Validation(text, @"^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$");
- break;
- case RegexType.Year:
- b = Validation(text, @"^((1[6-9]|[2-9]\d)\d{2})$");
- break;
- case RegexType.Month:
- b = Validation(text, @"^(0?[123456789]|1[012])$");
- break; span>
- case RegexType.Day:
- b = Validation(text, @"^(0?[1-9]|[12]\d|3[01])$");
- break;
- case RegexType.Time:
- b = Validation(text, @"^(20|21|22|23|[0-1]?\d):[0-5]?\d:[0-5]?\d$");
- break;
- case RegexType.DateTimeAm:
- b = Validation(text, @"^((\d{2}(([02468][048])|([13579][26]))[\-\/\s]?((((0?[13578])|(1[02]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|([1-2][0-9])))))|(\d{2}(([02468][1235679])|([13579][01345789]))[\-\/\s]?((((0?[13578])|(1[02]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(\s(((0?[1-9])|(1[0-2]))\:([0-5][0-9])((\s)|(\:([0-5][0-9])\s))([AM|PM|am|pm]{2,2})))?$");
- break;
- case RegexType.Date2:
- b = Validation(text, @"^((((1[6-9]|[2-9]\d)\d{2})年(0?[13578]|1[02])月(0?[1-9]|[12]\d|3[01])日)|(((1[6-9]|[2-9]\d)\d{2})年(0?[13456789]|1[012])月(0?[1-9]|[12]\d|30)日)|(((1[6-9]|[2-9]\d)\d{2})年0?2月(0?[1-9]|1\d|2[0-8])日)|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))年0?2月29日))$");
- break;
- }
- return b;
- }
- privatebool Validation(string validValue, string regularExpression)
- {
- Regex regex;
- try
- {
- regex = new Regex(regularExpression);
- }
- catch
- {
- returnfalse;
- }
- if (regex.IsMatch(validValue))
- {
- returntrue;
- }
- else
- {
- returnfalse;
- }
- }
關鍵代碼就是這么多,其實也都很簡單,下面來看下實現的效果:
控件完成后,添加到窗體當中,則有如下自定義屬性:
控件運行效果如下:
運行狀態下,紅色邊框的為出錯提示。
三、關聯知識補遺
1、C#TextRenderer類相關應用
TextRenderer 類提供了一組 static 方法,可用於在 Windows 窗體控件上測量和繪制文本。
2、正則表達式的相關應用
C#中的正則表達式包含在.NET基礎類庫的一個名稱空間下,這個名稱空間是System.Text.RegularExpressions。
該命名空間包括8個類,1個枚舉,1個委托。他們分別是:
Capture: 包含一次匹配的結果;
CaptureCollection: Capture的序列;
Group: 一次組記錄的結果,由Capture繼承而來;
GroupCollection:表示捕獲組的集合
Match: 一次表達式的匹配結果,由Group繼承而來;
MatchCollection: Match的一個序列;
MatchEvaluator: 執行替換操作時使用的委托;
Regex:編譯后的表達式的實例。
RegexCompilationInfo:提供編譯器用於將正則表達式編譯為獨立程序集的信息
RegexOptions 提供用於設置正則表達式的枚舉值
Regex類中還包含一些靜態的方法:
Escape: 對字符串中的regex中的轉義符進行轉義;
IsMatch: 如果表達式在字符串中匹配,該方法返回一個布爾值;
Match: 返回Match的實例;
Matches: 返回一系列的Match的方法;
Replace: 用替換字符串替換匹配的表達式;
Split: 返回一系列由表達式決定的字符串;
Unescape:不對字符串中的轉義字符轉義。
正則表達式在C#中的應該很簡單就是一個簡單的匹配,但是如何去寫這個表達式則需要技巧和實踐。
[PS:補充源代碼下載]源代碼