質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

1回答

9334閲覧

C# 子コントロールのイベントを親のユーザーコントロールに通知、定義させる

damedameSE

総合スコア0

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2020/08/05 12:38

編集2020/08/05 13:48

こんにちは
C#で開発を進めている者です。
SplitterPanelのクリックイベントを親のUserControlに通知、定義する方法を探しています。

単純にSplitterPanelの数が固定であれば問題ないのですが、
SplitterPanelは追加、削除されることが前提で、
そのたびにSplitterPanelに対してイベントを付与する必要があり、
そのイベントを親のUserControlに通知、定義する方法を教えて頂ければとおもいます。

開発言語:C#
開発環境:VisualStudio2017

初投稿のため、情報不足等あるとはおもいますが、よろしくお願いいたします。

※※補足※※

構成は
ユーザーコントロール
→SplitContainerを拡張したコンポーネント
→SplitterPanelを拡張したコンポーネント
となっていて、SplitterPanelの作成、イベントの付与はSplitContainerを拡張した
コンポーネントで行っており、そこで作成されたSplitterPanelを拡張した
コンポーネントに対するイベントの発生を上のユーザーコントロールに通知する方法を
教えて頂きたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YAmaGNZ

2020/08/05 12:49

SplitterPanelを親コントロールで生成しているということでしょうか? それなら、生成時にイベントハンドラを追加すればいいのではないでしょうか?
damedameSE

2020/08/05 13:46

ご回答ありがとうございます。 説明不足で申し訳ございません。 構成は ユーザーコントロール  →SplitContainerを拡張したコンポーネント   →SplitterPanelを拡張したコンポーネント となっています SplitterPanelを作成しているのはSplitContainerを拡張したコンポーネントですので このコンポーネントでの作成時に上のユーザーコントロールに通知したいということです。
YAmaGNZ

2020/08/05 14:24 編集

SplitterPanelはSplitContainerのみが見れる状態でユーザーコントロールからはアクセスできないということですか? もし、ユーザーコントロールからSplitterPanelのインスタンスへのアクセス手段がないのであれば、イベントも直接通知するような形ではなく、一旦SplitContainerが受けて、そこから「SplitContainerがSplitterPanelがイベントを発生させた」といったイベントを作成し、SplitContainerからユーザーコントロールへイベントを起こすような形になるのではないでしょうか?
damedameSE

2020/08/05 14:50

ご回答ありがとうございます。 ユーザーコントロールからSplitterPanelにアクセスすることも可能です。 それぞれの役割として ユーザーコントロール  SplitContainerにパネルの作成元となるデータのリストを渡す SplitContainerを拡張したコンポーネント  ユーザーコントロールから受け取ったリストを元に、SplitterPanelを拡張した  コンポーネントを作成し、Click等のイベントを付与 といった形です。 SplitterPanelを拡張したコンポーネントに付与されたClickイベントを どのようにユーザーコントロール側に通知すればよいのかがわからず質問させていただいた次第です。
YAmaGNZ

2020/08/05 15:01 編集

ユーザーコントロールからアクセスできるのであれば、作成した時にそのSplitterPanelのClickイベントに対してイベントハンドラを追加すればいいのでは? というのが最初のコメントだったのですが・・・ SplitterPanel.Click += ~ といった感じで
退会済みユーザー

退会済みユーザー

2020/08/06 06:58

@damedameSE 名前にとても親近感を覚えたので横から失礼します。 最近.NETは嵩張るので開発環境ごと消してしまったので適当ですが、 SplitContainer.ControlAdded/SplitContainer.ControlRemovedあたりのイベントを使うか、拡張しているなら自分でイベントを定義すればいいだけだと思います。親コントロールから各パネルをそこまでして制御したいユースケースが分かりませんが、実際にやりたいことがそれで実現できるかどうかは、コードすらない段階で第三者には分かりません。 そのまま動くサンプルコードを貼りつつ、目的などを説明/共有した方がいいと思います。
guest

回答1

0

YAmaGNZさん 2020/08/05 23:24 のコメントのざっくり実装です。

同様のものを作るのは大変なので、以下のように置き換えさせていただきます。
ユーザーコントロール → Form1(Form)
SplitContainerを拡張したコンポーネント → ParentPanel(FlowLayoutPanel)
SplitterPanelを拡張したコンポーネント → ChildPanel(Panel)

ChildPanelの追加・削除時にイベント登録・解除して、新しく作った専用のイベントにまわすだけです。
本題に関係ないコードのほうが多いですが、ポイントはコメントが入ってる部分だけです。

cs

1using System; 2using System.Collections.Generic; 3using System.Diagnostics; 4using System.Drawing; 5using System.Linq; 6using System.Windows.Forms; 7 8namespace Questions282825 9{ 10 public partial class Form1 : Form 11 { 12 private ParentPanel parentPanel; 13 private ListBox listBox; 14 15 public Form1() 16 { 17 InitializeComponent(); 18 19 parentPanel = new ParentPanel() { Dock = DockStyle.Fill, }; 20 21 // 子供クリックイベントの登録 22 parentPanel.ChildClick += ParentPanel_ChildClick; 23 Controls.Add(parentPanel); 24 25 var addButton = new Button { Dock = DockStyle.Bottom, Text = "add", }; 26 addButton.Click += AddButton_Click; 27 28 var clearButton = new Button { Dock = DockStyle.Top, Text = "clear", }; 29 clearButton.Click += ClearButton_Click; 30 31 var tableLayoutPanel = new TableLayoutPanel { Dock = DockStyle.Left, Width = 80, }; 32 tableLayoutPanel.Controls.Add(addButton, 0, 0); 33 tableLayoutPanel.Controls.Add(clearButton, 0, 1); 34 tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F)); 35 tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F)); 36 Controls.Add(tableLayoutPanel); 37 38 listBox = new ListBox { Dock = DockStyle.Left, SelectionMode = SelectionMode.MultiSimple, }; 39 listBox.DataSource = new Color[] { Color.Red, Color.Green, Color.Blue, }; 40 Controls.Add(listBox); 41 } 42 43 private void ParentPanel_ChildClick(object sender, EventArgs e) 44 { 45 if(sender is Control control) 46 { 47 Debug.WriteLine($"{control.Name} {control.BackColor}"); 48 } 49 } 50 51 private void AddButton_Click(object sender, EventArgs e) 52 { 53 var colors = listBox.SelectedItems.Cast<Color>(); 54 parentPanel.AddRange(colors); 55 } 56 57 private void ClearButton_Click(object sender, EventArgs e) 58 { 59 parentPanel.Clear(); 60 } 61 62 63 class ParentPanel : FlowLayoutPanel 64 { 65 // 新しく作った子供クリックイベント 66 public event EventHandler ChildClick; 67 68 public void AddRange(IEnumerable<Color> colors) 69 { 70 foreach(var color in colors) 71 { 72 var child = new ChildPanel(color); 73 74 // 追加時にイベント登録 75 child.Click += Child_Click; 76 Controls.Add(child); 77 } 78 } 79 80 public void Clear() 81 { 82 foreach(var child in Controls.Cast<Control>().ToArray()) 83 { 84 // 削除時にイベント解除 85 child.Click -= Child_Click; 86 Controls.Remove(child); 87 child.Dispose(); 88 } 89 } 90 91 private void Child_Click(object sender, EventArgs e) 92 { 93 // 子供クリックイベントを発砲(sender e そのまま渡しは雑い^^; 94 ChildClick?.Invoke(sender, e); 95 } 96 } 97 98 class ChildPanel : Panel 99 { 100 private static int i; 101 102 public ChildPanel(Color color) 103 { 104 Name = $"Panel{++i}"; 105 Width = 100; 106 Height = 100; 107 BackColor = color; 108 } 109 } 110 } 111}

そのたびにSplitterPanelに対してイベントを付与する必要

もしかして「やれるのはわかっているが、いちいち登録解除がめんどくさい」って意味でしょうか?

こんなソリューションがありました(この要件だとオーバーすぎる気がしますが)
Broadcasting Events through a Control Hierarchy - CodeProject
たしか無料登録しないとダウンロードできなかったと思うので、面倒な場合こちら(おそらく↑そのまま)
POSTSTAR/EventBroadcastProvider.cs at master · ohintaek/POSTSTAR

最初読み違えてSplitContainerを多段に入れ子すると思っていたので、そんな作りになってしまっています^^;

cs

1using System; 2using System.Collections.Generic; 3using System.Diagnostics; 4using System.Drawing; 5using System.Linq; 6using System.Windows.Forms; 7 8namespace Questions282825 9{ 10 public partial class Form1 : Form 11 { 12 public Form1() 13 { 14 InitializeComponent(); 15 16 var splitContainer = new MySplitContainer(); 17 Controls.Add(splitContainer); 18 19 // どちらにしろこれは必須 20 var broadcastProvider = EventBroadcastProvider.CreateProvider(splitContainer, "Click"); 21 22 // 単にクリックが拾えればいい場合 23 splitContainer.Click += SplitContainer_Click; 24 25 // クリックされたコントロールやツリーも欲しい場合 26 broadcastProvider.Relayed += BroadcastProvider_Relayed; 27 } 28 29 private void SplitContainer_Click(object sender, EventArgs e) 30 { 31 Debug.WriteLine("Click"); 32 } 33 34 private void BroadcastProvider_Relayed(object sender, RelayedEventArgs e) 35 { 36 if(e.RelayChain[0] is Control control) 37 { 38 Debug.WriteLine($"{control.Name} {control.BackColor}"); 39 } 40 } 41 42 43 // 本題には関係ないが検証用に雑に作成 44 class MySplitContainer : SplitContainer 45 { 46 private static List<KnownColor> colors; 47 private static Random random = new Random(); 48 private static int i; 49 50 private static ContextMenuStrip contextMenuStrip; 51 private static ToolStripMenuItem addToolStripMenuItem; 52 private static ToolStripMenuItem delToolStripMenuItem; 53 54 static MySplitContainer() 55 { 56 colors = Enum.GetValues(typeof(KnownColor)).Cast<KnownColor>().ToList(); 57 58 addToolStripMenuItem = new ToolStripMenuItem { Text = "追加", }; 59 addToolStripMenuItem.Click += AddToolStripMenuItem_Click; 60 61 delToolStripMenuItem = new ToolStripMenuItem { Text = "削除", }; 62 delToolStripMenuItem.Click += DelToolStripMenuItem_Click; 63 64 contextMenuStrip = new ContextMenuStrip(); 65 contextMenuStrip.Items.Add(addToolStripMenuItem); 66 contextMenuStrip.Items.Add(delToolStripMenuItem); 67 } 68 69 public MySplitContainer() 70 { 71 BorderStyle = BorderStyle.Fixed3D; 72 Dock = DockStyle.Fill; 73 74 Panel1.Name = $"Panel{++i}"; 75 Panel2.Name = $"Panel{++i}"; 76 77 Panel1.ContextMenuStrip = contextMenuStrip; 78 Panel2.ContextMenuStrip = contextMenuStrip; 79 80 Panel1.BackColor = Color.FromKnownColor(colors.Skip(random.Next(0, colors.Count)).First()); 81 Panel2.BackColor = Color.FromKnownColor(colors.Skip(random.Next(0, colors.Count)).First()); 82 } 83 84 private static void AddToolStripMenuItem_Click(object sender, EventArgs e) 85 { 86 if(sender is ToolStripItem item) 87 { 88 if(item.Owner is ContextMenuStrip menuStrip) 89 { 90 if(menuStrip.SourceControl is SplitterPanel panel) 91 { 92 var o = (int)Orientation.Vertical; 93 if(panel.Parent is SplitContainer container) 94 { 95 o = (int)container.Orientation; 96 o ^= 1; 97 } 98 99 var sc = new MySplitContainer { Orientation = (Orientation)o }; 100 panel.Controls.Add(sc); 101 } 102 } 103 } 104 } 105 106 private static void DelToolStripMenuItem_Click(object sender, EventArgs e) 107 { 108 if(sender is ToolStripItem item) 109 { 110 if(item.Owner is ContextMenuStrip menuStrip) 111 { 112 if(menuStrip.SourceControl is SplitterPanel panel) 113 { 114 if(panel.Parent is SplitContainer container) 115 { 116 container.Parent.Controls.Remove(container); 117 container.Dispose(); 118 } 119 } 120 } 121 } 122 } 123 } 124 } 125}

投稿2020/08/06 09:01

編集2023/07/22 10:19
TN8001

総合スコア9862

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問