Files
HLib/Forms/DgvHelper.cs
T
2026-06-01 13:23:34 +09:00

727 lines
29 KiB
C#
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using KssSmaPlaLib.Commons;
using KssSmaPlaLib.Forms.ContextMenuFunction;
using KssSmaPlaLib.Ini;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace KssSmaPlaLib.Forms
{
public class DgvHelper
{
/// <summary>
/// 汎用的な初期化
/// </summary>
/// <param name="dgv"></param>
public static void Initialize(DataGridView dgv)
{
// 行選択
dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
// 行ヘッダ消去
dgv.RowHeadersVisible = false;
// 編集不可
dgv.ReadOnly = true;
// ユーザ操作制限
dgv.AllowUserToAddRows = false;
dgv.AllowUserToDeleteRows = false;
dgv.AllowUserToOrderColumns = false;
dgv.AllowUserToResizeColumns = true;
dgv.AllowUserToResizeRows = false;
// 列ヘッダはセンタリング
dgv.ColumnHeadersDefaultCellStyle.Alignment=DataGridViewContentAlignment.MiddleCenter;
//// ソート用余白を消す
//dgv.ColumnHeadersDefaultCellStyle.Padding = new Padding(0);
// セル内余白(上下左右)をゼロに
dgv.DefaultCellStyle.Padding = new Padding(0);
// 行高さの自動調整
dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
// ダブルバッファ(プロパティが隠れている)
var type = typeof(DataGridView);
var prop = type.GetProperty("DoubleBuffered",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
prop.SetValue(dgv, true, null);
}
/// <summary>
/// 列ヘッダのソート用余白を消す
/// ※列が確定してからCallすること
/// </summary>
/// <param name="dgv"></param>
//public static void ClearColumnHeaderSortYohaku(DataGridView dgv)
//{
// // 列ヘッダの幅にソート用の幅は不要
// //foreach (DataGridViewColumn col in dgv.Columns) col.SortMode = DataGridViewColumnSortMode.NotSortable;
// // 何をやってもダメなので、テキストの先頭に余白入れます!👉ダメ!orz
// //const string YOHAKU = "       ";
// //foreach (DataGridViewColumn col in dgv.Columns) col.HeaderText = YOHAKU + col.HeaderText;
//}
// どちらのDTOでも使える共通バインド関数
public static void BindListWithAttributeHeaders<T>(
DataGridView grid,
IList<T> source,
bool isForce = false)
{
// 強制でないとき、データソースありなら何もしない
if (!(grid.DataSource is null) && !isForce) return;
// データバインド完了後にヘッダー等を属性で上書き
grid.DataBindingComplete += (_, __) => ApplyAttributeHeaders<T>(grid);
// 「とりあえず ToString() で文字列表示」したい場合の統一フォーマット
grid.CellFormatting += (_, e) =>
{
try
{
// AutoGenerateColumnsだと各セルの Value はプロパティ値になります
// ここで ToString() 統一(nullは空文字に)
if (e.Value == null) { e.Value = string.Empty; e.FormattingApplied = true; return; }
// 文化依存や日付/数値の既定書式を崩したくなければコメントアウト推奨
if (!(e.Value is bool) &&
string.IsNullOrEmpty(grid.Columns[e.ColumnIndex].DefaultCellStyle.Format))
{
e.Value = e.Value.ToString();
e.FormattingApplied = true;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
};
grid.AutoGenerateColumns = true;
// 変更通知を効かせたい場合は BindingList に包む
var bindingList = source is BindingList<T> bl ? bl : new BindingList<T>(source);
// BindingSource 経由が安定
var bs = new BindingSource { DataSource = bindingList };
grid.DataSource = bs;
}
private const string KEY_NAME_DISPLAY_NAME_CSV = "DisplayNameCsv";
/// <summary>
/// プライベートな子関数
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="grid"></param>
public static void ApplyAttributeHeaders<T>(DataGridView grid)
{
// 編集した表示名
var csv = IniHelper.ReadValue(null, grid.Parent.Name + "." + grid.Name, KEY_NAME_DISPLAY_NAME_CSV);
var props = TypeDescriptor.GetProperties(typeof(T))
.Cast<PropertyDescriptor>()
.ToDictionary(p => p.Name);
foreach (DataGridViewColumn col in grid.Columns)
{
var key = col.DataPropertyName ?? col.Name;
if (key is null) continue;
if (props.TryGetValue(key, out var pd))
{
// タグに入れておく(PropertyDescriptor型)
col.Tag = pd;
// 列ヘッダーを [DisplayName] に
col.HeaderText = pd.DisplayName;
// 表示名を登録されてたら、置き換える
var editName = StringUxer.GetStringByIndexFormCsv(csv,col.Index,string.Empty);
if (!string.IsNullOrEmpty(editName)) col.HeaderText = editName;
// 文字寄せ
var alignment =
(col.HeaderText.IndexOf("[R]") >= 0 ? DataGridViewContentAlignment.MiddleRight :
(col.HeaderText.IndexOf("[C]") >= 0 ? DataGridViewContentAlignment.MiddleCenter :
DataGridViewContentAlignment.MiddleLeft));
col.DefaultCellStyle.Alignment = alignment;
col.HeaderText = col.HeaderText.Replace("[L]", "").Replace("[C]", "").Replace("[R]", "");
// [Browsable(false)] の列は非表示(AutoGenerateでも列が立つ場合の保険)
var browsableAttr = pd.Attributes.OfType<BrowsableAttribute>().FirstOrDefault();
if (browsableAttr != null && !browsableAttr.Browsable)
{
col.Visible = false;
continue;
}
// 読み取り専用属性の尊重
col.ReadOnly = pd.IsReadOnly;
// 便利系:ソートを自動に
// col.SortMode = DataGridViewColumnSortMode.Automatic;
// 必要なら列幅の初期値
// col.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
}
}
}
private const string INI_KEY_COL_WIDTH_CSV = "ColWidthCsv";
/// <summary>
/// 列幅保存(FormのFormClosingにてCall
/// </summary>
/// <param name="dgv"></param>
public static void DgvSaveColWidth(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
var list = new List<int>();
foreach(DataGridViewColumn col in dgv.Columns) list.Add(col.Width);
IniHelper.WriteValue(null, section, INI_KEY_COL_WIDTH_CSV, string.Join(",", list));
}
/// <summary>
/// 列幅復元(FormのコンストラクタにてCall)
/// </summary>
/// <param name="dgv"></param>
public static void DgvLoadColWidth(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
var list = IniHelper.ReadValue(null, section,INI_KEY_COL_WIDTH_CSV).Split(',').ToList();
foreach (DataGridViewColumn col in dgv.Columns)
{
if (list.Count - 1 < col.Index) break;
if (!int.TryParse(list[col.Index], out int val)) break;
col.Width = val;
}
}
private const string INI_KEY_FONT_CSV = "Font";
public static void DgvSelectFont(Form form, DataGridView dgv)
{
if (!FontUxer.SelectFont(form, form.Name, dgv.Name + ".Font", dgv.DefaultCellStyle.Font, out var font)) return;
DgvSetFontFromFont(dgv, font);
DgvSaveFontToIni(dgv);
}
public static void DgvSetFontFromFont(DataGridView dgv, Font font)
{
var font2 = new Font(font.Name, font.Size, font.Bold ? FontStyle.Bold : FontStyle.Regular);
dgv.DefaultCellStyle.Font = font2;
dgv.ColumnHeadersDefaultCellStyle.Font = font2;
dgv.AutoResizeRows();
}
public static void DgvSaveFontToIni(DataGridView dgv)
{
var fontCsv =
dgv.ColumnHeadersDefaultCellStyle.Font.Name + "," +
dgv.ColumnHeadersDefaultCellStyle.Font.Size.ToString() + "," +
dgv.ColumnHeadersDefaultCellStyle.Font.Bold.ToString();
IniHelper.WriteValue(null, dgv.Parent.Name + "." + dgv.Name, INI_KEY_FONT_CSV, fontCsv);
}
public static void DgvLoadFontFromIni(DataGridView dgv)
{
var defaultFontCsv =
dgv.ColumnHeadersDefaultCellStyle.Font.Name + "," +
dgv.ColumnHeadersDefaultCellStyle.Font.Size.ToString() + "," +
dgv.ColumnHeadersDefaultCellStyle.Font.Bold.ToString();
var iniFontCsv = IniHelper.ReadValue(null, dgv.Parent.Name + "." + dgv.Name, INI_KEY_FONT_CSV, defaultFontCsv);
var fontItems = iniFontCsv.Split(',');
if (fontItems.Length != 3) fontItems = defaultFontCsv.Split(',');
if (float.TryParse(fontItems[1], out var size) == false) size = dgv.ColumnHeadersDefaultCellStyle.Font.Size;
if (bool.TryParse(fontItems[2], out var bold) == false) bold = dgv.ColumnHeadersDefaultCellStyle.Font.Bold;
var font = new Font(fontItems[0], size, bold ? FontStyle.Bold : FontStyle.Regular);
dgv.DefaultCellStyle.Font = font;
dgv.ColumnHeadersDefaultCellStyle.Font = font;
dgv.AutoResizeRows();
}
private const string KEY_NAME_DISPLAY_INDEX_CSV = "DisplayIndexCsv";
private const string KEY_NAME_COLUMN_VISIBLE_CSV = "ColumnVisibleCsv";
/// <summary>
/// INIの列表示順をDGVに反映
/// </summary>
/// <param name="dgv"></param>
public static void SetColumnDisplayIndexFromIni(DataGridView dgv)
{
// 表示順(DisplayIndex)の設定
var csv = IniHelper.ReadValue(null, dgv.Parent.Name + "." + dgv.Name, KEY_NAME_DISPLAY_INDEX_CSV);
if (string.IsNullOrEmpty(csv)) return;
var items = csv.Split(',');
if (items.Length != dgv.Columns.Count) return;
List<int> displayIndexList = new List<int>();
foreach ( var item in items) displayIndexList.Add(int.Parse(item));
for (var cnt = 0; cnt < dgv.Columns.Count; cnt++)
{
var index = displayIndexList.IndexOf(cnt);
dgv.Columns[index].DisplayIndex = cnt;
}
// 列の表示/非表示の設定
csv = IniHelper.ReadValue(null, dgv.Parent.Name + "." + dgv.Name, KEY_NAME_COLUMN_VISIBLE_CSV);
if (string.IsNullOrEmpty(csv)) return;
items = csv.Split(',');
if (items.Length != dgv.Columns.Count) return;
List<bool> visibleList = new List<bool>();
foreach (var item in items) visibleList.Add(bool.TryParse(item, out var boolValue) ? boolValue : true);
for (var cnt = 0; cnt < dgv.Columns.Count; cnt++)
{
dgv.Columns[cnt].Visible = visibleList[cnt];
}
}
public static void MoveRow(DataGridView dgv, int rowIndex, bool moveUp)
{
// 移動先のインデックスを計算
int targetIndex = moveUp ? rowIndex - 1 : rowIndex + 1;
// 範囲チェック(一番上より上、または一番下より下には行かない)
if (targetIndex < 0 || targetIndex >= dgv.Rows.Count - (dgv.AllowUserToAddRows ? 1 : 0))
{
return;
}
// 選択状態などを保持したい場合の準備
DataGridViewRow row = dgv.Rows[rowIndex];
// 一旦削除して挿入し直す
dgv.Rows.RemoveAt(rowIndex);
dgv.Rows.Insert(targetIndex, row);
// 移動後の行を選択状態にする(ユーザー体験向上)
dgv.ClearSelection();
dgv.Rows[targetIndex].Selected = true;
dgv.CurrentCell = dgv.Rows[targetIndex].Cells[0];
}
private const string INI_KEY_DGV_ROW_HEADER_HEIGHT = "DgvRowHeaderHeight";
/// <summary>
/// ヘッダ行の高さ保存
/// </summary>
/// <param name="dgv"></param>
public static void DgvSaveHeaderHeight(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
IniHelper.WriteValue(null, section, INI_KEY_DGV_ROW_HEADER_HEIGHT, dgv.ColumnHeadersHeight.ToString());
}
/// <summary>
/// ヘッダ行の高さロード
/// </summary>
/// <param name="dgv"></param>
public static void DgvLoadHeaderHeight(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
var str = IniHelper.ReadValue(null, section, INI_KEY_DGV_ROW_HEADER_HEIGHT, dgv.ColumnHeadersHeight.ToString());
dgv.ColumnHeadersHeight = int.TryParse(str, out var intValue) ? intValue : dgv.ColumnHeadersHeight;
}
/// <summary>
/// 非表示列の再表示
/// </summary>
/// <param name="dgv"></param>
public static void DgvRedispAllColumn(DataGridView dgv)
{
foreach (DataGridViewColumn col in dgv.Columns) col.Visible = true;
}
private const string INI_KEY_COL_VISIBLE_CSV = "ColVisibleCsv";
/// <summary>
/// 列の表示非表示を保存
/// </summary>
/// <param name="dgv"></param>
public static void DgvSaveColVisible(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
var list = new List<bool>();
foreach (DataGridViewColumn col in dgv.Columns) list.Add(col.Visible);
IniHelper.WriteValue(null, section, INI_KEY_COL_VISIBLE_CSV, string.Join(",", list));
}
/// <summary>
/// 列の表示非表示をロード
/// </summary>
/// <param name="dgv"></param>
public static void DgvLoadColVisible(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
var list = IniHelper.ReadValue(null, section, INI_KEY_COL_VISIBLE_CSV).Split(',').ToList();
foreach (DataGridViewColumn col in dgv.Columns)
{
if (list.Count - 1 < col.Index) break;
if (!bool.TryParse(list[col.Index], out bool val)) break;
col.Visible = val;
}
}
private const string INI_KEY_COL_DISPLAY_INDEX_CSV = "ColDisplayIndexCsv";
/// <summary>
/// 列の表示順インデックスを保存
/// </summary>
/// <param name="dgv"></param>
public static void DgvSaveColDisplayIndex(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
var list = new List<string>();
foreach (DataGridViewColumn col in dgv.Columns) list.Add($"{col.Name}={col.DisplayIndex}");
IniHelper.WriteValue(null, section, INI_KEY_COL_DISPLAY_INDEX_CSV, string.Join(",", list));
}
/// <summary>
/// 列の表示順インデックスをロード
/// </summary>
/// <param name="dgv"></param>
public static void DgvLoadColDisplayIndex(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
var list = IniHelper.ReadValue(null, section, INI_KEY_COL_DISPLAY_INDEX_CSV).Split(',').ToList();
if (list.Count == 1 && string.IsNullOrEmpty(list[0])) return;
Dictionary<int,string> dic = new Dictionary<int,string>();
foreach (var str in list)
{
dic.Add(int.Parse(str.Split('=')[1]), str.Split('=')[0]);
}
// DisplayIndexは小さい方から設定しないと崩れる
foreach (var di in dic.Keys.OrderBy(m => m))
{
dgv.Columns[dic[di]].DisplayIndex = di;
}
}
/// <summary>
/// 列幅の自動調整
/// </summary>
/// <param name="dgv"></param>
public static void DgvColumnWidthAutoAdjust(DataGridView dgv)
{
dgv.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
}
/// <summary>
/// 行高さの自動調整
/// </summary>
/// <param name="dgv"></param>
public static void DgvRowHeightAutoAdjust(DataGridView dgv)
{
dgv.AutoResizeRows(DataGridViewAutoSizeRowsMode.AllCells);
}
/// <summary>
/// 列固定
/// </summary>
/// <param name="dgv"></param>
public static void DgvColumnFix(DataGridView dgv)
{
if (dgv.CurrentCell == null) return;
// 1. 今クリックした列の「見た目の位置」を取得
int targetDisplayIndex = dgv.CurrentCell.OwningColumn.DisplayIndex;
// 2. いったん全列の固定を解除(これをしないとエラーになる場合がある)
DgvColumnFixKaijo(dgv);
// 3. 見た目の位置(DisplayIndex)が、ターゲット以下の列をすべて固定!
// ※DisplayIndexの小さい順(左側から順)にFrozenをかけていくのがコツ
var targetColumns = dgv.Columns.Cast<DataGridViewColumn>()
.OrderBy(c => c.DisplayIndex);
foreach (var col in targetColumns)
{
if (col.DisplayIndex <= targetDisplayIndex)
{
col.Frozen = true;
}
}
}
/// <summary>
/// 列固定解除
/// </summary>
/// <param name="dgv"></param>
public static void DgvColumnFixKaijo(DataGridView dgv)
{
// 2. いったん全列の固定を解除(これをしないとエラーになる場合がある)
foreach (DataGridViewColumn col in dgv.Columns)
{
col.Frozen = false;
}
}
private const string INI_KEY_COL_FROZEN_CSV = "ColFrozenCsv";
/// <summary>
/// 列の固定を保存
/// </summary>
/// <param name="dgv"></param>
public static void DgvSaveColFrozen(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
var list = new List<bool>();
foreach (DataGridViewColumn col in dgv.Columns) list.Add(col.Frozen);
IniHelper.WriteValue(null, section, INI_KEY_COL_FROZEN_CSV, string.Join(",", list));
}
/// <summary>
/// 列の固定をロード
/// </summary>
/// <param name="dgv"></param>
public static void DgvLoadColFrozen(DataGridView dgv)
{
var section = dgv.Parent.Name + "." + dgv.Name;
var list = IniHelper.ReadValue(null, section, INI_KEY_COL_FROZEN_CSV).Split(',').ToList();
foreach (DataGridViewColumn col in dgv.Columns)
{
if (list.Count - 1 < col.Index) break;
if (!bool.TryParse(list[col.Index], out bool val)) break;
col.Frozen = val;
}
}
private const string INI_KEY_DGV_HEADER_BACK_COLOR = "DgvHeaderBackColor";
/// <summary>
/// ヘッダ背景色選択
/// </summary>
/// <param name="form"></param>
/// <param name="dgv"></param>
public static void DgvSelectHeaderBackColor(Form form, DataGridView dgv)
{
using (ColorDialog cd = new ColorDialog())
{
// 今の色を初期選択にしておく
cd.Color = dgv.ColumnHeadersDefaultCellStyle.BackColor;
if (cd.ShowDialog() == DialogResult.OK)
{
// 1. フォームの背景色を即時変更
dgv.ColumnHeadersDefaultCellStyle.BackColor = cd.Color;
// 文字色を計算して算出し設定
dgv.ColumnHeadersDefaultCellStyle.ForeColor = ColorHelper.GetContrastColor(dgv.ColumnHeadersDefaultCellStyle.BackColor);
// 2. 🔥 Colorを16進数(#RRGGBB)に変換してINIに書き込み
string colorHex = ColorTranslator.ToHtml(cd.Color);
IniHelper.WriteValue(null, form.Name, INI_KEY_DGV_HEADER_BACK_COLOR, colorHex);
}
}
}
/// <summary>
/// ヘッダ背景色(名前でマッチした列のみ)選択
/// </summary>
/// <param name="form"></param>
/// <param name="dgv"></param>
/// <param name="colNamePattern"></param>
/// <param name="iniKey"></param>
public static void DgvSelectHeaderBackColorWithNameMatch(Form form, DataGridView dgv, string colNamePattern, string iniKey)
{
// マッチする列を探し、現在の色を採取
Color? color = null;
foreach (DataGridViewColumn col in dgv.Columns)
{
if (StringUxer.WildcardMatch(col.Name, colNamePattern))
{
color = col.HeaderCell.Style.BackColor;
}
}
if (color == null)
{
MessageBox.Show(form, $"パターンにマッチする列なし[パターン:{colNamePattern}]");
return;
}
using (ColorDialog cd = new ColorDialog())
{
// 今の色を初期選択にしておく
cd.Color = (Color)color;
if (cd.ShowDialog() == DialogResult.OK)
{
var backColor = cd.Color;
// 文字色を計算して算出し設定
var foreColor = ColorHelper.GetContrastColor(backColor);
// DGVに反映
foreach (DataGridViewColumn col in dgv.Columns)
{
if (StringUxer.WildcardMatch(col.Name, colNamePattern))
{
col.HeaderCell.Style.BackColor = backColor;
col.HeaderCell.Style.ForeColor = foreColor;
}
}
// 2. 🔥 Colorを16進数(#RRGGBB)に変換してINIに書き込み
string colorHex = ColorTranslator.ToHtml(cd.Color);
IniHelper.WriteValue(null, form.Name, iniKey, colorHex);
}
}
}
/// <summary>
/// ヘッダ背景色をINIから取得して設定
/// </summary>
/// <param name="form"></param>
public static void DgvSetHeaderBackColorFromIni(Form form, DataGridView dgv)
{
var colorHex = IniHelper.ReadValue(null, form.Name, INI_KEY_DGV_HEADER_BACK_COLOR, ColorTranslator.ToHtml(dgv.ColumnHeadersDefaultCellStyle.BackColor));
dgv.ColumnHeadersDefaultCellStyle.BackColor = ColorTranslator.FromHtml(colorHex);
// 文字色を計算して算出し設定
dgv.ColumnHeadersDefaultCellStyle.ForeColor = ColorHelper.GetContrastColor(dgv.ColumnHeadersDefaultCellStyle.BackColor);
}
/// <summary>
/// ヘッダ背景色をINIから取得して設定(名前マッチング)
/// </summary>
/// <param name="form"></param>
/// <param name="dgv"></param>
/// <param name="colNamePattern"></param>
/// <param name="iniKey"></param>
public static void DgvSetHeaderBackColorFromIniWithNameMatch(Form form, DataGridView dgv, string colNamePattern, string iniKey)
{
// マッチする列を探し、現在の色を採取
Color? color = null;
foreach (DataGridViewColumn col in dgv.Columns)
{
if (StringUxer.WildcardMatch(col.Name, colNamePattern))
{
color = col.HeaderCell.Style.BackColor;
}
}
if (color == null)
{
MessageBox.Show(form, $"パターンにマッチする列なし[パターン:{colNamePattern}]");
return;
}
// INIから取得
var colorHex = IniHelper.ReadValue(null, form.Name, iniKey, ColorTranslator.ToHtml((Color)color));
var backColor = ColorTranslator.FromHtml(colorHex);
var foreColor = ColorHelper.GetContrastColor(backColor);
// マッチする列に設定
foreach (DataGridViewColumn col in dgv.Columns)
{
if (StringUxer.WildcardMatch(col.Name, colNamePattern))
{
col.HeaderCell.Style.BackColor = backColor;
col.HeaderCell.Style.ForeColor = foreColor;
}
}
}
public const string TSV_PATH = @".\@@@DGV@@@.tsv";
/// <summary>
/// DGVデータTSV保存
/// </summary>
/// <param name="form"></param>
/// <param name="dgv"></param>
public static void SaveGridDataToTsv(Form form, DataGridView dgv)
{
var path = TSV_PATH.Replace("@@@DGV@@@", $"{form.Name}.{dgv.Name}");
try
{
using (StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8))
{
foreach (DataGridViewRow row in dgv.Rows)
{
// 新規行(入力用の空行)はスキップ
if (row.IsNewRow) continue;
// 各セルの値をタブで結合
var cells = row.Cells.Cast<DataGridViewCell>().Select(c => c.Value?.ToString() ?? "");
sw.WriteLine(string.Join("\t", cells));
}
}
}
catch (Exception ex)
{
MessageBox.Show("DGVのTSV保存に失敗しました: " + ex.Message);
}
}
public static void LoadGridDataToTsv(Form form, DataGridView dgv)
{
var path = TSV_PATH.Replace("@@@DGV@@@", $"{form.Name}.{dgv.Name}");
if (!File.Exists(path)) return;
try
{
dgv.Rows.Clear();
string[] lines = File.ReadAllLines(path, Encoding.UTF8);
foreach (string line in lines)
{
if (string.IsNullOrWhiteSpace(line)) continue;
// タブで分割して行を追加
string[] values = line.Split('\t');
dgv.Rows.Add(values);
}
}
catch (Exception ex)
{
MessageBox.Show("DGVのTSVによる復元に失敗しました: " + ex.Message);
}
}
public static void SearchGrid(
DataGridView dgv,
string searchText,
bool forward)
{
if (string.IsNullOrEmpty(searchText)) return;
int rowCount = dgv.RowCount;
int colCount = dgv.ColumnCount;
// 現在のセルの位置を取得(選択されていなければ 0,0 から)
int startRow = dgv.CurrentCell?.RowIndex ?? 0;
int startCol = dgv.CurrentCell?.ColumnIndex ?? 0;
// 全セルをフラットなインデックスとして考えて計算
int totalCells = rowCount * colCount;
int startIndex = (startRow * colCount) + startCol;
for (int i = 1; i < totalCells; i++)
{
// forwardがtrueならプラス、falseならマイナス方向にずらす
int offset = forward ? i : -i;
int nextIndex = (startIndex + offset + totalCells) % totalCells;
int r = nextIndex / colCount;
int c = nextIndex % colCount;
DataGridViewCell cell = dgv[c, r];
if (cell.Value != null && cell.Value.ToString().Contains(searchText))
{
dgv.CurrentCell = cell;
return; // 見つかったら終了
}
}
MessageBox.Show("他には見つかりませんでしたにゃ!🐾");
}
}
}