既然是一個窗體設計器,那就應該能夠設置控件的屬性,設置屬性最好的當然是PropertyGrid了,我們僅僅需要使用一個PropertyGrid.SelectedObject = Control就可以搞定,讓PropertyGrid顯示Control的所有屬性。可是這里顯示的屬性名是英文的。對於我們開發人員來說這無可厚非,我們也樂於接受。並且讓PropertyGrid顯示中文屬性名,這對於我們開發人員的使用來說顯得多此一舉。可是,對於我這種類型的一個應用工具,英文屬性名對於很多客戶來說可能就很難懂了。所以應該讓PrpertyGrid能夠顯示中文屬性名。
如圖:
另外,對於這樣的一個系統。並不是控件的所有屬性都是用戶希望的,可能用戶希望看到的僅僅是控件高度、控件寬度、控件文本。。等等的屬性,但是如果直接將一個控件屬性全部顯示給用戶的話,估計對用戶造成的干擾和困惑是很大的。如何解決這個問題呢?其實用戶控件開發的時候,如果我們不希望此屬性在PropertyGrid中顯示,我們可以設置這個控件的Attribute,如:
[Browsable(false)] public int Width { get { } set { } }
通過使用BrowsableAttribute就可以設置將此屬性對PropertyGrid隱藏。
你可能要問到了,對於控件來說,其中的很多屬性都是直接繼承來的,我們並沒有辦法控制是否對PropertyGrid隱藏啊?呵呵,對啊,這就是我下面要說的解決方法(當然此方法顯得不是很靈活,但是對於這種類型的系統的確相當有用)。
在我的解決方式中,我不直接這樣PropertyGrid.SelectedObject = Control,而是把這個Control替換成一個專門為此類型的Control設計的類對象上。比如我對TextBox設計了一個TextBoxProperty,這樣我們使用的是PropertyGrid.SelectedObject = TextBoxProperty的一個對象。
下面就是TextBoxProperty的代碼:
public class TextBoxProperty : PropertyBase { private TextBox _Control; public TextBoxProperty() { } public TextBoxProperty(TextBox control) { this._Control = control; } [MyControlAttibute("文本", "獲取或者設置控件文本", "")] public string Text { get { return this._Control.Text; } set { this._Control.Text = value; } } [MyControlAttibute("寬度", "獲取或者設置控件寬度", "")] public int Width { get { return this._Control.Width; } set { this._Control.Width = (int)value; } } [MyControlAttibute("高度", "獲取或者設置控件高度", "")] public int Height { get { return this._Control.Height; } set { this._Control.Height = (int)value; } } [MyControlAttibute("上邊距", "獲取或者設置控件上邊位置", "")] public int Top { get { return this._Control.Top; } set { this._Control.Top = value; } } [MyControlAttibute("左邊距", "獲取或者設置控件左邊位置", "")] public int Left { get { return this._Control.Left; } set { this._Control.Left = value; } } [MyControlAttibute("背景色", "獲取或者設置控件背景顏色", "")] public Color BackColor { get { return this._Control.BackColor; } set { this._Control.BackColor = value; } } [MyControlAttibute("前景色", "獲取或者設置控件的前景顏色", "")] public Color ForeColor { get { return this._Control.ForeColor; } set { this._Control.ForeColor = value; } } }
你從代碼里面已經看出了一些端倪了,在TextBoxProperty中每個要屬性都增加了MyControlAttibute,具體Attribute的概念和使用方法您可以參考我的另一篇Blog文《C#基礎系列:實現自己的ORM(反射以及Attribute在ORM中的應用)》。這里對Attribute做了詳細的介紹(對初學者有效)。所以我就直接貼出MyControlAttibute的代碼好了:
public class MyControlAttibute : Attribute { private string _PropertyName; private string _PropertyDescription; private object _DefaultValue; public MyControlAttibute(string Name, string Description, object DefalutValue) { this._PropertyName = Name; this._PropertyDescription = Description; this._DefaultValue = DefalutValue; } public MyControlAttibute(string Name, string Description) { this._PropertyName = Name; this._PropertyDescription = Description; this._DefaultValue = ""; } public MyControlAttibute(string Name) { this._PropertyName = Name; this._PropertyDescription = ""; this._DefaultValue = ""; } public string PropertyName { get { return this._PropertyName; } } public string PropertyDescription { get { return this._PropertyDescription; } } public object DefaultValue { get { return this._DefaultValue; } } }
通過上面的兩段代碼,你已經初步看出了在PropertyGrid中顯示中文屬性以及僅僅顯示我們需要的屬性的基本思路。下面就介紹的就是怎么讓MyControlAttibute中定義了的中文屬性名在PropertyGrid中顯示出來。下面這段代碼,我僅僅貢獻了PropertyStub中這個函數的實現。所以不做過多解釋了:
public override string DisplayName { get { if (info != null) { MyControlAttibute uicontrolattibute = (MyControlAttibute)Attribute.GetCustomAttribute(info, typeof(MyControlAttibute)); if (uicontrolattibute != null) return uicontrolattibute.PropertyName; else { return info.Name; } } else return ""; } }
整個代碼如下,里面包含兩個類,你直接拷貝出來就可以使用了:
public delegate void PropertyChanged(object Value); /// <summary> /// 主要是實現中文化屬性顯示 /// </summary> public class PropertyBase : ICustomTypeDescriptor { /// <summary> /// 下面這段代碼來源於:http://www.bluespace.cn/Html/Csdn/2_47/View_4702219.html /// </summary> /// <returns></returns> #region ICustomTypeDescriptor 顯式接口定義 AttributeCollection ICustomTypeDescriptor.GetAttributes() { return TypeDescriptor.GetAttributes(this, true); } string ICustomTypeDescriptor.GetClassName() { return TypeDescriptor.GetClassName(this, true); } string ICustomTypeDescriptor.GetComponentName() { return TypeDescriptor.GetComponentName(this, true); } TypeConverter ICustomTypeDescriptor.GetConverter() { return TypeDescriptor.GetConverter(this, true); } EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); } PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { return null; } object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); } EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { return TypeDescriptor.GetEvents(this, true); } EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { return TypeDescriptor.GetEvents(this, attributes, true); } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]); } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { ArrayList props = new ArrayList(); Type thisType = this.GetType(); PropertyInfo[] pis = thisType.GetProperties(); foreach (PropertyInfo p in pis) { if (p.DeclaringType == thisType || p.PropertyType.ToString() == "System.Drawing.Color") { //判斷屬性是否顯示 BrowsableAttribute Browsable = (BrowsableAttribute)Attribute.GetCustomAttribute(p, typeof(BrowsableAttribute)); if (Browsable != null) { if (Browsable.Browsable == true || p.PropertyType.ToString() == "System.Drawing.Color") { PropertyStub psd = new PropertyStub(p, attributes); props.Add(psd); } } else { PropertyStub psd = new PropertyStub(p, attributes); props.Add(psd); } } } PropertyDescriptor[] propArray = (PropertyDescriptor[])props.ToArray(typeof(PropertyDescriptor)); return new PropertyDescriptorCollection(propArray); } object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { return this; } #endregion } /// <summary> /// 下面這段代碼來源於:http://www.bluespace.cn/Html/Csdn/2_47/View_4702219.html /// </summary> public class PropertyStub : PropertyDescriptor { PropertyInfo info; public PropertyStub(PropertyInfo propertyInfo, Attribute[] attrs) : base(propertyInfo.Name, attrs) { this.info = propertyInfo; } public override Type ComponentType { get { return this.info.ReflectedType; } } public override bool IsReadOnly { get { return this.info.CanWrite == false; } } public override Type PropertyType { get { return this.info.PropertyType; } } public override bool CanResetValue(object component) { return false; } public override object GetValue(object component) { //Console.WriteLine("GetValue: " + component.GetHashCode()); try { return this.info.GetValue(component, null); } catch { return null; } } public override void ResetValue(object component) { } public override void SetValue(object component, object value) { //Console.WriteLine("SetValue: " + component.GetHashCode()); this.info.SetValue(component, value, null); } public override bool ShouldSerializeValue(object component) { return false; } //通過重載下面這個屬性,可以將屬性在PropertyGrid中的顯示設置成中文 public override string DisplayName { get { if (info != null) { MyControlAttibute uicontrolattibute = (MyControlAttibute)Attribute.GetCustomAttribute(info, typeof(MyControlAttibute)); if (uicontrolattibute != null) return uicontrolattibute.PropertyName; else { return info.Name; } } else return ""; } } }
修改后的Form界面如下:
所以我就是直接將創建TextBoxProperty對象的代碼放在了Control_Click中:
//我這里僅僅做TextBox的屬性演示,如果是其它的控件的話,那么你需要設計不同的ControlProperty(比如TextBoxProperty,ComboBoxProperty)
if (sender is TextBox) { this.propertyGrid1.SelectedObject = new TextBoxProperty((TextBox)sender); } else { this.propertyGrid1.SelectedObject = null; }
對於實際的需要來說應該是根據Control的類型,通過工廠模式來創建ControlProperty。
Form的整個代碼如下,窗體如上圖所示:
//在Form中增加幾個Button,分別命名為cmdArrow,cmdLabel,cmdTextBox,cmdComboBox,cmdGroupBox public partial class Form1 : Form { private MouseHook _MouseHook; //我們將所有的已經與具體控件關聯了的UISizeKnob緩存在這個HashTable中 private Hashtable _HashUISizeKnob; //負責控件移動的類 private Hashtable _HashUIMoveKnob; public Form1() { InitializeComponent(); this._MouseHook = new MouseHook(this); this._HashUISizeKnob = new Hashtable(); this._HashUIMoveKnob = new Hashtable(); //為了簡潔明了,我們在ControlAdded中來設置具體控件和UISizeKnob的關聯 this.ControlAdded += new ControlEventHandler(Form1_ControlAdded); } void Form1_ControlAdded(object sender, ControlEventArgs e) { if (!(e.Control is UISizeDot)) { this._HashUISizeKnob.Add(e.Control, new UISizeKnob(e.Control)); this._HashUIMoveKnob.Add(e.Control, new UIMoveKnob(e.Control)); //點擊控件的時候,顯示控件的選擇 e.Control.Click += new EventHandler(Control_Click); } } void Control_Click(object sender, EventArgs e) { //壽險清除已經選擇的控件 foreach (UISizeKnob knob in this._HashUISizeKnob.Values) { knob.ShowUISizeDots(false); } //System.ComponentModel.Design.ISelectionService //System.Drawing.Design.IToolboxService try { ((UISizeKnob)this._HashUISizeKnob[sender]).ShowUISizeDots(true); //我這里僅僅做TextBox的屬性演示,如果是其它的控件的話,那么你需要設計不同的ControlProperty(比如TextBoxProperty,ComboBoxProperty) if (sender is TextBox) { this.propertyGrid1.SelectedObject = new TextBoxProperty((TextBox)sender); } else { this.propertyGrid1.SelectedObject = null; } } catch { } } private void cmdArrow_Click(object sender, EventArgs e) { SettingService.Instance.SelectedToolBoxControl = null; } private void cmdLabel_Click(object sender, EventArgs e) { SettingService.Instance.SelectedToolBoxControl = new Label(); } private void cmdTextBox_Click(object sender, EventArgs e) { SettingService.Instance.SelectedToolBoxControl = new TextBox(); } private void cmdComboBox_Click(object sender, EventArgs e) { SettingService.Instance.SelectedToolBoxControl = new ComboBox(); } private void cmdGroupBox_Click(object sender, EventArgs e) { SettingService.Instance.SelectedToolBoxControl = new GroupBox(); } }
好了,讓PropertyGrid顯示中文屬性的思路和代碼都給了,希望能夠給你點啟發和幫助。
相關文章:
原文鏈接:
C#基礎系列:開發自己的窗體設計器(PropertyGrid顯示中文屬性名)
其他參考文獻:
C#基礎系列:開發自己的窗體設計器(在容器上拖動鼠標增加控件)