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

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

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

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

Q&A

解決済

2回答

1701閲覧

C# DataGridViewで選択したセルの行のボトムに赤い罫線を引きたい、ができません。

ohikazuma

総合スコア9

C#

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

0グッド

1クリップ

投稿2023/01/30 01:36

前提

DataGridViewに多数のカラムを追加していくと、グリッドの幅が広くなり、グリッドの端で選択したセルの行を目で追い辛くなります。
そこで、
「選択したセルの行のボトムに赤い罫線を引きたい」
のですが上手く行きません。

dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
は、行全体を選択するので目的とは異なると思っております。

環境:
ViualStudio2015
フォームアプリ (クラシックデスクトップアプリ)
C#
.Net Framework=4.5.2

実現したいこと

「選択したセルの行のボトムに赤い罫線を引きたい」
カレーパンを選択すると、こんな感じ↓
イメージ説明

発生している問題・エラーメッセージ

エラーは発生していないのですが、
以下の様状態にしかなりません。(TT)
イメージ説明

イメージ説明

イメージ説明

イメージ説明

該当のソースコード

public partial class Form1 : Form { int widthAllCol = 0; //各カラム幅の合計値 public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { // DataTableの個所は「猫の気ままなC#日記」さんのコードを利用させてもらってます。 // ありがとうございます。 DataTable dt = new DataTable("Table1"); string[] id = { "S0001", "S0002", "S0003", "S0004", "S0005", "S0006" }; string[] name = { "あんぱん", "メロンパン", "カレーパン", "いちごじゃむパン", "チョココロネ", "クロワッサン" }; string[] value = { "100", "105", "110", "115", "120", "125" }; // DataTableに列を追加します。 dt.Columns.Add("商品番号"); dt.Columns.Add("商品名"); dt.Columns.Add("価格"); // DataTableにデータを格納します。 for (int i = 0; i < 5; i++) { dt.Rows.Add(dt.NewRow()); dt.Rows[i]["商品番号"] = id[i]; dt.Rows[i]["商品名"] = name[i]; dt.Rows[i]["価格"] = value[i]; } //dataGridView1に表示 dataGridView1.DataSource = dt; //dataGridView1の基本設定 dataGridView1.ReadOnly = true; //読取専用 dataGridView1.AllowUserToDeleteRows = false; //行削除禁止 dataGridView1.AllowUserToAddRows = false; //行挿入禁止 dataGridView1.AllowUserToResizeRows = false; //行の高さ変更禁止 dataGridView1.RowHeadersVisible = false; //行ヘッダーを非表示にする dataGridView1.MultiSelect = false; //セル、行、列が複数選択禁止 //dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect; //セルを選択すると行全体が選択されるようにする for (int i = 0; i < dataGridView1.ColumnCount; i++) { widthAllCol += dataGridView1.Columns[i].Width; //各カラム幅の合計値 } } private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) { DataGridView dgv = (DataGridView)sender; //各カラム幅の合算…ここに入れると複数回計算されるのでどうかと思うのですが… widthAllCol = 0; for (int i = 0; i < dgv.ColumnCount; i++) { widthAllCol += dgv.Columns[i].Width; //各カラム幅の合計値 } Console.WriteLine("widthAllCol: " + widthAllCol.ToString()); Pen penRed = new Pen(Color.Red, 2); Pen penOriginal = new Pen(Color.Gray, 1); if (e.RowIndex == dgv.CurrentRow.Index) { int startX = dgv.RowHeadersVisible ? dgv.RowHeadersWidth : 0; int startY = e.RowBounds.Top + 1; int endX = startX + dgv.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - dgv.HorizontalScrollingOffset; startX = 0; endX = widthAllCol; startY = e.RowBounds.Top + e.RowBounds.Height - 1; e.Graphics.DrawLine(penRed, startX, startY, endX, startY); } else { int startX = dgv.RowHeadersVisible ? dgv.RowHeadersWidth : 0; int startY = e.RowBounds.Top + 1; int endX = startX + dgv.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - dgv.HorizontalScrollingOffset; startX = 0; endX = widthAllCol; startY = e.RowBounds.Top + e.RowBounds.Height - 1; e.Graphics.DrawLine(penOriginal, startX, startY, endX, startY); } } private void dataGridView1_SizeChanged(object sender, EventArgs e) { //【期待通りの動作で無かったので廃止】 //Console.WriteLine("dataGridView1_SizeChangedに入りました。"); //DataGridView dgv = (DataGridView)sender; //for(int i = 0; i < dgv.ColumnCount; i++) //{ // widthAllCol += dgv.Columns[i].Width; //各カラム幅の合計値 //} } }

試したこと

dataGridView1_RowPostPaintが一部のセル(最小で済むセルを選んでいるみたい)しかリフレッシュしてくれない。
全セルを再描画してくれるメソッド等を探したが、見つからなかった。
検討違いのことをしてるのでしょうか?

宜しくお願い致します。

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

ViualStudio2015
フォームアプリ (クラシックデスクトップアプリ)
C#
.Net Framework=4.5.2

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

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

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

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

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

ohikazuma

2023/01/30 03:58

試してみます。 暫くお待ちください。
ohikazuma

2023/01/30 04:53 編集

できました、ドンピシャです。 ありがとうございます。 幾つか試してみて、_CellClickイベントに.Invalidate()を追加しました。 KOZ6.0さんなら、どの様なイベントを使われますか? private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) { //各カラム幅の合計値を集計する。 //_RowPostPaintにあったが、ここに引っ越し。 widthAllCol = 0; for (int i = 0; i < dataGridView1.ColumnCount; i++) { widthAllCol += dataGridView1.Columns[i].Width; //各カラム幅の合計値 } //ここで再表示! dataGridView1.Invalidate(); }
ohikazuma

2023/01/30 05:04

セルをクリックした際と、矢印キーで選択セルを変更した際に、赤線を引くには、  _SelectionChanged が良さそうですね。
KOZ6.0

2023/01/30 07:21

Control.Invalidate メソッドは、Win32API の InvalidateRect を呼び出します。 「InvalidateRect 関数 (winuser.h)」 https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-invalidaterect 何が行われるかというと、以下の URL を参照してください。 「領域の消去と有効化」 http://wisdom.sakura.ne.jp/system/winapi/win32/win35.html 「WM_PAINTメッセージの使用」 https://learn.microsoft.com/ja-jp/windows/win32/gdi/using-the-wm-paint-message 引数なしの Invalidate() メソッドはコントロールの全体を無効化しますが、YAmaGNZ さんが提示された InvalidateRow は、コントロールの一部分のみを無効化します。 うまく使えば画面への転送量が抑えられるため高速になります。
ohikazuma

2023/01/30 08:47

KOZ6.0さん、素人相手にご教授ありがとうございます。 なるほどそうなのか、と言えたら良いのですが、読んでも読んでもモヤが掛かっている状態です。 Invalidate() メソッド⇒コントロールの全体を無効化 InvalidateRow() メソッド⇒ 行のみ無効化 転送量(反応速度)を見ながらこの二つを使い分ける、と理解致しました。(^^)
KOZ6.0

2023/01/30 08:56

その理解でOKです。
guest

回答2

0

ベストアンサー

C#

1 private int prevSelectRow = -1; 2 private void dataGridView1_SelectionChanged(object sender, EventArgs e) 3 { 4 if (prevSelectRow != -1) dataGridView1.InvalidateRow(prevSelectRow); 5 if (dataGridView1.SelectedCells.Count != 0) 6 { 7 prevSelectRow = dataGridView1.SelectedCells[0].RowIndex; 8 dataGridView1.InvalidateRow(prevSelectRow); 9 } 10 }

このように前に選択されていた行を覚えておいて、選択が変更された時に前の選択行と現在の選択行を再描画してやると正常に動作しました。

投稿2023/01/30 04:59

YAmaGNZ

総合スコア10242

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

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

ohikazuma

2023/01/30 07:12 編集

ありがとうございました。 解決です。 YAmaGNZさんから頂いたコードの理解と若干の試行錯誤をしておりました。 頂いたコードでは、 任意のカラムの幅を小さくし任意のセルを選択した場合、 選択セルの行だけの横線が書き直され(短くなり)、 他の横線が(長いまま)残ってしまいます。 そこで… if (prevSelectRow != -1) dataGridView1.InvalidateRow(prevSelectRow); if (dataGridView1.SelectedCells.Count != 0) { prevSelectRow = dataGridView1.SelectedCells[0].RowIndex; dataGridView1.InvalidateRow(prevSelectRow); } を、 dataGridView1.Invalidate(); 一発で処理しようと思います。 よく判ってないのに乱暴ですが、うまくいったので…
guest

0

KOZ6.0さん、YAmaGNZさんありがとうございました。
自己解決ではないのですが、最終のコードを書き留めます。

C#

1 int widthAllCol = 0; //各カラム幅の合計値 2 //private int prevSelectRow = -1; 3 4 public Form1() 5 { 6 InitializeComponent(); 7 } 8 9 private void Form1_Load(object sender, EventArgs e) 10 { 11 // DataTableの個所は「猫の気ままなC#日記」さんのコードを利用させてもらってます。 12 // ありがとうございます。 13 14 DataTable dt = new DataTable("Table1"); 15 string[] id = { "S0001", "S0002", "S0003", "S0004", "S0005", "S0006" }; 16 string[] name = { "あんぱん", "メロンパン", "カレーパン", "いちごじゃむパン", "チョココロネ", "クロワッサン" }; 17 string[] value = { "100", "105", "110", "115", "120", "125" }; 18 19 // DataTableに列を追加します。 20 dt.Columns.Add("商品番号"); 21 dt.Columns.Add("商品名"); 22 dt.Columns.Add("価格"); 23 24 // DataTableにデータを格納します。 25 for (int i = 0; i < 5; i++) 26 { 27 dt.Rows.Add(dt.NewRow()); 28 dt.Rows[i]["商品番号"] = id[i]; 29 dt.Rows[i]["商品名"] = name[i]; 30 dt.Rows[i]["価格"] = value[i]; 31 } 32 33 //dataGridView1に表示 34 dataGridView1.DataSource = dt; 35 36 //dataGridView1の基本設定 37 dataGridView1.ReadOnly = true; //読取専用 38 dataGridView1.AllowUserToDeleteRows = false; //行削除禁止 39 dataGridView1.AllowUserToAddRows = false; //行挿入禁止 40 dataGridView1.AllowUserToResizeRows = false; //行の高さ変更禁止 41 dataGridView1.RowHeadersVisible = false; //行ヘッダーを非表示にする 42 dataGridView1.MultiSelect = true; //セル、行、列が複数選択禁止 43 //dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect; //セルを選択すると行全体が選択されるようにする 44 45 widthAllCol = 0; 46 for (int i = 0; i < dataGridView1.ColumnCount; i++) 47 { 48 widthAllCol += dataGridView1.Columns[i].Width; //各カラム幅の合計値 49 } 50 } 51 52 53 private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) 54 { 55 DataGridView dgv = (DataGridView)sender; 56 57 Pen penRed = new Pen(Color.Red, 2); 58 Pen penOriginal = new Pen(SystemColors.ControlDark, 1); //デフォルトの罫線の色 59 60 if (e.RowIndex == dgv.CurrentRow.Index) 61 { 62 //オリジナルコード 63 //参考にさせて頂いた先 64 //http://ssk-development.blogspot.com/2017/09/datagridview_5.html 65 //https://tt195361.hatenablog.com/entry/2015/05/22/164840 66 //int startX = dgv.RowHeadersVisible ? dgv.RowHeadersWidth : 0; 67 //int startY = e.RowBounds.Top + 1; 68 //int endX = startX + dgv.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - dgv.HorizontalScrollingOffset; 69 70 int startX = 0; 71 int startY = e.RowBounds.Top + e.RowBounds.Height - 1; 72 int endX = widthAllCol; 73 74 e.Graphics.DrawLine(penRed, 75 startX, 76 startY, 77 endX, 78 startY); 79 } 80 else 81 { 82 int startX = 0; 83 int startY = e.RowBounds.Top + e.RowBounds.Height - 1; 84 int endX = widthAllCol; 85 86 e.Graphics.DrawLine(penOriginal, 87 startX, 88 startY, 89 endX, 90 startY); 91 } 92 } 93 94 95 private void dataGridView1_SelectionChanged(object sender, EventArgs e) 96 { 97 DataGridView dgv = (DataGridView)sender; 98 99 widthAllCol = 0; 100 for (int i = 0; i < dgv.ColumnCount; i++) 101 { 102 widthAllCol += dgv.Columns[i].Width; //各カラム幅の合計値 103 } 104 105 //描画指示 106 dataGridView1.Invalidate(); 107 108 //if (prevSelectRow != -1) dataGridView1.InvalidateRow(prevSelectRow); 109 //if (dataGridView1.SelectedCells.Count != 0) 110 //{ 111 // prevSelectRow = dataGridView1.SelectedCells[0].RowIndex; 112 // dataGridView1.InvalidateRow(prevSelectRow); 113 //} 114 } 115 116 117

投稿2023/01/30 07:21

ohikazuma

総合スコア9

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問