前提
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
Control.Invalidate メソッド
https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.control.invalidate?view=netframework-4.5.2
を実行したらどうなりますか?
試してみます。
暫くお待ちください。
できました、ドンピシャです。
ありがとうございます。
幾つか試してみて、_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();
}
セルをクリックした際と、矢印キーで選択セルを変更した際に、赤線を引くには、
_SelectionChanged
が良さそうですね。
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 は、コントロールの一部分のみを無効化します。
うまく使えば画面への転送量が抑えられるため高速になります。
KOZ6.0さん、素人相手にご教授ありがとうございます。
なるほどそうなのか、と言えたら良いのですが、読んでも読んでもモヤが掛かっている状態です。
Invalidate() メソッド⇒コントロールの全体を無効化
InvalidateRow() メソッド⇒ 行のみ無効化
転送量(反応速度)を見ながらこの二つを使い分ける、と理解致しました。(^^)
その理解でOKです。
回答2件
あなたの回答
tips
プレビュー


