顯示具有 Windows Form 標籤的文章。 顯示所有文章
顯示具有 Windows Form 標籤的文章。 顯示所有文章

2017年6月30日 星期五

[Windows Form] Property Grid 使用心得 - Part III(EditorAttribute)


(承接此篇)

EditorAttibute

在該欄位點下"編輯"後,系統會找尋該欄位的是否有EditorAttribute,若有,則根據你指定的Editor型別配置(Allocate)一個新實例(Instance),並呼叫該實例的EditValue函式,最後將EditValue傳回的值Set回該Property

實作方式是宣告一Class,繼承UITypeEditor,並複寫(override)以下函式:

  • GetEditStyle
    • 傳回值型別為UITypeEditorEditStyle,傳回不同值可改變欄位編輯入口樣式
      • Model:該欄位會提供(...)讓使用者點擊,通常要另外提供表單(Form)讓使用者操作
      • DropDown:顧名思義是提供下拉式選單
      • None:沒得編輯
  • EditValue
    • 就是Editor最主要的函式,函式執行完畢後傳回值即為編輯完成的值
    • 慣用起手式:
      • 另外配置(Allocate)一個表單(Form)
      • 帶入(Controls.Add)自己的UserControl,i.e TextBox.Text
      • 顯示該表單,通常使用ShowDialog,使EditValue被Block在此行
      • 在使用者關閉表單後,ShowDialog返回
      • 從剛剛的UserControl取得使用者輸入的值,i.e TextBox.Text
      • 將輸入值傳回(Return)
 
class stringUIEditor :
        UITypeEditor
    {
        public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.Modal;
        }

        public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            Form __form = new Form();       // allocating a form , used to show-up editable facility 
            TextBox __tb = new TextBox();
            __form.Controls.Add(__tb);
            __form.AutoSize=true;
            __form.ShowDialog(); // show-up the form 
            return __tb.Text; //return the text value the user just input.
        }
    }


註:EditValue的輸入引數有許多妙用,可以用來判斷呼叫對象的特性,若要打造比較萬能的Editor就得要深入了解

從(...)呼喚的UIEditor
參考:
程式碼參考

[Windows Form] Property Grid 使用心得 - Part II(TypeConvertor)

再稍微整理一下Property Grid的一些概念:
  1. 顯示的欄位名稱及內容是依SelectedObject內宣告的Properties展開
  2. 顯示樣式則由各Property所擁有的屬性(Attribute)所定義
    1. 值(Value)以外的顯示樣式由以下屬性定義
      1. Display - 控制顯示名稱
      2. Category - 控制群組分類
      3. Description - 控制下方描述
      4. Browsable - 控制是否隱藏
    2. 值(Value)的樣式控制有以下幾方面
      1. Readonly - 控制值可否給使用者輸入
      2. 顯示樣式的控制,由TypeConvertor屬性決定
      3. 編輯器的控制,由EditorAttribute屬性決定
PropertyGrid各顯示樣式控制對應屬性



TypeConvertor
概念上就像是"顯示樣式轉換器",把該欄位的值丟進去對應的Convertor,根據你自訂的邏輯處理後將傳回的值顯示,實作方式為,宣告一Class,繼承TypeConvertor,並複寫
  • ConvertTo
    • 將Object轉為顯示值,通常是String
  • ConvertFrom
    • 將顯示值轉回Object
對於Enum的顯示樣式轉換則需要自訂EnumConvertor,其實EnumConvertor也是繼承自TypeConvertor,尚不清楚此層繼承多了什麼細節,實作方式亦為宣告一Class,並繼承EnumConvertor,複寫ConvertTo/ConvertFrom


 
 class optionEnumConvertor :
        EnumConverter
    {
        /// <summary>
        /// Convert From String To Enum Type
        /// </summary>
        /// <param name="context" />
        /// <param name="culture" />
        /// <param name="value" />
        /// <returns></returns>
        public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            switch (value.ToString())
            {
                case "選項1":
                    return MyEnum.OPTION1;
                case "選項2":
                    return MyEnum.OPTION2;
                default:
                    return null;
            }
        }
        /// <summary>
        /// Convert From Enum Type To String Type
        /// </summary>
        /// <param name="context" />
        /// <param name="culture" />
        /// <param name="value" />
        /// <param name="destinationType" />
        /// <returns></returns>
        public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            switch ((MyEnum)value)
            {
                case MyEnum.OPTION1:
                    return "選項1";
                case MyEnum.OPTION2:
                    return "選項2";
                default:
                    return null;
            }
        }

       public optionEnumConvertor(Type type):base(type)
       {
       }
    }
最後就是在對應的Property上加註TypeConvertor屬性,PropertyGrid就可以根據其指定的TypeConvertor進行樣式轉換了
 
 [DisplayName("想要選什麼?")] // much more human-readable?
        [TypeConverter(typeof(optionEnumConvertor))]
        public MyEnum Property3 { get; set; }

  

註: Enum比較特別的是,可以加註在屬性上,也可以加註在宣告上
 
  [TypeConverter(typeof(optionEnumConvertor))]
    enum MyEnum
    {
        OPTION1,
        OPTION2,
    }


Enum轉String


疑:Code-prettify如何顯示</summary>...不懂
自問自答:XML註解在HTML內當然是要Escaping呀傻傻,把程式貼到這邊Escaping完再貼入<pre>Block中

參考:
程式碼參考

2015年8月10日 星期一

[Windows Form] Property Grid 使用心得 - Part I


 Windows Form 架構中,有個視覺元件叫做Property Grid,她展現的Layout大概就是像設計工具中屬性頁的樣子,可以把物件中標定為Property的欄位顯示出來

這東西有著許多好處:


  • 可以很快速地開發出反映一個Class中的Property的基本UI(在一些不重視視覺效果的應用領域奇方便,例如…自動化設備軟體(說溜嘴了?)
  • 由於是反映Property,所以可在Property中限制參數的變動範圍,提供一種基本的、制式的使用者輸入防呆機制 
  • 搭配Type Convertor還有其他若干屬性設定後,可以以此為框架延伸出看來很有一致性的UI架構,而且只需要少量的程式碼
  • 總體來說我覺得用在不需要很Fancy但講究開發效率的領域中非常的適合(就是在說自己的工作)
  • 最基本用法:
  1. 定義一個Class 裡面有若干個Property,並宣告一個Instance出來
  2. 拉入一個Property Grid到你的Form內,將SelectedObject指向剛剛宣告的Instance
  3. 執行,Property Grid會完全把Class內的Property給反映出來
其中有趣的是,Property Grid會自動限制輸入為變數可接受的型態,宣告為Integer你就不可能輸入abc、宣告為boolean選項就只會有true/false,若宣告為某種Enum,就會以ComboBox的形式提供有限個選項,選項會以Enum項目名稱顯示。
 enum MyEnum
    {
        OPTION1,
        OPTION2,
        OPTION3,
    }

    class MyClass
    {
        public int Property1 
        {
            get
            {
                return __innerVariableInteger;
            }
            set 
            {
                // constrained range
                if (value > 10 && value < 20)
                    __innerVariableInteger = value;
            }
        }
        public string Property2
        {
            get
            {
                return __innerVariableString;
            }
            set
            {
                __innerVariableString = value;
            }
        }
        public MyEnum Property3 { get; set; }

        protected string __innerVariableString = "";
        protected int __innerVariableInteger = 15;

    }
若是型別為Enum則會自動提供ComboBox選項



如果我想限制參數1數值必須大於10小於20,只要在Property中的Set函式內寫一些拒絕輸入的機制,使用Property Grid的使用者就不可能突破此關卡,例如:
public int Property1 
        {
            get
            {
                return __innerVariableInteger;
            }
            set 
            {
                // constrained range
                if (value > 10 && value < 20)
                    __innerVariableInteger = value;
            }
        }
你永遠沒辦法Key-in這範圍以外的值。

  • 使用Attribute標籤補述Property:

若是以最陽春的作法直接反映Property,可以注意到顯示出來的變數名稱都是以程式的宣告變數名為顯示,這樣是很難被使用者接受的(又有底線、名詞又不平實誰看得懂)

又或是某些Property並不想提供給使用者編輯,更甚者不想給人家看見

此時就要以Attribute描述的方式給予Property一些敘述,常用的敘述有:

    • Browsable         // 控制屬性是否可顯示在Grid上
    • ReadOnly          // 控制屬性是否可編輯
    • DisplayName     // 控制屬性顯示出來的名稱(就是用此標籤將艱澀的變數名轉譯成友善的敘述)
    • Categrory          // 控制屬性的分類,這可以做出折疊分類的效果
    • Description        // 控制屬性的描述,就是下面灰色的Bar會帶出的描述
加工一下MyClass:
  class MyClass
    {
        [Description("The input value range is 16-19")] //offered the description which would showed on the bar below
        [Category("Catagory A")]            // offered the folded categoring method
        [DisplayName("Integer Parameter")]  // offered the customised display name
        public int Property1 
        {
            get
            {
                return __innerVariableInteger;
            }
            set 
            {
                // constrained range
                if (value > 10 && value < 20)
                    __innerVariableInteger = value;
            }
        }
        [Browsable(false)]  //wont be showed on PropertyGrid
        public string Property2
        {
            get
            {
                return __innerVariableString;
            }
            set
            {
                __innerVariableString = value;
            }
        }
        [DisplayName("想要選什麼?")] // much more human-readable?
        public MyEnum Property3 { get; set; }

        protected string __innerVariableString = "";
        protected int __innerVariableInteger = 15;

    }

  • Property1顯示名稱改變嚕,而且有了分類
  • Property2隱藏嚕
  • Property3更讓人看得懂意義

咦,但是選項還是以程式語言的形式呈現阿?Boolean選項是true/false,怎麼就不能叫做 "是/否",Enum選項是英文,阿公阿罵哪看得懂,而且還帶了宅味在其中,這該怎麼去除呢?接著就要應用TypeConvertor,將選項做適當的轉譯,是退散宅味的好秘方,字字句句都可以成為很口語與詳實的描述。(之後再寫…)

後記:

一切的入手是從這篇開始的,非常感謝
最近發現這篇做了更多進階的介紹,讚
屬性化程式設計概念
關於TypeConvertor的MSDN原始說明