使用C#盡可能以最少的代碼完成多層次的軟件配置(基於PropertyGrid控件)


1、前言

現在搜索PropertyGuid,發現的一些文檔,特別是在百度文庫中,都是互相抄,我發現最初的文檔在這兒http://msdn.microsoft.com/en-us/library/aa302326.aspx。這里面也有一些錯誤,看的時候注意辨別一下。

2、目標:以最少的代碼,實現在GUI中配置下列結構的對象。

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private Database database = new Database();
    }
}

這個database對象就是待配置的對象,下面是Database類的結構:

public class Database
{
    public string Name { get; set; }
    public string Password { get; set; }
    public string InitializeCommand { get; set; }
    public List<Table> Tables { get; set; }
}

下面是Table類的結構:

public class Table
{
    public string Name { get; set; }
    public List<Column> Columns { get; set; }
    public string Comment { get; set; }
}

下面是Column類的結構:

public class Column
{
    public string Name { get; set; }
    public SupportedType Type { get; set; }
    public string Comment { get; set; }
    public bool IsMainKey { get; set; }
    public bool NotNull { get; set; }
    public bool IsAutoIncrement { get; set; }
    public bool IsIndex { get; set; }
}

下面是SupportedType的結構:

public enum SupportedType
{
    Boolean,
    DateTime,
    Decimal,
    Double,
    Int64,
    Int32,
    Int16,
    SByte,
    Single,
    String,
    UInt64,
    UInt32,
    UInt16,
    Byte
}

這里的例子用了大家都熟悉的數據庫結構的模型,方便理解。

這里完成之后,向界面拖入Property控件,並把database字段賦給控件的SelectedObject屬性,運行一下,得到以下效果:

點擊...,得到以下效果:

點擊“添加”,再點擊...,得到以下效果:

可以注意到我並沒有寫多少代碼,就完成的大部分的效果,和Visual Studio的屬性窗口十分類似。但是,這個不美觀,也有BUG,下面開始改善一下這些效果。

首先介紹一些在使用PropertyGrid控件時常用的Attribute(至於Attribute是什么,自行搜索之):

1)在屬性框被點擊后,光標自動跳到一個屬性的框中:DefaultProperty,附着在類上,參數是屬性的名稱;

2)當值改變時,變成粗體的效果:DefaultValue,附着在屬性上,參數是非粗體時的值;(注意這個不會決定默認值,這個決定粗體效果。)

3)對屬性分類:Category,附着在屬性上,參數是類別名稱;

4)只讀,不可編寫,變灰色:ReadOnly,附着在屬性上,參數是false或true,不加此Attribute的效果是此值為false的效果;

5)顯示的名稱,設置后不再顯示為屬性的名稱:DisplayName,附着在屬性上,參數是顯示的名稱;

6)描述信息,在描述框里顯示:Description,附着在屬性上,參數是描述信息;

7)是否顯示:Browsable,附着在屬性上,參數為false或true,不加此Attribute的效果是此值為true的效果。

以下是一些經驗:

8)每個項都寫成自動屬性的形式;

9)在構造函數中對每一個值都盡可能賦值,這個也決定了默認值。沒有這一步,可能造成設置后無法保存的BUG;

一般:string類型可以初始化為string.empty,bool類型不必初始化,List先判斷是否為null,如果為null,初始化一下。

10)可以被正確識別的對象:各種值類型、String、數組、DateTime、TimeSpan、Point、Size、Font、Color、枚舉類型、Image、Bitmap、Metafile、Icon、Cursor、List<T>。

下面是優化后的代碼,都在這兒了:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        propertyGrid1.SelectedObject = database;
    }
    private readonly Database database = new Database();
}
[DefaultProperty("Tables")]
public class Database
{
    public Database()
    {
        Name = "NewDatabase";
        Password = string.Empty;
        InitializeCommand = string.Empty;
        if (Tables == null)
        {
            Tables = new List<Table>();
        }
    }

    [Category("屬性"),
        DisplayName("數據庫名稱")]
    public string Name { get; set; }

    [Category("屬性"),
        DisplayName("數據庫密碼")]
    public string Password { get; set; }

    [Category("屬性"),
        DisplayName("初始化語句")]
    public string InitializeCommand { get; set; }

    [Category("數據表設置"),
        DisplayName("數據表集合")]
    public List<Table> Tables { get; set; }
}
[DefaultProperty("Columns")]
public class Table
{
    public Table()
    {
        Name = "NewTable";
        Comment = string.Empty;
        if (Columns == null)
        {
            Columns = new List<Column>();
        }
    }

    [Category("屬性"),
        DisplayName("數據表名稱")]
    public string Name { get; set; }

    [Category("數據列設置"),
        DisplayName("數據列集合")]
    public List<Column> Columns { get; set; }

    [Category("屬性"),
        DisplayName("備注")]
    public string Comment { get; set; }
}
[DefaultProperty("Name")]
public class Column
{
    public Column()
    {
        Name = "NewColumn";
        Type = SupportedType.Int32;
        Comment = string.Empty;
        NotNull = true;
    }

    [DisplayName("列名"),
        Category("屬性")]
    public string Name { get; set; }

    [DisplayName("類型"),
        Category("屬性")]
    public SupportedType Type { get; set; }

    [DisplayName("注釋"),
        Category("屬性")]
    public string Comment { get; set; }

    [DisplayName("是否是主鍵"),
        DefaultValue(false),
        Category("屬性")]
    public bool IsMainKey { get; set; }

    [DisplayName("是否非空"),
        DefaultValue(true),
        Category("屬性")]
    public bool NotNull { get; set; }

    [DisplayName("是否自動增長"),
        DefaultValue(false),
        Category("屬性")]
    public bool IsAutoIncrement { get; set; }

    [DisplayName("是否是索引"),
        DefaultValue(false),
        Category("屬性")]
    public bool IsIndex { get; set; }
}

下面是優化后的效果:

 

3、遺留的問題

1)點擊右上角的問號,沒反應;

2)如果不是最頂層,就沒有Description欄;

3)除了之前提到的一些類型,其它的類都不能自動使用上。

上述問題需要增加自定義的代碼來解決,不符合本文“使用盡可能少的代碼”的主題,相關的擴展心得會在之后的博文中表述出來。

4、更新

PropertyGrid is the exact same control as used in the Visual Studio Properties window. Where it is pretty useful to edit the properties of various Winforms class objects that you use in the designer. That's because Microsoft did a lot of work to provide the classes that convert a class object to a string. A custom TypeConverter class, like the ColorConverter class. And a custom editor for the property, were desired. A custom UITypeEditor, like the AnchorEditor class, the one that gives the Anchor property the special dropdown that lets you click the bars.

But when you display your own class object in a PropertyGrid then those goodies are missing. Unless you create your own TypeConverter and UITypeEditor classes. Right now you are just getting the default ones. Which are not very useful, as you noted in your question.

Beware of the cost of writing those classes, they have a fairly steep learning curve. Which is very rarely worth the hassle, especially since a PropertyGrid isn't exactly the friendliest user interface for a non-programmer. Just creating your own Form class is almost always the better bang for the buck.

由此,我應該不會在此方向繼續研究了。

5、更新

在類中override ToString方法可以解決在listbox中顯示錯誤的問題。


免責聲明!

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



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