1。C#中控件和組件的區別:
一般組件派生于:Component類,所以從此類派生出的稱之為組件。
一般用戶控件派生于:Control類或UserControl類,所以從該類派生出的稱之為用戶控件。
他們之間的關系主要是:UserControl繼承Control繼承Component。
概括:組件包括控件,控件肯定是組件,但組件不一定是控件。
控件的突出特點:就是交互式組件(能動,能和客戶交互),而用戶控件則是將某些特定的組件或控件復合從而實現特定的業務功能。
https://blog.csdn.net/cxu123321/article/details/103014278
2。自定義控件分類?
自定義控件、擴展控件、復合控件
完全自定義控件: 繼承Control類
擴展控件:繼承某個具體的控件類,如:Button,Label等
復合控件:繼承UserControl類,又稱用戶控件UserControl。即:把多個控件通過組合的形式,形成更大,功能更全的控件。
https://www.cnblogs.com/zhangchenliang/archive/2012/08/17/2643744.html
https://blog.csdn.net/yysyangyangyangshan/article/details/7078471
繼承鏈
-
Button具體控件類
繼承鏈:Button—>ButtonBase—>Control—>Component -
Control類
繼承鏈:Control—>Component -
UserControl類
繼承鏈:UserControl—>ContainerControl—>ScrollableControl—>Control—>Component
ContainerControl容器控件,支持其他控件向本控件中拖放。
ScrollableControl滾動控件,讓控件支持水平和垂直滾動條。 -
Component類
C# Winform窗體中包括組件和控件兩類:
組件的基類是Component類
控件的基類是Control類
結論:控件肯定是組件,但組件不一定是控件控件包含兩種:自定義控件、官方控件
自定義控件包含三種:
完全自定義的控件:直接繼承Control類
擴展控件:直接繼承某一個官方控件,比如:Button,Label等
用戶控件:最常用一種自定義控件,也是最簡單的,直接繼承UserControl類
用戶控件
概念:
用戶控件:即UserControl,也稱復合控件,又稱組合控件,不能直接運行。必須嵌套在窗體中使用。
用戶控件繼承UserControl類,而UserControl—>…—>繼承Control類
用戶控件怎么創建?怎么設計?怎么使用?
創建:右鍵項目—>用戶控件
設計:像窗體一樣去設計。
使用:先編譯,編譯后工具箱發生變化,像使用自帶的控件一樣去使用用戶控件。
建議:用戶控件不建議在使用時編寫業務邏輯,應該把業務邏輯寫到用戶控件中。優缺點:很容易編寫邏輯,但不靈活。把用戶控件的邏輯暴露出去,讓開發者在使用此控件時,定制邏輯。優缺點:邏輯難寫,但靈活。
公開屬性和公共事件把握一個適度原則,切記:不要把用戶控件中的某個具體的官方控件直接公開。
// 一般情況下在用戶控件中最常用公開的:屬性和事件。
public partial class Login : UserControl
{// 公開一個事件,考慮:這個事件什么時候觸發?[Description("登錄事件")]public event EventHandler LoginEvent;public Login(){InitializeComponent();// 用戶控件,開發者不需要重繪,所以一般可以省略這些配置。/*this.SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint | ControlStyles.SupportsTransparentBackColor, true);*/}private void Login_Load(object sender, EventArgs e){}// 用戶控件:公開屬性//[Category("我的屬性")]//[Category("雜項")]//[Category()][Category][DefaultValue(typeof(string), "賬號:")] // DefaultValue//[Obsolete]// 以上的特性都可以不用,如下的特性在自定義控件時,常用[Description("賬號的標題,默認值賬號:")] // 主要提示用的[Browsable(true)] // 控制某個公開屬性是否可以在屬性窗口中被開發者看見。使用時只能通過代碼訪問。public string AccountText{get { return this.lblAccount.Text; }set{if (string.IsNullOrWhiteSpace(value))throw new ArgumentNullException("AccountText屬性不能為空!");if (value.Length != 3)throw new ArgumentNullException("AccountText屬性只能輸入3個漢字或字母!");this.lblAccount.Text = value;}}[Description("賬號")]public string Account{get { return this.txtAccount.Text; }set{this.txtAccount.Text = value;}}[Description("密碼")]public string Password{get { return this.txtPassword.Text; }set{this.txtPassword.Text = value;}}// LoginEvent的觸發時機:讓用戶點擊btnLogin按鈕時,觸發LoginEvent事件。private void btnLogin_Click(object sender, EventArgs e){//if (LoginEvent != null)// LoginEvent.Invoke(sender, e);// 簡寫LoginEvent?.Invoke(sender, e);}
}
擴展控件
擴展控件怎么創建?怎么設計?怎么使用?
創建:兩種方法,通過用戶控件和組件修改而來。
設計:不建議使用設計器,建議直接編寫代碼。(難點)
使用:先編譯,編譯后工具箱發生變化,像使用自帶的控件一樣去使用擴展控件。
設計步驟:
a. 先公開擴展屬性,事件等
b.在擴展控件類中,使用相應的屬性實現具體的業務邏輯流。(重點:重寫OnPaint事件!)
// 把一個用戶控件改寫成擴展控件,只需要把UserControl基類,改寫成具體的控件類即可!
// 通過用戶控件改寫后,把錯誤修復一下即可。
// InitializeComponent()中的this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;去掉即可!
public partial class MyTextBox : TextBox{public MyTextBox(){InitializeComponent();}[Description("文本框的背景,使用方式同BackColor")]public Color MyBackColor{get{return this.BackColor;}set{this.BackColor = value;Refresh();}}}
3。特性簡單理解?
CategoryAttribute DescriptionAttribute
特性(Attribute)是用于在運行時傳遞程序中各種元素(比如類、屬性、方法、結構、枚舉、組件等)的行為信息的聲明性標簽。您可以通過使用特性向程序添加聲明性信息。一個聲明性標簽是通過放置在它所應用的元素前面的方括號([ ])來描述的。
特性是運行時給各種元素添加聲明性標簽。語法:[某個特性]
特性(Attribute)用于添加元數據,如編譯器指令和注釋、描述、方法、類等其他信息。.Net 框架提供了兩種類型的特性:預定義特性和自定義特性。
https://www.toutiao.com/article/6969542149637833230
https://www.toutiao.com/article/7106017793186824744
完全自定義控件
完全自定義控件繼承Control,不是繼承UserControl,VS2022中沒有提供定義完全自定義控件的模板。
1.方法1:通過用戶控件,改寫成完全自定義控件。
2.方法2:通過組件,改寫成完全自定義控件。
通過用戶控件改寫后,把錯誤修復一下即可。
InitializeComponent()中的this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;去掉即可!
public partial class MyLabel : Control{// 私有字段,畫圖時使用private Font font = new Font("宋體", 9, FontStyle.Regular, GraphicsUnit.Point);public MyLabel(){InitializeComponent();// ControlStyles枚舉項逐個設置,解決重繪時閃爍的問題/* this.SetStyle(ControlStyles.UserPaint, true);//自繪this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); //雙緩沖,為了兼容this.SetStyle(ControlStyles.DoubleBuffer, true);// 雙緩沖,主要解決閃爍的問題this.SetStyle(ControlStyles.ResizeRedraw, true); //調整大小時重繪this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); //禁止檫除背景this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); //透明效果*/// 簡寫:把ControlStyles多個枚舉項都設置成truethis.SetStyle(ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint | ControlStyles.SupportsTransparentBackColor, true);this.BackColor = ColorTranslator.FromHtml("#ECE9D8");this.MouseEnter += MyLabel_MouseEnter;this.MouseLeave += MyLabel_MouseLeave;}private void MyLabel_MouseLeave(object sender, EventArgs e){this.TextColor = Color.FromArgb(22, 95, 162);this.Cursor = Cursors.Arrow;font = new Font("宋體", 9, FontStyle.Regular, GraphicsUnit.Point);Refresh();}private void MyLabel_MouseEnter(object sender, EventArgs e){this.TextColor = Color.Red;this.Cursor = Cursors.Hand;font = new Font("宋體", 12, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Point);Refresh();}/// <summary>/// 對畫布配置,讓畫面畫出來的圖形更清晰,質量更高/// </summary>/// <param name="g">畫布</param>private void SetGraphics(Graphics g){// 設置合成模式為源覆蓋g.CompositingMode = CompositingMode.SourceOver;// 合成圖像時,使用高質量模式g.CompositingQuality = CompositingQuality.HighQuality;// 抗鋸齒(讓畫筆,畫刷平滑些,更清晰)g.SmoothingMode = SmoothingMode.AntiAlias;// 設置插值模式為高質量雙三次插值g.InterpolationMode = InterpolationMode.HighQualityBicubic;}// 完全自定義的控件要求:// 1。必須繼承Control類。// 2。需要重繪(你自己畫控件),需要重寫一個方法OnPaint()// 3。考慮閃屏(雙緩沖),固定配置protected override void OnPaint(PaintEventArgs e){base.OnPaint(e); // 調用基類OnPaint,可以省略。也可以留下。留下時建議寫到第一行。// 如何畫圖?使用GDI或GDI+技術。后面會講,今天了解一下。// 畫板,畫筆,畫刷,畫開狀,畫筆或畫刷的顏色,粗細Graphics g = e.Graphics;// 畫布SetGraphics(g);// 對畫布做一些設置SolidBrush brush = new SolidBrush(TextColor);// 畫刷RectangleF rectF = new RectangleF(5, 5, this.Width - 10, this.Height - 10);g.FillRectangle(new SolidBrush(Color.Gray), rectF); // 把矩形填充顏色g.DrawString(MyText, font, brush, rectF, Format);}// 公開的3個屬性TextColor、MyText、Format(只讀)private Color textColor = Color.FromArgb(22, 95, 162);[Description("文本顏色")]public Color TextColor{get { return textColor; }set{textColor = value;Refresh();//Update();//Invalidate();}}[Description("文本內容")]public string MyText{get{return this.Text;}set{this.Text = value;Refresh();}}private StringFormat format = null;[Description("設置文本對齊格式")]public StringFormat Format{get{if (format == null){format = new StringFormat();format.Alignment = StringAlignment.Center; // 水平居中format.LineAlignment = StringAlignment.Center; // 垂直居中format.FormatFlags = StringFormatFlags.NoWrap; // 不換行format.Trimming = StringTrimming.EllipsisCharacter; // 超出加省略號}return format;}}}
- 屬性顯示在雜項中如下圖
自定義組件
// 組件:一般情況組件不帶界面效果,只帶業務邏輯// 自己封裝了一個定時器組件。public partial class MyTimer : Component{//System.Windows.Forms.Timer工具箱中的定時器//System.Timers.Timer//System.Threading.Timerprivate Timer _timer = new Timer();public event EventHandler TimerTick;// 無參構造函數,給代碼public MyTimer(){InitializeComponent();// 實例化一個定時器_timer = new Timer(TimerInterval);// Elapsed事件相當于System.Windows.Forms.Timer定時器中的Tick事件_timer.Elapsed += _timer_Elapsed;}private void _timer_Elapsed(object sender, ElapsedEventArgs e){TimerTick?.Invoke(sender, e);}// 有參構造函數,給設計器用的public MyTimer(IContainer container){// 把當前的組件this添加容器container中container.Add(this);InitializeComponent();// 實例化一個定時器_timer = new Timer(TimerInterval);// Elapsed事件相當于System.Windows.Forms.Timer定時器中的Tick事件_timer.Elapsed += _timer_Elapsed;}[Description("定時器間隔時間,單位毫秒,默認值1000")]public double TimerInterval{get { return _timer.Interval; }set { _timer.Interval = value; }}[Description("是否啟用定時器")]public bool TimerEnabled{get{return _timer.Enabled;}set{_timer.Enabled = value;}}}
4。反射?
反射是指在程序運行中,查看、操作其他程序集或者自身的元數據的各種信息(類、方法,屬性、變量、對象等)的行為。C#中的反射(Reflection)是一種強大的特性,允許你在運行時檢查和操作程序集、類型和對象的信息,基本上,使用反射可以在代碼運行時檢查和操作指定的類及其成員。C#反射的原理主要基于元數據(與C#特性相似),即程序集中存儲的有關類型、方法等的信息。因為反射可以在程序編譯后獲得信息,所以它提高了程序的拓展性和靈活性。
// 反射最大的目的為了解耦。// 加載Model.dll程序集,只有加載了程序集,才能創建程序集中某個類的實例。Assembly assembly = Assembly.Load("Model");// 拿Model.Student的實例,相當于 Student s = new Student();object stu = assembly.CreateInstance("Model.Student");// 拿到Model.Student的實例的類型,類型Model.StudentType type = stu.GetType();// 拿到實例的Name屬性PropertyInfo name = type.GetProperty("Name");// 設置Name屬性的值為張三,相當于s.Name = "張三";name.SetValue(stu, "張三");// 拿到Name屬性的值,輸出張三Console.WriteLine(name.GetValue(stu)); MethodInfo method = type.GetMethod("ToString");Console.WriteLine(method.Invoke(stu, null));// 獲取所有的方法和屬性MethodInfo[] methodInfos = type.GetMethods();PropertyInfo[] propertyInfos = type.GetProperties();
反射就是為了拿到各種元素對應的標簽。Reflection反射
https://blog.csdn.net/qq_57671924/article/details/134208556
https://blog.csdn.net/naer_chongya/article/details/130532672
5。練習題:
a.圓角按鈕:(自定義控件應用)
https://blog.csdn.net/shi_xi_sheng/article/details/130969580
b.單行文本框定高、文本垂直居中問題:(自定義控件應用)
https://www.cnblogs.com/weekend001/p/3518020.html
https://blog.csdn.net/mazhiyuan1981/article/details/124350065
https://blog.csdn.net/ngl272/article/details/125226552
c.winform界面美化技巧:IrisSkin4皮膚
https://blog.csdn.net/weixin_37864926/article/details/131822828
d.winform UI庫:
https://www.cnblogs.com/bfyx/p/11361809.html
https://www.cnblogs.com/dxqNet/p/17088088.html
https://www.zhihu.com/question/267772520/answers/updated