none
オブジェクトのプロパティをPropertyGrid上で展開して設定を行いたい RRS feed

  • 質問

  • オブジェクトのプロパティを、デザイナーのPropertyGrid上で展開して設定を行いたいと思っています。
    具体的にはSystem.Drawing.Drawing2D. ColorBlendです。

    自作プロパティだとExpandableObjectConverterを利用してプロパティの展開、設定が出来ますが、
    ColorBlend(に限らず)は、アクセサを用意しただけではPropertyGrid上からは操作することができません。

    public class ExLabel : Label
    {
        private ColorBlend _colorBlend = new ColorBlend();
        public ColorBlend ColorBlend
        {
            get { return _colorBlend; }
            set { _colorBlend = value; }
        }
    }

    ColorBlendをラッピングする自作プロパティを用意すれば済むと思いますが、全メソッド・プロパティを
    いちいちラッピングするのが果てしなく面倒です。

    簡単に、ColorBlendをプロパティとしてPropertyGrid上で操作できるようにする方法はありませんでしょうか?

    • 編集済み takiru 2013年4月24日 5:03
    2013年4月24日 4:57

回答

  • こんな?

    [TypeConverter(typeof(ExpandableSubObjectConverter))]
    public class ExLabel
    {
        public ExLabel()
        {
            ColorBlend = new System.Drawing.Drawing2D.ColorBlend();
            Test = new TestClass();
        }
    
        public ColorBlend ColorBlend{get;set;}
        public TestClass Test { get; set; }
    }
        
    public class TestClass
    {
        public string Text { get; set; }
        public DateTime Time { get; set; }
    }
    
    /// <summary>
    /// クラスに含まれるプロパティで独自のTypeConverterを持っていないプロパティをExpandableObjectConverが指定されているように偽装するConverter
    /// </summary>
    class ExpandableSubObjectConverter : ExpandableObjectConverter 
    {
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            var ps= base.GetProperties(context, value, attributes);
            List<PropertyDescriptor> list = new List<PropertyDescriptor>();
            foreach (PropertyDescriptor pd in ps)
            {
                //if (pd.PropertyType != typeof(ColorBlend))
                //{
                //    list.Add(pd);
                //}
                //else
                //{
                    TypeConverterAttribute tca = null;
                    foreach (Attribute attr in pd.Attributes)
                    {
                        tca = attr as TypeConverterAttribute;
                        if (tca != null)
                        {
                            break;
                        }
                    }
                    if (tca == null)
                    {
                        list.Add(new ExpandPropertyDescriptor(pd));
                    }
                    else
                    {
                        list.Add(pd);
                    }
                //}
            }
    
            PropertyDescriptorCollection retval = new PropertyDescriptorCollection(list.ToArray());
            return retval;
        }
    
        public override bool GetPropertiesSupported(ITypeDescriptorContext context)
        {
            var b= base.GetPropertiesSupported(context);
    
                
            return true;
        }
    
        /// <summary>
        /// TypeConverterがデフォルトのTyepConverterの場合にExpandableObjectConverterに差し替えるPropertyDescriptor
        /// </summary>
        class ExpandPropertyDescriptor : PropertyDescriptor
        {
            /// <summary></summary>
            /// <param name="pd"></param>
            /// <param name="isExpandSubProperties">さらに下位のプロパティも展開するようにするならtrueにする</param>
            public ExpandPropertyDescriptor(PropertyDescriptor pd,bool isExpandSubProperties=true)
                : base(pd)
            {
                baseDescriptor = pd;
    
                if (isExpandSubProperties)
                {
                    List<Attribute> list = new List<Attribute>(base.AttributeArray);
                    TypeConverterAttribute tca = new TypeConverterAttribute(typeof(ExpandableSubObjectConverter));
                    list.Add(tca);
                    base.AttributeArray = list.ToArray();
                }
            }
            private static ExpandableObjectConverter expandable = new ExpandableObjectConverter();
            private PropertyDescriptor baseDescriptor;
    
            public override TypeConverter Converter
            {
                get
                {
                        
                    var converter = base.Converter;
                    if (converter.GetType() == typeof(TypeConverter))
                    {
                        return expandable;
                    }
                    else
                    {
                        return converter;
                    }
                }
            }
    
            public override bool CanResetValue(object component)
            {
                return baseDescriptor.CanResetValue(component);
            }
    
            public override Type ComponentType
            {
                get { return baseDescriptor.ComponentType; }
            }
    
            public override object GetValue(object component)
            {
                return baseDescriptor.GetValue(component);
            }
    
            public override bool IsReadOnly
            {
                get { return baseDescriptor.IsReadOnly; }
            }
    
            public override Type PropertyType
            {
                get { return baseDescriptor.PropertyType; }
            }
    
            public override void ResetValue(object component)
            {
                baseDescriptor.ResetValue(component);
            }
    
            public override void SetValue(object component, object value)
            {
                baseDescriptor.SetValue(component, value);
            }
    
            public override bool ShouldSerializeValue(object component)
            {
                return baseDescriptor.ShouldSerializeValue(component);
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク takiru 2013年4月25日 2:48
    2013年4月24日 8:29

すべての返信

  • PropertyGridに出したいというお話しであれば、下記で良いと思います。

    public class ExLabel : Label
    {
    	private ColorBlend _colorBlend = new ColorBlend();
    
    	[Browsable(true), Category(分類), DefaultValue(デフォルト値), Description(説明)]
    	public ColorBlend ColorBlend
    	{
    		get { return _colorBlend; }
    		set { _colorBlend = value; }
    	}
    }
    ※分類・デフォルト値・説明の部分は適宜設定して下さい。
    2013年4月24日 5:48
  • ご回答ありがとうございます。

    教えていただいた方法だと、PropertyGridに表示されるだけで、ColorBlendの内容を
    デザイナー上から編集することができません。



    デザイナー上から、ColorBlend.Colorsプロパティ、Positionsプロパティなどを利用者に操作を
    委ねた形で編集出来るようにしたいのですが。

    2013年4月24日 6:41
  • こんな?

    [TypeConverter(typeof(ExpandableSubObjectConverter))]
    public class ExLabel
    {
        public ExLabel()
        {
            ColorBlend = new System.Drawing.Drawing2D.ColorBlend();
            Test = new TestClass();
        }
    
        public ColorBlend ColorBlend{get;set;}
        public TestClass Test { get; set; }
    }
        
    public class TestClass
    {
        public string Text { get; set; }
        public DateTime Time { get; set; }
    }
    
    /// <summary>
    /// クラスに含まれるプロパティで独自のTypeConverterを持っていないプロパティをExpandableObjectConverが指定されているように偽装するConverter
    /// </summary>
    class ExpandableSubObjectConverter : ExpandableObjectConverter 
    {
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            var ps= base.GetProperties(context, value, attributes);
            List<PropertyDescriptor> list = new List<PropertyDescriptor>();
            foreach (PropertyDescriptor pd in ps)
            {
                //if (pd.PropertyType != typeof(ColorBlend))
                //{
                //    list.Add(pd);
                //}
                //else
                //{
                    TypeConverterAttribute tca = null;
                    foreach (Attribute attr in pd.Attributes)
                    {
                        tca = attr as TypeConverterAttribute;
                        if (tca != null)
                        {
                            break;
                        }
                    }
                    if (tca == null)
                    {
                        list.Add(new ExpandPropertyDescriptor(pd));
                    }
                    else
                    {
                        list.Add(pd);
                    }
                //}
            }
    
            PropertyDescriptorCollection retval = new PropertyDescriptorCollection(list.ToArray());
            return retval;
        }
    
        public override bool GetPropertiesSupported(ITypeDescriptorContext context)
        {
            var b= base.GetPropertiesSupported(context);
    
                
            return true;
        }
    
        /// <summary>
        /// TypeConverterがデフォルトのTyepConverterの場合にExpandableObjectConverterに差し替えるPropertyDescriptor
        /// </summary>
        class ExpandPropertyDescriptor : PropertyDescriptor
        {
            /// <summary></summary>
            /// <param name="pd"></param>
            /// <param name="isExpandSubProperties">さらに下位のプロパティも展開するようにするならtrueにする</param>
            public ExpandPropertyDescriptor(PropertyDescriptor pd,bool isExpandSubProperties=true)
                : base(pd)
            {
                baseDescriptor = pd;
    
                if (isExpandSubProperties)
                {
                    List<Attribute> list = new List<Attribute>(base.AttributeArray);
                    TypeConverterAttribute tca = new TypeConverterAttribute(typeof(ExpandableSubObjectConverter));
                    list.Add(tca);
                    base.AttributeArray = list.ToArray();
                }
            }
            private static ExpandableObjectConverter expandable = new ExpandableObjectConverter();
            private PropertyDescriptor baseDescriptor;
    
            public override TypeConverter Converter
            {
                get
                {
                        
                    var converter = base.Converter;
                    if (converter.GetType() == typeof(TypeConverter))
                    {
                        return expandable;
                    }
                    else
                    {
                        return converter;
                    }
                }
            }
    
            public override bool CanResetValue(object component)
            {
                return baseDescriptor.CanResetValue(component);
            }
    
            public override Type ComponentType
            {
                get { return baseDescriptor.ComponentType; }
            }
    
            public override object GetValue(object component)
            {
                return baseDescriptor.GetValue(component);
            }
    
            public override bool IsReadOnly
            {
                get { return baseDescriptor.IsReadOnly; }
            }
    
            public override Type PropertyType
            {
                get { return baseDescriptor.PropertyType; }
            }
    
            public override void ResetValue(object component)
            {
                baseDescriptor.ResetValue(component);
            }
    
            public override void SetValue(object component, object value)
            {
                baseDescriptor.SetValue(component, value);
            }
    
            public override bool ShouldSerializeValue(object component)
            {
                return baseDescriptor.ShouldSerializeValue(component);
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク takiru 2013年4月25日 2:48
    2013年4月24日 8:29
  • ご回答ありがとうございます。

    おかげさまで実現することができました。
    TypeConverterあたりになると理解が及ばず全く思いついたりしないので、大変助かりました。
    VS2010だと動きましたが、VS2005に合わせたコードに直して試してみたら、コンパイルエラーにはならないものの、
    なぜか上手く展開することができませんでした。

    バージョン違いがあからさまに影響を受けたシーンを初めて見たので、この機会に、VS2005で作成途中のものを
    VS2010で作りかえようかと思います。

    追記:
    勘違いで、VS2005でも動いてました(^_^;)
    • 編集済み takiru 2013年4月25日 6:02
    2013年4月25日 2:48