[C#.NET][Winform][User Control] 使用 IExtenderProvider 擴充 控制項樣式模版
一直以來都想要用一種方式來快速換掉整個 Form/Control 的Skin以及Style,Devexpress元件就有這樣的好功能,真的很威,這元件要價$800 USD多,今天不是要介紹該元件。
我這次想要,在不重寫控制項的情況下使用 IExtenderProvider ,來為每一個.NET預設的控制項新增一個屬性,讓那個屬性決定控制項的樣式,我腦中這樣想著,所以
我需要一個StyleContainer元件,為控制項,新增一個Style屬性,
由StyleAppearance元件則決定樣式,比如說字型、顏色、滑鼠事件樣式,一個StyleAppearance可以套用在不同的控制項
我們也可以為控制項在不同的狀態下展現不同的樣式,比如說Focus、Disable,你若想要學習如何使用就接著看下去吧。
@Appearance.cs,定義Appearance 類別,這裡存放樣式定義
using System.Drawing;
namespace System.Windows.Forms.Extend.Style
{
[TypeConverter(typeof(ExpandableObjectConverter))]
public class Appearance : INotifyPropertyChanged
{
public Appearance() { }
public const string BindingFont = "Font";
public const string BindingBackColor = "BackColor";
public const string BindingForeColor = "ForeColor";
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private Font _Font = new Font("Microsoft Sans Serif", 8.25f);
[DefaultValue(typeof(Font), "Microsoft Sans Serif")]
public Font Font
{
get { return _Font; }
set
{
_Font = value;
OnPropertyChanged(BindingFont);
}
}
private Color _BackColor = SystemColors.Control;
[DefaultValue(typeof(Color), "Control")]
public Color BackColor
{
get { return _BackColor; }
set
{
_BackColor = value;
OnPropertyChanged(BindingBackColor);
}
}
private Color _ForeColor = SystemColors.ControlText;
[DefaultValue(typeof(Color), "ControlText")]
public Color ForeColor
{
get { return _ForeColor; }
set
{
_ForeColor = value;
OnPropertyChanged(BindingForeColor);
}
}
public override string ToString()
{
return "Appearance";
}
}
}
@StyleAppearance.cs 用在VS設計階段的元件,以便在VS裡選擇套用
namespace System.Windows.Forms.Extend.Style
{
public partial class StyleAppearance : Component
{
public StyleAppearance()
{
InitializeComponent();
}
public StyleAppearance(IContainer container)
: this()
{
container.Add(this);
}
private Appearance _AppearanceNone = new Appearance();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Appearance AppearanceNone
{
get
{
return _AppearanceNone;
}
set
{
_AppearanceNone = value;
}
}
}
}
@StyleContainer.cs 為控制項增加屬性
using System.ComponentModel;
namespace System.Windows.Forms.Extend.Style
{
[ProvideProperty("Style", typeof(Control))]
public partial class StyleContainer : Component, IExtenderProvider
{
#region 實作 IExtenderProvider 成员
public bool CanExtend(object target)
{
if (target is Form)
return false;
else
return true;
}
#endregion
public StyleContainer()
{
InitializeComponent();
}
public StyleContainer(IContainer container)
{
container.Add(this);
InitializeComponent();
}
private Dictionary<Control, StyleAppearance> _Styles = new Dictionary<Control, StyleAppearance>();
public void SetStyle(Control Ctrl, StyleAppearance Style)
{
if (this._Styles.ContainsKey(Ctrl))
{
this._Styles[Ctrl] = Style;
}
else
{
this._Styles.Add(Ctrl, Style);
}
if (Style == null)
{
Ctrl.Font = null;//沒有選擇StyleAppearance時則清掉字型,在VS設計階段時可觀察用
}
else
{
Ctrl.Font = Style.AppearanceNone.Font;//有選擇StyleAppearance時則指定字型,在VS設計階段時可觀察用
Style.AppearanceNone.PropertyChanged += new PropertyChangedEventHandler(Appearance_PropertyChanged);
}
}
void Appearance_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Appearance appearance = (Appearance)sender;
//TODO:可以利用反射再處理過
foreach (var item in this._Styles)
{
Control ctrl = item.Key;
StyleAppearance style = item.Value;
if (ctrl == null || style == null)
continue;
switch (e.PropertyName)
{
case Appearance.BindingFont:
ctrl.Font = appearance.Font;
break;
case Appearance.BindingBackColor:
ctrl.BackColor = appearance.BackColor;
break;
case Appearance.BindingForeColor:
ctrl.ForeColor = appearance.ForeColor;
break;
}
}
}
[DefaultValue(null)]
public StyleAppearance GetStyle(Control Ctrl)
{
if (this._Styles.ContainsKey(Ctrl))
{
return this._Styles[Ctrl];
}
else
{
return null;
}
}
public override string ToString()
{
return "StyleContainer";
}
}
}
這樣一來便完成了基本的樣式套版
完成後
選擇已建立的StyleAppearance元件
在StyleAppearance元件裡修改AppearanceNone屬性的字型大小
看看VS幫我們產生的程式碼
在StyleContainer.cs我們處理這個事件Style.AppearanceNone.PropertyChanged += new PropertyChangedEventHandler(Appearance_PropertyChanged); 所以在執行階段能幫我們處理控制項屬性
若要在VS設計階段觀察我們變更的值,我們則在SetStyle裡處理
到目前為止我們已經完成了基本的功能樣式變更,如果要讓控制項停用時有不一樣的樣式呢?該怎麼做呢?用程式碼變更控制項的Enable屬性後,怎麼捕捉Enable屬性變更事件?
這又是另一項工程,留到下回再戰吧~
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET