トップ回答者
TextBoxを貼り付けた時にリストに値を1件追加したい

質問
-
Visual Studio 2017
.NET Framework 4.5TextBoxを継承して、ExTextBoxを作っています。
List<T>クラスのプロパティAを用意しています。
ExTextBoxを貼り付けた直後では、プロパティAには1件登録されており、それをデザイナー上で編集して、デザイナーを閉じ、また開いても、編集した状態に復元されるコードが実現できません。感覚的には、結局designerファイルが編集状態に復元するわけなので、何らかのコードの書き方をして、List<T>のインスタンス化および1件登録をdesignerファイル上で行う実装が行えればいいのではと思っているのですが、皆目見当がつきません。
どうすると実装できるでしょうか?
回答
-
別案。
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
すべての返信
-
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
-
こんな?
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
-
ツールボックスから配置したときの初期値設定を制御したいという話ですよね?
なら、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
- 編集済み AzuleanMVP, Moderator 2017年4月10日 13:59
-
別案。
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
-
ご回答ありがとうございます。
ControllDesignerを継承したものを作成することは一瞬検討しましたが、以前の質問で教えていただいた際に検証したところ、やはりどこかTextBoxと異なる動作をしてしまうため、既存動作担保の問題でやめた経緯がありました。
確かに正攻法はこちらが正しいのかもしれません。
時間がある際に、TextBoxと全く同様の動作をするクラスを作成・検証してみようかと思います。ドハマりしてしまっていたため、暫定でひとまず、スタックトレースによる対応を行い、後日、自前のデザイナーを作成する形を取ろうかと思います。
みなさま、ご回答いただきありがとうございました。
-
やはりどこか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; } }