none
デザイナーで、オブジェクトをプルダウンで選択可能にしたい RRS feed

  • 質問

  • C# 2017
    .NET Framework 4.5
    WinForms

    コントロールのプロパティに、プルダウンの選択肢を設け、オブジェクトを設定したいです。
    デザイナー時は、選択肢は文字列で表示され、選択された実態自体はオブジェクト、というのがしたいです。

    TypeConverterでそれらしいのを作ればできそうなのかなとは思っているのですが、いかんせんTypeConverterの書き方がイマイチわかっておらず、どう実装すると上記のことが実現できるのかわからず、教えてください。

    以下、書いてるコードになります。

    【コントロールクラスのプロパティ宣言部】

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public List<FileSelectionType> SelectionType { get; } = new List<FileSelectionType>();
            
    [TypeConverter(typeof(DefaultFileTypeConverter))]
    public FileSelectionType DefaultFileType { get; set; }


    【TypeConverterクラス】
        public class DefaultFileTypeConverter : TypeConverter
        {
            public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
            {
                // ドロップダウンで選択可能にする
                return true;
            }
    
            public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
            {
                dynamic fsb = context.Instance;
    
                //List<string> choices = new List<string>();
                List<FileSelectionType> choices = new List<FileSelectionType>();
                foreach (FileSelectionType fileType in fsb.SelectionType)
                {
                    //choices.Add(fileType.Name + " (" + fileType.Pattern + ")");
                    choices.Add(fileType);
                }
    
                return new StandardValuesCollection(choices);
            }
    
            public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
            {
                // 選択肢以外は選択不可
                return true;
            }
        }
    【FileSelectionTypeクラス】
    public class FileSelectionType
    {
        public const string AllFileTypePattern = "*.*";
        public const string AllFileTypeName = "すべてのファイル";
        private string pattern = "*.*";
        private string name = "ファイルタイプ名";
    
        [Browsable(true)]
        [Category("ファイルタイプ")]
        public string Pattern
        {
            get { return this.pattern; }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentException("パターンに空は指定できません。");
                }
                this.pattern = value;
            }
        }
    
        [Browsable(true)]
        [Category("ファイルタイプ")]
        public string Name
        {
            get { return this.name; }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentException("名前に空は指定できません。");
                }
                this.name = value;
            }
        }
    
        public new string ToString()
        {
            return this.Name + " (" + this.Pattern + ")";
        }
    }



    2017年12月15日 5:24

回答

  • こんな?

    namespace WindowsFormsApplication1
    {
        using System;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.Drawing;
        using System.Globalization;
        using System.Linq;
        using System.Windows.Forms;
    
        //特定の型に依存させたくないならインターフェースを
        interface IFileTypes
        {
            List<FileSelectionType> SelectionTypes { get; }
            FileSelectionType DefaultFileType { get; set; }
        }
    
        class TestControl : UserControl, IFileTypes
        {
            public TestControl()
            {
                this.BackColor = Color.Green;
            }
    
            [System.ComponentModel.Category("_TEST_")]
            [System.ComponentModel.RefreshProperties(RefreshProperties.All)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
            public List<FileSelectionType> SelectionTypes { get; } = new List<FileSelectionType>();
    
    
            [System.ComponentModel.Category("_TEST_")]
            [System.ComponentModel.TypeConverter(typeof(DefaultFileTypeConverter))]
            public FileSelectionType DefaultFileType
            {
                get
                {
                    if (_DefaultFileType != null && !SelectionTypes.Contains(_DefaultFileType))
                    {
    
                        //SelectionTypesをObservableCollectionなどで監視していないなら
                        //一覧に存在しない時はnullにしてしまう
                        //デザイナでシリアル化したものを復元した場合も、同じになるものに差し替える
                        _DefaultFileType = SelectionTypes.FirstOrDefault(_ => _.ToString() == _DefaultFileType.ToString());
                    }
                    return _DefaultFileType;
                }
                set
                {
                    _DefaultFileType = value;
                }
            }
            public FileSelectionType _DefaultFileType;
        }
    
        public class DefaultFileTypeConverter : TypeConverter //System.ComponentModel.ExpandableObjectConverter//変更されたことがわかりやすいようにExpandableObjectConverterにしてみる
        {
            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                if (sourceType == typeof(string))
                {
                    //文字列から変換可能
                    return true;
                }
                return base.CanConvertFrom(context, sourceType);
            }
            public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
            {
                if (value is string)
                {
                    //一覧の文字列と一致するオブジェクトを探して返す
                    //同じ文字列になるパターンの場合は同じものとみなす
                    string s = (string)value;
                    var ift = context.Instance as IFileTypes;
                    if (ift != null && ift.SelectionTypes != null)
                    {
                        FileSelectionType fst = ift.SelectionTypes.FirstOrDefault(_ => _.ToString() == s);
                        if (fst != null)
                        {
                            return fst;
                        }
                    }
                    return null;
                }
                return base.ConvertFrom(context, culture, value);
            }
    
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                if (destinationType == typeof(string))
                {
                    //文字列に変換可能
                    return true;
                }
                return base.CanConvertTo(context, destinationType);
            }
            public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
            {
                if (value is FileSelectionType && destinationType == typeof(string))
                {
                    //文字列化
                    var fst = (FileSelectionType)value;
                    return fst.ToString();
                }
                else
                {
                    return base.ConvertTo(context, culture, value, destinationType);
                }
            }
    
            public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
            {
                // ドロップダウンで選択可能にする
                return true;
            }
    
            public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
            {
                //System.Diagnostics.Debugger.Launch();//デバッグしたいばあい
    
                IFileTypes fsb = context.Instance as IFileTypes;
                List<FileSelectionType> choices = new List<FileSelectionType>();
                foreach (FileSelectionType fileType in fsb.SelectionTypes)
                {
                    choices.Add(fileType);
                }
                return new StandardValuesCollection(choices);
            }
    
            public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
            {
                // 選択肢以外は選択不可
                return true;
            }
        }
    
        public class FileSelectionType
        {
            public const string AllFileTypePattern = "*.*";
            public const string AllFileTypeName = "すべてのファイル";
            private string pattern = "*.*";
            private string name = "ファイルタイプ名";
    
            [System.ComponentModel.NotifyParentProperty(true)]//ToString()の文字列がPropertyGrid反映されるように
            [Browsable(true)]
            [Category("ファイルタイプ")]
            public string Pattern
            {
                get { return this.pattern; }
                set
                {
                    if (string.IsNullOrWhiteSpace(value))
                    {
                        throw new ArgumentException("パターンに空は指定できません。");
                    }
                    this.pattern = value;
                }
            }
    
            [System.ComponentModel.NotifyParentProperty(true)]//ToString()の文字列がPropertyGridで反映されるように
            [Browsable(true)]
            [Category("ファイルタイプ")]
            public string Name
            {
                get { return this.name; }
                set
                {
                    if (string.IsNullOrWhiteSpace(value))
                    {
                        throw new ArgumentException("名前に空は指定できません。");
                    }
                    this.name = value;
                }
            }
    
            public new string ToString()
            {
                return this.Name + " (" + this.Pattern + ")";
            }
        }
    }
    

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

    • 回答としてマーク takiru 2017年12月25日 6:12
    2017年12月15日 8:59

すべての返信

  • こんな?

    namespace WindowsFormsApplication1
    {
        using System;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.Drawing;
        using System.Globalization;
        using System.Linq;
        using System.Windows.Forms;
    
        //特定の型に依存させたくないならインターフェースを
        interface IFileTypes
        {
            List<FileSelectionType> SelectionTypes { get; }
            FileSelectionType DefaultFileType { get; set; }
        }
    
        class TestControl : UserControl, IFileTypes
        {
            public TestControl()
            {
                this.BackColor = Color.Green;
            }
    
            [System.ComponentModel.Category("_TEST_")]
            [System.ComponentModel.RefreshProperties(RefreshProperties.All)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
            public List<FileSelectionType> SelectionTypes { get; } = new List<FileSelectionType>();
    
    
            [System.ComponentModel.Category("_TEST_")]
            [System.ComponentModel.TypeConverter(typeof(DefaultFileTypeConverter))]
            public FileSelectionType DefaultFileType
            {
                get
                {
                    if (_DefaultFileType != null && !SelectionTypes.Contains(_DefaultFileType))
                    {
    
                        //SelectionTypesをObservableCollectionなどで監視していないなら
                        //一覧に存在しない時はnullにしてしまう
                        //デザイナでシリアル化したものを復元した場合も、同じになるものに差し替える
                        _DefaultFileType = SelectionTypes.FirstOrDefault(_ => _.ToString() == _DefaultFileType.ToString());
                    }
                    return _DefaultFileType;
                }
                set
                {
                    _DefaultFileType = value;
                }
            }
            public FileSelectionType _DefaultFileType;
        }
    
        public class DefaultFileTypeConverter : TypeConverter //System.ComponentModel.ExpandableObjectConverter//変更されたことがわかりやすいようにExpandableObjectConverterにしてみる
        {
            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                if (sourceType == typeof(string))
                {
                    //文字列から変換可能
                    return true;
                }
                return base.CanConvertFrom(context, sourceType);
            }
            public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
            {
                if (value is string)
                {
                    //一覧の文字列と一致するオブジェクトを探して返す
                    //同じ文字列になるパターンの場合は同じものとみなす
                    string s = (string)value;
                    var ift = context.Instance as IFileTypes;
                    if (ift != null && ift.SelectionTypes != null)
                    {
                        FileSelectionType fst = ift.SelectionTypes.FirstOrDefault(_ => _.ToString() == s);
                        if (fst != null)
                        {
                            return fst;
                        }
                    }
                    return null;
                }
                return base.ConvertFrom(context, culture, value);
            }
    
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                if (destinationType == typeof(string))
                {
                    //文字列に変換可能
                    return true;
                }
                return base.CanConvertTo(context, destinationType);
            }
            public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
            {
                if (value is FileSelectionType && destinationType == typeof(string))
                {
                    //文字列化
                    var fst = (FileSelectionType)value;
                    return fst.ToString();
                }
                else
                {
                    return base.ConvertTo(context, culture, value, destinationType);
                }
            }
    
            public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
            {
                // ドロップダウンで選択可能にする
                return true;
            }
    
            public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
            {
                //System.Diagnostics.Debugger.Launch();//デバッグしたいばあい
    
                IFileTypes fsb = context.Instance as IFileTypes;
                List<FileSelectionType> choices = new List<FileSelectionType>();
                foreach (FileSelectionType fileType in fsb.SelectionTypes)
                {
                    choices.Add(fileType);
                }
                return new StandardValuesCollection(choices);
            }
    
            public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
            {
                // 選択肢以外は選択不可
                return true;
            }
        }
    
        public class FileSelectionType
        {
            public const string AllFileTypePattern = "*.*";
            public const string AllFileTypeName = "すべてのファイル";
            private string pattern = "*.*";
            private string name = "ファイルタイプ名";
    
            [System.ComponentModel.NotifyParentProperty(true)]//ToString()の文字列がPropertyGrid反映されるように
            [Browsable(true)]
            [Category("ファイルタイプ")]
            public string Pattern
            {
                get { return this.pattern; }
                set
                {
                    if (string.IsNullOrWhiteSpace(value))
                    {
                        throw new ArgumentException("パターンに空は指定できません。");
                    }
                    this.pattern = value;
                }
            }
    
            [System.ComponentModel.NotifyParentProperty(true)]//ToString()の文字列がPropertyGridで反映されるように
            [Browsable(true)]
            [Category("ファイルタイプ")]
            public string Name
            {
                get { return this.name; }
                set
                {
                    if (string.IsNullOrWhiteSpace(value))
                    {
                        throw new ArgumentException("名前に空は指定できません。");
                    }
                    this.name = value;
                }
            }
    
            public new string ToString()
            {
                return this.Name + " (" + this.Pattern + ")";
            }
        }
    }
    

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

    • 回答としてマーク takiru 2017年12月25日 6:12
    2017年12月15日 8:59
  • ありがとうござます!できました。
    インターフェースにはしようかと思っていたんですが、DefaultFileTypeが既に選択されている状態でSelectionTypesから削除された時というのは完全に頭から抜け落ちていました。
    詳細までコードしてくださりありがとうございます。

    TypeConverterの実装自体も、すごくなんとなーくわかりそうな気がします。
    ちょっとこれはよく使うことになりそうなので、これから細かく何しているかを調査しようかと思います。

    しかし、[System.ComponentModel.RefreshProperties(RefreshProperties.All)]についてだけ見当がつきませんでした。
    ここの部分だけ、なぜに必要なのか、ご教示いただけませんでしょうか。

    2017年12月15日 9:52