C#基礎系列:開發自己的窗體設計器(PropertyGrid顯示中文屬性名)


既然是一個窗體設計器,那就應該能夠設置控件的屬性,設置屬性最好的當然是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(反射以及AttributeORM中的應用)》。這里對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#基礎系列:開發自己的窗體設計器(總綱)

C#基礎系列:開發自己的窗體設計器(在容器上拖動鼠標增加控件)

C#基礎系列:開發自己的窗體設計器(實現控件的選擇)

C#基礎系列:開發自己的窗體設計器(實現控件的拖動)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM