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

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

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

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

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

2回答

902閲覧

自動生成しているラベルを重ねない方法、範囲外に出た時の対処方法

nya-3

総合スコア27

C#

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

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

1グッド

0クリップ

投稿2022/04/07 06:13

編集2022/04/07 07:29

前提

splitContainer1.Panel2に
x軸の10間隔でラベルを自動生成し、罫線を作成しています。
左からData1,Data2~~~とラベルには値を入れています。
(Width1にして見えないようにしてます)

splitContainer1.Panel2をクリックした時に、
自動でラベルを作成するようにしました。(参考)
https://teratail.com/questions/oa0qgwebrssgdm#reply-lkzvdc0u3zkrd6

実現したいこと

クリックした位置に従い、Data1などの背面にある
ラベルの値を取得して表示させたいと思います。
「GetChildAtPoint」を利用すれば取得できそうなことは分かったのですが、
右から左にドラッグしてラベルを自動生成した時に
自動生成しているラベルを読み取ってしまいます。
リアルタイムで値をヒントテキストのように表示させるのが最終目標です。

該当のソースコード

【対応方法】
・スクロールバーの領域を含めた、splitContainer1.Panel1の座標を取得する
100,100で右下にスクロールした後に、1000,800などの値になれば
Data1のデータを逆算できます
→でも、なんとなく難しそう

・指定座標の特定名を含んだラベルを検索する
GetChildAtPointでラベルを取得する

試したこと

 罫線のラベルを伸ばし、GetChildAtPointで取得する
next_data = splitContainer1.Panel2.GetChildAtPoint(new Point(next_x, 40)).Text;
(y座標40の位置までLabelを伸ばしました)

教えてほしい事

・マウスでドラッグしたまま枠外にでた場合などの対応方法
Mouse_Leaveでcontainerから出た時に終了させる処理を組もうとしましたが、
Leaveを認識せず断念。

・ヒントテキストをドラッグ中に表示させる方法
ラベルの値を常に取得できれば、それをツールチップに表示させられると思います。
何か他の動作に支障が出そうな気もしており、どうするか迷ってます。
ヒントテキストで問題ないでしょうか?

・ドラッグで自動生成しているラベルを重ねない方法
ドラッグしてラベルを自動生成していますが、
自動生成しているラベルを重ねたくありません。
重なるようだったら自動で確定するようにしたいです。
これが一番知りたいです…。
ラベルを新規挿せく制する際にBringToFront();としています。

補足情報(FW/ツールのバージョンなど)

.NET Framework 4.7.2

TN8001👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

Schedule Boardというフリーソフトがイメージに一番近いです。

なるほど横型のスケジュールのようなものですか(あるいはガントチャートのような)
商用ならそういうのもいっぱいあるんですけどねぇ^^;

暇だったので試しに作ってみましたが、思ったよりは少コードでできたなって印象です。
SplitContainerの余計なお世話がウザかったので、さらにパネルを入れました(だいぶ座標計算が楽)

・マウスでドラッグしたまま枠外にでた場合などの対応方法

特に問題なさそう。

・ヒントテキストをドラッグ中に表示させる方法

ToolTipで問題なし。

・ドラッグで自動生成しているラベルを重ねない方法

自動確定は嫌なので、移動・リサイズと同様に単に操作が効かないようにした。

cs

1using System; 2using System.Drawing; 3using System.Linq; 4using System.Windows.Forms; 5 6namespace Qozvvdivu7h1ow0 7{ 8 public partial class Form1 : Form 9 { 10 private const int cellWidth = 50; 11 private const int cellHeight = 30; 12 13 private readonly SplitContainer splitContainer1; // 本体部 14 private readonly SplitContainer splitContainer2; // ヘッダー部 15 private readonly TableLayoutPanel tableLayoutPanel1; // ヘッダー 16 private readonly PanelEx panel2; // グリッド線とPanel2の余計なお世話除け 17 private readonly ToolTip toolTip1 = new ToolTip(); 18 private readonly ContextMenuStrip contextMenuStrip1 = new ContextMenuStrip(); 19 private readonly Size halfCellSize = new Size(cellWidth / 2, cellHeight / 2); 20 21 private Label label; 22 private Point start; // ドラッグ開始位置(panel2基準) 23 private Point offset; // ドラッグ開始時のLabel内位置(label基準) 24 25 public Form1() 26 { 27 InitializeComponent(); 28 29 splitContainer1 = new SplitContainer 30 { 31 BorderStyle = BorderStyle.Fixed3D, 32 Dock = DockStyle.Fill, 33 }; 34 splitContainer1.Panel2.AutoScroll = true; 35 splitContainer1.Panel2.Paint += Panel2_Paint; // ヘッダー同期(Scrollイベントではスプリッターサイズ変更等でダメ) 36 splitContainer1.SplitterMoved += SplitContainer_SplitterMoved; // スプリッター同期 37 Controls.Add(splitContainer1); 38 39 panel2 = new PanelEx(cellWidth, cellHeight) 40 { 41 Size = new Size(cellWidth * 24 + 1, cellHeight * 10 + 1), 42 }; 43 panel2.MouseDown += Panel2_MouseDown; 44 panel2.MouseMove += Panel2_MouseMove; 45 panel2.MouseUp += Panel2_MouseUp; 46 splitContainer1.Panel2.Controls.Add(panel2); 47 48 // ヘッダー部 49 splitContainer2 = new SplitContainer 50 { 51 BorderStyle = BorderStyle.Fixed3D, 52 Dock = DockStyle.Top, 53 Height = 20, 54 }; 55 splitContainer2.SplitterMoved += SplitContainer_SplitterMoved; // スプリッター同期 56 Controls.Add(splitContainer2); 57 58 // ヘッダー 59 tableLayoutPanel1 = new TableLayoutPanel 60 { 61 ColumnCount = 25, // 23+2 23個の数字のマスと両端に半分サイズのマスでグリッド線上に数字が来るように 62 Width = cellWidth * 24 + 1, 63 Height = 20, 64 }; 65 66 tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f / 24 / 2)); 67 tableLayoutPanel1.Controls.Add(new Label()); 68 for (var i = 1; i < 24; i++) 69 { 70 tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f / 24)); 71 var label = new Label 72 { 73 Dock = DockStyle.Fill, 74 Text = $"{i}", 75 TextAlign = ContentAlignment.MiddleCenter, 76 }; 77 tableLayoutPanel1.Controls.Add(label); 78 } 79 tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f / 24 / 2)); 80 splitContainer2.Panel2.Controls.Add(tableLayoutPanel1); 81 82 83 contextMenuStrip1.Items.Add("削除", null, ContextMenu_Click); 84 } 85 86 private void Panel2_MouseDown(object sender, MouseEventArgs e) 87 { 88 start = panel2.PointToClient(Cursor.Position); // 同じ new Point(e.X, e.Y); 89 offset = new Point(e.X % cellWidth, e.Y % cellHeight); 90 Cursor = Cursors.SizeWE; 91 92 var x = e.X / cellWidth * cellWidth; 93 var y = e.Y / cellHeight * cellHeight; 94 95 label = new Label 96 { 97 BackColor = Color.MistyRose, 98 BorderStyle = BorderStyle.FixedSingle, 99 ContextMenuStrip = contextMenuStrip1, 100 Location = new Point(x, y), 101 Size = new Size(cellWidth, cellHeight), 102 Text = "テスト", 103 TextAlign = ContentAlignment.MiddleCenter, 104 }; 105 label.MouseDown += Label_MouseDown; 106 label.MouseMove += Label_MouseMove; 107 label.MouseUp += Label_MouseUp; 108 109 panel2.Controls.Add(label); 110 } 111 private void Panel2_MouseMove(object sender, MouseEventArgs e) 112 { 113 if (label != null) TryResize(label); 114 } 115 private void Panel2_MouseUp(object sender, MouseEventArgs e) 116 { 117 Cursor = Cursors.Default; 118 if (label != null) toolTip1.Hide(label); 119 label = null; 120 } 121 122 private void Label_MouseDown(object sender, MouseEventArgs e) 123 { 124 if (sender is Label label) 125 { 126 // リサイズ時に反対の辺をstartに設定 127 offset = new Point(e.X, e.Y); 128 if (e.X < 8) 129 { 130 start = label.Location + label.Size - halfCellSize; 131 } 132 if (label.Width - 8 < e.X) 133 { 134 start = label.Location + halfCellSize; 135 } 136 } 137 } 138 private void Label_MouseMove(object sender, MouseEventArgs e) 139 { 140 if (sender is Label label) 141 { 142 if (e.Button == MouseButtons.None) 143 { 144 if (e.X < 8 || label.Width - 8 < e.X) label.Cursor = Cursors.SizeWE; 145 else label.Cursor = Cursors.Hand; 146 } 147 148 if (e.Button == MouseButtons.Left) 149 { 150 if (label.Cursor == Cursors.SizeWE) TryResize(label); 151 if (label.Cursor == Cursors.Hand) TryMove(label); 152 } 153 } 154 } 155 private void Label_MouseUp(object sender, MouseEventArgs e) 156 { 157 toolTip1.Hide((Label)sender); 158 } 159 160 private void ContextMenu_Click(object sender, EventArgs e) 161 { 162 if (sender is ToolStripMenuItem item && item.Owner is ContextMenuStrip contextMenu) 163 { 164 if (contextMenu.SourceControl is Label label) 165 { 166 label.MouseDown -= Label_MouseDown; 167 label.MouseMove -= Label_MouseMove; 168 label.MouseUp -= Label_MouseUp; 169 panel2.Controls.Remove(label); 170 } 171 } 172 } 173 174 // スプリッターの同期 175 private void SplitContainer_SplitterMoved(object sender, SplitterEventArgs e) 176 { 177 if (sender == splitContainer1 && splitContainer2 != null) 178 splitContainer2.SplitterDistance = splitContainer1.SplitterDistance; 179 if (sender == splitContainer2 && splitContainer1 != null) 180 splitContainer1.SplitterDistance = splitContainer2.SplitterDistance; 181 } 182 // ヘッダーの同期 183 private void Panel2_Paint(object sender, PaintEventArgs e) 184 { 185 var p = splitContainer1.Panel2.AutoScrollPosition; 186 tableLayoutPanel1.Left = p.X; 187 } 188 189 private void TryMove(Label target) 190 { 191 var p = panel2.PointToClient(Cursor.Position); 192 var x = (p.X - offset.X + cellWidth / 2) / cellWidth * cellWidth; 193 var y = (p.Y - offset.Y + cellHeight / 2) / cellHeight * cellHeight; 194 195 var rect = target.Bounds; 196 rect.X = x; 197 rect.Y = y; 198 TrySetBounds(target, rect); 199 } 200 private void TryResize(Label target) 201 { 202 var end = panel2.PointToClient(Cursor.Position); 203 var s = start.X / cellWidth * cellWidth; 204 var e = end.X / cellWidth * cellWidth; 205 var rect = target.Bounds; 206 207 if (start.X < end.X) 208 { 209 rect.X = s; // 本来不要なはずだが謎挙動に悩まされた... 210 rect.Width = e - s + cellWidth; 211 } 212 else 213 { 214 rect.X = e; 215 rect.Width = s - e + cellWidth; 216 } 217 TrySetBounds(target, rect); 218 } 219 private void TrySetBounds(Label target, Rectangle rect) 220 { 221 if (target.Bounds == rect) return; // ToolTipがちらつくし無駄なので 222 223 // panel2の中で自分以外と交差するものがあったら動かさない(=重ねられない) 224 foreach (var label in panel2.Controls.OfType<Label>()) 225 { 226 if (label == target) continue; 227 if (label.Bounds.IntersectsWith(rect)) return; 228 } 229 target.Bounds = rect; 230 231 var t = $"Location: {target.Location}\nSize: {target.Size}"; 232 toolTip1.Show(t, target, 0, -50); 233 } 234 } 235 236 237 public class PanelEx : Panel // グリッド線付きPanel 238 { 239 private readonly int cellWidth; 240 private readonly int cellHeight; 241 242 public PanelEx(int cellWidth, int cellHeight) 243 { 244 this.cellWidth = cellWidth; 245 this.cellHeight = cellHeight; 246 DoubleBuffered = true; 247 } 248 249 protected override void OnPaint(PaintEventArgs e) 250 { 251 base.OnPaint(e); 252 253 var right = ClientRectangle.Right; 254 var bottom = ClientRectangle.Bottom; 255 256 for (var x = 0; x < right; x += cellWidth) 257 { 258 e.Graphics.DrawLine(Pens.Gray, x, 0, x, bottom); 259 } 260 for (var y = 0; y < bottom; y += cellHeight) 261 { 262 e.Graphics.DrawLine(Pens.Gray, 0, y, right, y); 263 } 264 } 265 } 266}

アプリ画像

投稿2022/04/09 10:24

編集2023/07/30 05:55
TN8001

総合スコア9321

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

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

TN8001

2022/04/09 10:25

文字数ぎりぎりで追記できないので別回答に分けました。
nya-3

2022/04/11 02:59

TN8001様 回答ありがとうございます。 すごいです!まさに欲しかったものそのものです! ただ、作成したラベルをつかんだまま枠外に行ったり、 ドラッグでラベルを作成時に枠外に行った場合の 値がどうなるか気になるところです。検証してみます。 また、事前に作っておいたTabPageに追加したいと思っております。 Controls.Addの部分の一部を修正したら スクロールした際に文字がついてこなくなりました。 tabPage1.Controls.Add(splitContainer1); tabPage1.Controls.Add(splitContainer2); 上記のように修正すれば出来るかと思ったのですが…。 Panel2_Paintの var p = splitContainer1.Panel2.AutoScrollPosition; 上記を修正すればいいのだと思いますが、 tabPageは事前に設定済のものを入れたい為 どうもうまくいきません。 罫線はスクロール動作が出来ますが、 ヘッダーの文字が同期しないのです。 スクロールさせたあとにFormのサイズとかを変えたら同期してくれるのですが… 勉強不足で申し訳ありませんが、分かれば教えてくださいorz
TN8001

2022/04/11 08:41

> ただ、作成したラベルをつかんだまま枠外に行ったり、 > ドラッグでラベルを作成時に枠外に行った場合の > 値がどうなるか気になるところです。 罫線の範囲外に行っても コントロールの範囲外に行っても ウィンドウの範囲外に行っても そのまま継続します(まあやってみたら一目瞭然ですが^^; そのためLocationがマイナスになったり、サイズがマス目*24以上の大きさになることがあります。 > また、事前に作っておいたTabPageに追加したいと思っております。 > Controls.Addの部分の一部を修正したら > スクロールした際に文字がついてこなくなりました。 「事前に作っておいたTabPage」というのはデザイナで置いた、TabControlとTabPageってことですか? こちらの手元で試したらnya-3さんの予想通り、下記2行の修正だけで動きましたが。 //Controls.Add(splitContainer1); tabPage1.Controls.Add(splitContainer1); //Controls.Add(splitContainer2); tabPage1.Controls.Add(splitContainer2); 「事前に作っておいたTabPage」の中に、SplitContainerとかも入っているという意味でしょうか? だとするとプロパティとかイベントを、同じように注意深く設定してください。 Panel2_PaintはPanelEx panel2.Paintではなく、splitContainer1.Panel2.Paintなことに注意してください^^;
nya-3

2022/04/13 07:02 編集

TN8001様 回答ありがとうございます。返信遅れて申し訳ありません。 移動については理解しました。ありがとうございます。 「事前に作っておいたTabControl(TabPage含む)」の中に 今回のSplitContainerを入れたいのです。 Controls.Add(splitContainer1)などをコメントアウトして置き換えました。 罫線や文字は問題なくTabPageに表示されるのですが、 横スクロールなどの時にヘッダーの文字が一緒にスクロールしてくれないのです。 ヘッダーもTabPageに書かれているのですが、なぜかついてこないのです…。 TN8001様の方では再現しないでしょうか? 2022/04/13 16:01 あまり良く分かっていないのですが、以下のようにしたらスクロールが同期しました! splitContainer1.Panel2.Paint += Panel2_Paint; // ヘッダー同期(Scrollイベントではスプリッターサイズ変更等でダメ) ↓ panel2.Paint += Panel2_Paint; あと1つだけやりたい事があったのですが、 これは別で質問させて頂きます。 本当に、助かりました!回答ありがとうございました!
guest

0

右から左にドラッグしてラベルを自動生成した時に
自動生成しているラベルを読み取ってしまいます。

罫線のラベルを(さらに)パネルにくるんだりすればどうですか?
Panel2にばらばらに入れるのではなく、Panel2に入れたhogePanel(例えばの名前)に罫線のラベルだけ入れればhogePanel.GetChildAtPointでいいことになります。

・マウスでドラッグしたまま枠外にでた場合などの対応方法

ドラッグ操作中はマウスキャプチャしてしまうほうが普通かなと思います。
マウス キャプチャ - Windows Forms .NET Framework | Microsoft Docs

例えばWindows標準のペイントでペンツールを使うとき(ドラッグ中)は、ウィンドウ外にマウスが出てもペン描画が継続します。

・ヒントテキストをドラッグ中に表示させる方法

なんのヒントなんですかね?前回の質問でいうピンクのラベルのサイズ等?
よくわかりませんがToolTipでダメだったら、ほかの手段を考えることになりますね。

・ドラッグで自動生成しているラベルを重ねない方法

ピンクのラベルはすべてわかってるわけですから、計算することはできますよね?
全ラベルを交差判定しても、大した負荷でもないんじゃないですかね。
Rectangle.IntersectsWith(Rectangle) メソッド (System.Drawing) | Microsoft Docs

ピンクのラベルを(範囲選択のような感じで)、ドラッグで作るように変えたってことですよね?
「自動で確定」はUX的にどうか(ユーザーの意図に反してドラッグが終了する)とは思います(代案を思いついてるわけではないが^^;


そもそも罫線をラベルで表現する必要あるんでしょうか?
OnPaintででも線を引くだけでいいような。
Control.OnPaint(PaintEventArgs) メソッド (System.Windows.Forms) | Microsoft Docs

投稿2022/04/07 10:32

TN8001

総合スコア9321

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

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

nya-3

2022/04/08 01:51 編集

TN8001様 回答ありがとうございます! >Panel2にばらばらに入れるのではなく、Panel2に入れたhogePanel(例えばの名前)に >罫線のラベルだけ入れればhogePanel.GetChildAtPointでいいことになります。 パネルに罫線だけ入れることはできると思いますが、 Panel2をクリック(ドラッグ)したときに、ラベルを自動生成するようにしているため、 背面に隠れてしまうということはないでしょうか?試してみます。 >ピンクのラベルを(範囲選択のような感じで)、ドラッグで作るように変えたってことですよね? おっしゃるとおりです! ドラッグで自動生成するピンクのラベルは、LabelListに全て入っていると思っています。 ただ、ドラッグしながら重なるかどうかの判断方法が分からなかったのと、 移動範囲の制限の方法が分からなかったのです。 ラベルというのにこだわっているわけでもなく、値を保持できるのであれば Rectangleでも良いのです。 確定するまではダミーで四角い枠を見せ、別ウィンドウを開いて色とか内容を設定し もどったらラベルの色や内容が設定されているというのでも良いと思います。 >そもそも罫線をラベルで表現する必要あるんでしょうか? ラベルにする必要は無かったのですが、罫線の位置に値を用意してあるため、 それを自動生成するピンクのラベルの時に値を取得するようにしたいのです。 それで思いついたのが、GetChildAtPointでした。 今気づきました。AutoScrollを利用していますが、 splitContainerの最大幅は決めており、それは超えないようにしたかったのです。スクロールバーを新たに付けてみましたが、 AutoScrollのものも表示されてしまいますし、上手くいきません。うーん。 【そもそも、どういう操作を実現させたいか】  ドラッグし、特定の位置に四角い枠を自動生成したい。(色など中身を変更する可能性あり)  ドラッグで時間指定できるスケジュールボードのようなものです。  クリックしてラベルを移動できる機能も付ける予定です。 (それはEventAddでつけれそう)  Schedule Boardというフリーソフトがイメージに一番近いです。 値を取得するのに、ラベルから取得するのではなく座標から取得したほうが良い気がしてきました。初心者がいきなり難しい事を実施しようとしているのは分かってますが、中々難しいですね。
nya-3

2022/04/08 05:54 編集

ToolTipでの更新を考えました。以下は、spritContener1のPanel2のMouseMoveにあります。 NowTime = splitContainer1.Panel2.GetChildAtPoint(new Point(next_x, 40)).Text; if (toolTip1.GetToolTip(splitContainer1.Panel2) != NowTime) { toolTip1.SetToolTip(splitContainer1.Panel2, NowTime); Console.WriteLine(NowTime); } 凄く適当な作りですが、値が更新されたらtoolTipに更新した値を表示にしてみました。 ですが、表示されるのは空欄のtoolTipのみ。Console.WriteLineには値が入ってました。 toolTipに値が入っていないのではないかとtoolTip1.GetToolTip(splitContainer1.Panel2) とし toolTipの値をログに出してみましたが、きちんと設定されてました。 どういうことでしょう?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問