none
TextBoxを貼り付けた時にリストに値を1件追加したい RRS feed

  • 質問

  • Visual Studio 2017
    .NET Framework 4.5

    TextBoxを継承して、ExTextBoxを作っています。
    List<T>クラスのプロパティAを用意しています。
    ExTextBoxを貼り付けた直後では、プロパティAには1件登録されており、それをデザイナー上で編集して、デザイナーを閉じ、また開いても、編集した状態に復元されるコードが実現できません。

    感覚的には、結局designerファイルが編集状態に復元するわけなので、何らかのコードの書き方をして、List<T>のインスタンス化および1件登録をdesignerファイル上で行う実装が行えればいいのではと思っているのですが、皆目見当がつきません。

    どうすると実装できるでしょうか?

    2017年4月10日 9:54

回答

  • 別案。
    ExTextBox 側にメソッドを用意すれば、少しだけかんたんな形になるか。

    [Designer(typeof(ExTextBoxDesigner))]
    public class ExTextBox<T> : TextBox
        where T : new()
    {
        private List<T> _list;
    
        public ExTextBox()
        {
            _list = new List<T>();
        }
    
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<T> List { get; set; }
    
        internal void InitializeForNewComponent()
        {
            Text = string.Empty;
            _list.Add(new T());
        }
    }
    
    
    // System.Designの参照が必要です
    public class ExTextBoxDesigner : ControlDesigner
    {
        public override void InitializeNewComponent(IDictionary defaultValues)
        {
            base.InitializeNewComponent(defaultValues);
    
            var targetControl = Control;
            if (targetControl == null) return;
    
            var type = targetControl.GetType();
            var exTextBoxType = GetExTextBoxType(type);
            if (exTextBoxType == null) return;
    
            ((dynamic)targetControl).InitializeForNewComponent();
        }
    
        private static Type GetExTextBoxType(Type type)
        {
            Type current = type;
            while (current != null)
            {
                if (current.IsGenericType && current.GetGenericTypeDefinition() == typeof(ExTextBox<>)) return current;
                current = current.BaseType;
            }
            return null;
        }
    }

    • 回答としてマーク takiru 2017年4月11日 0:25
    2017年4月10日 13:57
    モデレータ

すべての返信

  • 何を作っているのですか? Windows Forms? WPF? ASP.NET Web Forms? その他?
    2017年4月10日 10:25
  • Forms なら DefaultValueAttribute クラスを使えば、デザイナで既定値を設定可能です。使い方については、以下の記事がわかりやすいと思います。

    デフォルト値の設定


    本フォーラムは、ユーザー(開発者)同士で情報交換を行うためのコミュニティです。初めて利用される方は、以下のアナウンスをご覧ください。 https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?

    2017年4月10日 10:38
    モデレータ
  • 失礼しました。 Windowsフォームアプリです。
    2017年4月10日 10:53
  • DefaultValueの存在は把握しておりますが、List〈T〉の型を持つプロパティに既定値を設定することはできるのでしょうか?
    Tは独自で作成したクラスになります。

    -- 追加

    現在、以下のようなコードです。
    今のところ、Hogeクラスに、各プロパティの値を引数で持つコンストラクタは用意していません。
    この時、Apropの既定値もしくは初期値として1件存在させ、デザイナーで編集後(既定値もしくは初期値の1件を削除することもあり)にも正しくList内が復元されることをしたいです。

    public class ExTextBox : TextBox {
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<Hoge> Aprop { get; set; }
    }
    
    public class Hoge {
        [Browsable(true)]
        public string Pattern { get; set; }
    
        [Browsable(true)]
        public string Name { get; set; }
    }


    • 編集済み takiru 2017年4月10日 11:23
    2017年4月10日 11:01
  • こんな?

    public class ExTextBox<T> : System.Windows.Forms.TextBox where T : new()
    {
        public ExTextBox()
        {
            this.List = new List<T>();
        }
    
        private bool IsComponentPaste
        {
            get
            {
                return Environment.StackTrace.Contains("System.Windows.Forms.Design.ControlDesigner.InitializeNewComponent");
            }
        }
    
        protected override void OnParentChanged(EventArgs e)
        {
            if (this.Parent != null && this.List != null)
            {
                if (this.IsComponentPaste)
                {
                    this.List.Add(new T());
                }
            }
    
            base.OnParentChanged(e);
        }
    
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<T> List { get; set; }
    }
    
    #region テスト
    
    public class ExTextBox_int : ExTextBox<int>
    {
    }
    
    class ExTextBox_Item : ExTextBox<Item>
    {
    }
    
    class Item
    {
        public Item()
        {
            this.Text = DateTime.Now.ToString("HH:mm:ss");
        }
        public string Text { get; set; }
    }
    
    #endregion
    #以前の質問のコンポーネントを配置した瞬間を判断したいを応用

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

    • 編集済み gekkaMVP 2017年4月10日 11:21
    2017年4月10日 11:19
  • ご回答ありがとうございます。

    確かに以前にスタックトレースで判断するご回答をいただいており、実はその方法も以前のご回答から確認済みでした。

    が、スタックトレースのメソッドの完全修飾名?を決め打ちで実装する点に些かの疑問を抱いております。
    既定値だろうが初期値だろうが構わないのですが、それ(スタックトレースによる判断)をしてあげないと実現できないものなのでしょうか?
    そういうことしたい時は大体いつでもそういうもんだ、とご回答いただけたらそれまでなんですが(笑)
    2017年4月10日 11:29
  • ツールボックスから配置したときの初期値設定を制御したいという話ですよね?
    なら、DesignerAttribute が正攻法に思えます。

    以下は無理矢理感あるコードですが…。
    TextBoxBaseDesigner はもっと機能を提供しているようなので、この Designer クラスを使った場合、デザイナ上での動きが TextBox と違う感じになります。それらの振る舞いも必要ならもっとメソッドをオーバーライドして作っていくことになります)

    // System.Designの参照が必要です
    public class ExTextBoxDesigner : ControlDesigner
    {
        public override void InitializeNewComponent(IDictionary defaultValues)
        {
            base.InitializeNewComponent(defaultValues);
    
            var targetControl = Control;
            if (targetControl == null) return;
    
            var type = targetControl.GetType();
            var exTextBoxType = GetExTextBoxType(type);
            if (exTextBoxType == null) return;
    
            var typeParameter = exTextBoxType.GenericTypeArguments.Single();
            dynamic obj = Activator.CreateInstance(typeParameter);
    
            dynamic list = Activator.CreateInstance(typeof(List<>).MakeGenericType(typeParameter));
            list.Add(obj);
    
            ((dynamic)targetControl).List = list;
            ((dynamic)targetControl).Text = string.Empty;
        }
    
        private static Type GetExTextBoxType(Type type)
        {
            Type current = type;
            while (current != null)
            {
                if (current.IsGenericType && current.GetGenericTypeDefinition() == typeof(ExTextBox<>)) return current;
                current = current.BaseType;
            }
            return null;
        }
    }

    こういったクラスを作った後、DesignerAttribute で指定してください。

    [Designer(typeof(ExTextBoxDesigner))]
    public class ExTextBox<T> : TextBox

    2017年4月10日 13:54
    モデレータ
  • 別案。
    ExTextBox 側にメソッドを用意すれば、少しだけかんたんな形になるか。

    [Designer(typeof(ExTextBoxDesigner))]
    public class ExTextBox<T> : TextBox
        where T : new()
    {
        private List<T> _list;
    
        public ExTextBox()
        {
            _list = new List<T>();
        }
    
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<T> List { get; set; }
    
        internal void InitializeForNewComponent()
        {
            Text = string.Empty;
            _list.Add(new T());
        }
    }
    
    
    // System.Designの参照が必要です
    public class ExTextBoxDesigner : ControlDesigner
    {
        public override void InitializeNewComponent(IDictionary defaultValues)
        {
            base.InitializeNewComponent(defaultValues);
    
            var targetControl = Control;
            if (targetControl == null) return;
    
            var type = targetControl.GetType();
            var exTextBoxType = GetExTextBoxType(type);
            if (exTextBoxType == null) return;
    
            ((dynamic)targetControl).InitializeForNewComponent();
        }
    
        private static Type GetExTextBoxType(Type type)
        {
            Type current = type;
            while (current != null)
            {
                if (current.IsGenericType && current.GetGenericTypeDefinition() == typeof(ExTextBox<>)) return current;
                current = current.BaseType;
            }
            return null;
        }
    }

    • 回答としてマーク takiru 2017年4月11日 0:25
    2017年4月10日 13:57
    モデレータ
  • ご回答ありがとうございます。

    ControllDesignerを継承したものを作成することは一瞬検討しましたが、以前の質問で教えていただいた際に検証したところ、やはりどこかTextBoxと異なる動作をしてしまうため、既存動作担保の問題でやめた経緯がありました。
    確かに正攻法はこちらが正しいのかもしれません。
    時間がある際に、TextBoxと全く同様の動作をするクラスを作成・検証してみようかと思います。

    ドハマりしてしまっていたため、暫定でひとまず、スタックトレースによる対応を行い、後日、自前のデザイナーを作成する形を取ろうかと思います。

    みなさま、ご回答いただきありがとうございました。

    2017年4月11日 0:25
  • やはりどこかTextBoxと異なる動作をしてしまうため、既存動作担保の問題でやめた経緯がありました。
    確かに正攻法はこちらが正しいのかもしれません。

    Reference Source に TextBoxBaseDesigner がどういうメソッドをオーバーライドしているかはうかがえますので、リフレクションで internal クラスを生成して、そちらに中継すれば動きは近づくでしょうね。
    動作検証はあまりしていませんが、スナップラインが増える挙動は確認しました。

    // System.Designの参照が必要です
    public class ExTextBoxDesigner : ControlDesigner
    {
        private readonly dynamic _textBoxDesigner;
    
        public ExTextBoxDesigner()
        {
            var systemDesign = typeof(ControlDesigner).Assembly;
            var typeOfTextBoxBaseDesigner = systemDesign.GetType("System.Windows.Forms.Design.TextBoxBaseDesigner");
            _textBoxDesigner = Activator.CreateInstance(typeOfTextBoxBaseDesigner);
        }
    
        public override SelectionRules SelectionRules
        {
            get { return _textBoxDesigner.SelectionRules; }
        }
    
        public override IList SnapLines
        {
            get { return _textBoxDesigner.SnapLines; }
        }
    
        public override void Initialize(IComponent component)
        {
            base.Initialize(component);
            _textBoxDesigner.Initialize(component);
        }
    
        public override void InitializeNewComponent(IDictionary defaultValues)
        {
            base.InitializeNewComponent(defaultValues);
            _textBoxDesigner.InitializeNewComponent(defaultValues);
    
            var targetControl = Control;
            if (targetControl == null) return;
    
            var type = targetControl.GetType();
            var exTextBoxType = GetExTextBoxType(type);
            if (exTextBoxType == null) return;
    
            ((dynamic)targetControl).InitializeForNewComponent();
        }
    
        private static Type GetExTextBoxType(Type type)
        {
            Type current = type;
            while (current != null)
            {
                if (current.IsGenericType && current.GetGenericTypeDefinition() == typeof(ExTextBox<>)) return current;
                current = current.BaseType;
            }
            return null;
        }
    }

    2017年4月11日 13:20
    モデレータ