for Push.

This commit is contained in:
nobobo
2026-06-01 13:23:34 +09:00
parent 9e093ea99f
commit a6550e9928
93 changed files with 10393 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
using DocumentFormat.OpenXml.Wordprocessing;
using KssSmaPlaLib.Ini;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Color = System.Drawing.Color;
namespace KssSmaPlaLib.Forms
{
public static class ColorHelper
{
/// <summary>
/// 色選択
/// </summary>
/// <param name="form"></param>
/// <param name="defaultColor"></param>
/// <param name="iniKey"></param>
/// <returns></returns>
public static Color? SelectColor(Form form, Color defaultColor, string iniKey = "")
{
using (ColorDialog cd = new ColorDialog())
{
// 今の色を初期選択にしておく
cd.Color = defaultColor;
if (cd.ShowDialog(form) != DialogResult.OK) return null;
if (!string.IsNullOrEmpty(iniKey))
{
// 2. 🔥 Colorを16進数(#RRGGBB)に変換してINIに書き込み
string colorHex = ColorTranslator.ToHtml(cd.Color);
IniHelper.WriteValue(null, form.Name, iniKey, colorHex);
}
return cd.Color;
}
}
/// <summary>
/// Colorを汎用的なHTML色コード文字列に変換する
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static string ColorToHtmlColorString(Color color)
{
return ColorTranslator.ToHtml(color);
}
/// <summary>
/// 汎用的なHTML色コード文字列からColorに変換する
/// </summary>
/// <param name="htmlColorString"></param>
/// <returns></returns>
public static Color HtmlColorStringToColor(string htmlColorString)
{
return ColorTranslator.FromHtml(htmlColorString);
}
/// <summary>
/// 背景色から最適な文字色(黒/白)を返すメソッド
/// </summary>
/// <param name="backColor"></param>
/// <returns></returns>
public static Color GetContrastColor(Color backColor)
{
// 輝度計算 (ITU-R BT.601)
double luminance = (0.299 * backColor.R) + (0.587 * backColor.G) + (0.114 * backColor.B);
return (luminance > 128) ? Color.Black : Color.White;
}
}
}
+74
View File
@@ -0,0 +1,74 @@
using KssSmaPlaLib.Ini;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace KssSmaPlaLib.Forms.ContextMenuFunction
{
public static class FontUxer
{
/// <summary>
/// フォント選択
/// </summary>
/// <param name="form"></param>
/// <param name="section"></param>
/// <param name="key"></param>
/// <param name="defaultFont"></param>
/// <param name="resultFont"></param>
/// <returns></returns>
public static bool SelectFont(Form form, string section, string key, Font defaultFont, out Font resultFont)
{
// デフォルト値
resultFont = new Font(defaultFont, defaultFont.Style);
using (var dlg = new FontDialog())
{
var defaultFontCsv =
defaultFont.Name + "," +
defaultFont.Size.ToString() + "," +
defaultFont.Bold.ToString();
var iniFontCsv = IniHelper.ReadValue(null, section, key, defaultFontCsv);
var fontItems = iniFontCsv.Split(',');
if (fontItems.Length != 3) fontItems = defaultFontCsv.Split(',');
if (float.TryParse(fontItems[1], out var size) == false) size = defaultFont.Size;
if (bool.TryParse(fontItems[2], out var bold) == false) bold = defaultFont.Bold;
dlg.Font = new Font(fontItems[0], size, bold ? FontStyle.Bold : FontStyle.Regular);
dlg.AllowVerticalFonts = false;
// 文字飾り(取り消し線と下線)を表示させない
dlg.ShowEffects = false;
var result = dlg.ShowDialog(form);
if (result == DialogResult.Cancel) return false;
// INI保存
var fontCsv =
dlg.Font.Name + "," +
dlg.Font.Size.ToString() + "," +
dlg.Font.Bold.ToString();
IniHelper.WriteValue(null, section, key, fontCsv);
// 出力
resultFont = new Font(dlg.Font, dlg.Font.Style);
return true;
}
}
public static Font GetFont(string section, string key, Font defaultFont)
{
var defaultFontCsv =
defaultFont.Name + "," +
defaultFont.Size.ToString() + "," +
defaultFont.Bold.ToString();
var iniFontCsv = IniHelper.ReadValue(null, section, key, defaultFontCsv);
var fontItems = iniFontCsv.Split(',');
if (fontItems.Length != 3) fontItems = defaultFontCsv.Split(',');
if (float.TryParse(fontItems[1], out var size) == false) size = defaultFont.Size;
if (bool.TryParse(fontItems[2], out var bold) == false) bold = defaultFont.Bold;
return new Font(fontItems[0], size, bold ? FontStyle.Bold : FontStyle.Regular);
}
}
}
@@ -0,0 +1,197 @@
namespace KssSmaPlaLib.Forms.ContextMenuFunction.Gamen
{
partial class DgvColumnEditorForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.ColumnDgv = new System.Windows.Forms.DataGridView();
this.CancelButton2 = new System.Windows.Forms.Button();
this.OkButton = new System.Windows.Forms.Button();
this.colPropertyName = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colDefaultDisplayName = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colDisplayName = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.UpButton = new System.Windows.Forms.Button();
this.DownButton = new System.Windows.Forms.Button();
this.PopupMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
this.EditDisplayNameMenu = new System.Windows.Forms.ToolStripMenuItem();
this.VisibleMenu = new System.Windows.Forms.ToolStripMenuItem();
((System.ComponentModel.ISupportInitialize)(this.ColumnDgv)).BeginInit();
this.PopupMenu.SuspendLayout();
this.SuspendLayout();
//
// ColumnDgv
//
this.ColumnDgv.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.ColumnDgv.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.ColumnDgv.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.colPropertyName,
this.colDefaultDisplayName,
this.colDisplayName});
this.ColumnDgv.Location = new System.Drawing.Point(15, 16);
this.ColumnDgv.Margin = new System.Windows.Forms.Padding(6, 7, 6, 7);
this.ColumnDgv.Name = "ColumnDgv";
this.ColumnDgv.RowHeadersWidth = 62;
this.ColumnDgv.RowTemplate.Height = 27;
this.ColumnDgv.Size = new System.Drawing.Size(505, 429);
this.ColumnDgv.TabIndex = 0;
this.ColumnDgv.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ColumnDgv_KeyDown);
//
// CancelButton2
//
this.CancelButton2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.CancelButton2.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.CancelButton2.Location = new System.Drawing.Point(346, 455);
this.CancelButton2.Name = "CancelButton2";
this.CancelButton2.Size = new System.Drawing.Size(177, 58);
this.CancelButton2.TabIndex = 1;
this.CancelButton2.Text = "キャンセル";
this.CancelButton2.UseVisualStyleBackColor = true;
//
// OkButton
//
this.OkButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.OkButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.OkButton.Location = new System.Drawing.Point(163, 455);
this.OkButton.Name = "OkButton";
this.OkButton.Size = new System.Drawing.Size(177, 58);
this.OkButton.TabIndex = 2;
this.OkButton.Text = "OK";
this.OkButton.UseVisualStyleBackColor = true;
this.OkButton.Click += new System.EventHandler(this.OkButton_Click);
//
// colPropertyName
//
this.colPropertyName.HeaderText = "識別名";
this.colPropertyName.MinimumWidth = 8;
this.colPropertyName.Name = "colPropertyName";
this.colPropertyName.Width = 150;
//
// colDefaultDisplayName
//
this.colDefaultDisplayName.HeaderText = "デフォ表示名";
this.colDefaultDisplayName.MinimumWidth = 8;
this.colDefaultDisplayName.Name = "colDefaultDisplayName";
this.colDefaultDisplayName.Width = 150;
//
// colDisplayName
//
this.colDisplayName.HeaderText = "表示名";
this.colDisplayName.MinimumWidth = 8;
this.colDisplayName.Name = "colDisplayName";
this.colDisplayName.Width = 150;
//
// UpButton
//
this.UpButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.UpButton.Location = new System.Drawing.Point(15, 455);
this.UpButton.Name = "UpButton";
this.UpButton.Size = new System.Drawing.Size(59, 58);
this.UpButton.TabIndex = 3;
this.UpButton.Text = "👆";
this.UpButton.UseVisualStyleBackColor = true;
this.UpButton.Click += new System.EventHandler(this.UpButton_Click);
//
// DownButton
//
this.DownButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.DownButton.Location = new System.Drawing.Point(80, 455);
this.DownButton.Name = "DownButton";
this.DownButton.Size = new System.Drawing.Size(59, 58);
this.DownButton.TabIndex = 4;
this.DownButton.Text = "👇";
this.DownButton.UseVisualStyleBackColor = true;
this.DownButton.Click += new System.EventHandler(this.DownButton_Click);
//
// PopupMenu
//
this.PopupMenu.Font = new System.Drawing.Font("メイリオ", 14F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
this.PopupMenu.ImageScalingSize = new System.Drawing.Size(24, 24);
this.PopupMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.EditDisplayNameMenu,
this.VisibleMenu});
this.PopupMenu.Name = "PopupMenu";
this.PopupMenu.Size = new System.Drawing.Size(394, 133);
//
// EditDisplayNameMenu
//
this.EditDisplayNameMenu.Name = "EditDisplayNameMenu";
this.EditDisplayNameMenu.Size = new System.Drawing.Size(393, 48);
this.EditDisplayNameMenu.Text = "表示名の編集...";
this.EditDisplayNameMenu.Click += new System.EventHandler(this.EditDisplayNameMenu_Click);
//
// VisibleMenu
//
this.VisibleMenu.Name = "VisibleMenu";
this.VisibleMenu.Size = new System.Drawing.Size(393, 48);
this.VisibleMenu.Text = "表示/非表示の切り替え";
this.VisibleMenu.Click += new System.EventHandler(this.VisibleMenu_Click);
//
// DgvColumnEditorForm
//
this.AcceptButton = this.OkButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(19F, 42F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.White;
this.CancelButton = this.CancelButton2;
this.ClientSize = new System.Drawing.Size(535, 525);
this.ContextMenuStrip = this.PopupMenu;
this.Controls.Add(this.DownButton);
this.Controls.Add(this.UpButton);
this.Controls.Add(this.OkButton);
this.Controls.Add(this.CancelButton2);
this.Controls.Add(this.ColumnDgv);
this.Font = new System.Drawing.Font("メイリオ", 14F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
this.Margin = new System.Windows.Forms.Padding(6, 7, 6, 7);
this.Name = "DgvColumnEditorForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "列一覧💖";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DgvColumnEditorForm_FormClosing);
this.Load += new System.EventHandler(this.DgvColumnEditorForm_Load);
((System.ComponentModel.ISupportInitialize)(this.ColumnDgv)).EndInit();
this.PopupMenu.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.DataGridView ColumnDgv;
private System.Windows.Forms.Button CancelButton2;
private System.Windows.Forms.Button OkButton;
private System.Windows.Forms.DataGridViewTextBoxColumn colPropertyName;
private System.Windows.Forms.DataGridViewTextBoxColumn colDefaultDisplayName;
private System.Windows.Forms.DataGridViewTextBoxColumn colDisplayName;
private System.Windows.Forms.Button UpButton;
private System.Windows.Forms.Button DownButton;
private System.Windows.Forms.ContextMenuStrip PopupMenu;
private System.Windows.Forms.ToolStripMenuItem EditDisplayNameMenu;
private System.Windows.Forms.ToolStripMenuItem VisibleMenu;
}
}
@@ -0,0 +1,334 @@
using KssSmaPlaLib.Commons;
using KssSmaPlaLib.Forms;
using KssSmaPlaLib.Ini;
using KssSmaPlaLib.Input.Gamen;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace KssSmaPlaLib.Forms.ContextMenuFunction.Gamen
{
public partial class DgvColumnEditorForm : Form
{
private const string COL_NAME_PROPERTY_NAME = "colPropertyName";
private const string COL_NAME_DEFAULT_DISPLAY_NAME = "colDefaultDisplayName";
private const string COL_NAME_DISPLAY_NAME = "colDisplayName";
/// <summary>
/// 対象のDGV
/// </summary>
private DataGridView _targetDgv = null;
/// <summary>
/// 対象DGVの列名から列インデックスを取得する辞書
/// (後ほど列インデックスが全てゼロになる事象が起きるため)
/// </summary>
private readonly Dictionary<string, int> _colNameToRowIndexDic = null;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="dgv"></param>
public DgvColumnEditorForm(DataGridView dgv)
{
InitializeComponent();
_targetDgv = dgv;
// 列名から列インデックスを取得する辞書の作成(後ほど列インデックスが全てゼロになる事象が起きるため)
_colNameToRowIndexDic = new Dictionary<string, int>();
foreach (DataGridViewColumn col in _targetDgv.Columns) _colNameToRowIndexDic.Add(col.Name, col.Index);
this.DialogResult = DialogResult.Cancel;
DgvHelper.Initialize(ColumnDgv);
ColumnDgv.Rows.Clear();
// 編集した表示名
var csv = IniHelper.ReadValue(null, _targetDgv.Parent.Name + "." + _targetDgv.Name, KEY_NAME_DISPLAY_NAME_CSV);
// 表示順のリストを作る
var cols = new DataGridViewColumn[_targetDgv.Columns.Count];
_targetDgv.Columns.CopyTo(cols, 0);
Array.Sort(cols, (a, b) => a.DisplayIndex.CompareTo(b.DisplayIndex));
foreach (var col in cols)
{
var index = ColumnDgv.Rows.Add();
string propertyName = string.Empty;
string defaultDisplayName = string.Empty;
bool visible = true;
var colIndex = col.Index;
var displayIndex = col.DisplayIndex;
if (col.Tag == null || !(col.Tag is PropertyDescriptor))
{
propertyName = col.Name;
defaultDisplayName = propertyName;
}
else
{
PropertyDescriptor pd = col.Tag as PropertyDescriptor;
propertyName = pd.Name;
defaultDisplayName = pd.DisplayName;
var browsableAttr = pd.Attributes.OfType<BrowsableAttribute>().FirstOrDefault();
if (browsableAttr != null && !browsableAttr.Browsable)
{
visible = false;
}
}
//string displayName = col.HeaderText;
string displayName =
StringUxer.GetStringByIndexFormCsv(csv, colIndex, string.Empty)
.Replace("@@@COMMA@@@", ",");
ColumnDgv.Rows[index].Tag = col;
ColumnDgv.Rows[index].Cells[COL_NAME_PROPERTY_NAME].Value = (!visible ? "【非】" : "") + propertyName;
ColumnDgv.Rows[index].Cells[COL_NAME_DEFAULT_DISPLAY_NAME].Value = defaultDisplayName;
ColumnDgv.Rows[index].Cells[COL_NAME_DISPLAY_NAME].Value = displayName == defaultDisplayName ? "" : displayName;
SetVisible(index, col.Visible);
}
//// タグに入れておく(PropertyDescriptor型)
//col.Tag = pd;
//// 列ヘッダーを [DisplayName] に
//col.HeaderText = pd.DisplayName;
//// [Browsable(false)] の列は非表示(AutoGenerateでも列が立つ場合の保険)
//var browsableAttr = pd.Attributes.OfType<BrowsableAttribute>().FirstOrDefault();
//if (browsableAttr != null && !browsableAttr.Browsable)
//{
// col.Visible = false;
// continue;
//}
// DGV列幅復元
DgvHelper.DgvLoadColWidth(ColumnDgv);
}
/// <summary>
/// フォームロード
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DgvColumnEditorForm_Load(object sender, EventArgs e)
{
// バウンズ復元
FormHelper.LoadFormBounds(this);
}
/// <summary>
/// 👆ボタンクリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void UpButton_Click(object sender, EventArgs e)
{
// 対象外状況判断
if (ColumnDgv.SelectedRows.Count < 1) return;
if (ColumnDgv.SelectedRows[0].Index == 0) return;
// 事前取得
var row = ColumnDgv.SelectedRows[0];
var index = row.Index; // Removeしたら-1になるからその前に取得しておく
// 行移動の実行
ColumnDgv.Rows.Remove(row);
ColumnDgv.Rows.Insert(index - 1, row);
// 行選択
ColumnDgv.ClearSelection();
ColumnDgv.Rows[index - 1].Selected = true;
}
/// <summary>
/// 👇ボタンクリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DownButton_Click(object sender, EventArgs e)
{
// 対象外状況判断
if (ColumnDgv.SelectedRows.Count < 1) return;
if (ColumnDgv.SelectedRows[0].Index == ColumnDgv.RowCount - 1) return;
// 事前取得
var row = ColumnDgv.SelectedRows[0];
var index = row.Index; // Removeしたら-1になるからその前に取得しておく
// 行移動の実行
ColumnDgv.Rows.Remove(row);
ColumnDgv.Rows.Insert(index + 1, row);
// 行選択
ColumnDgv.ClearSelection();
ColumnDgv.Rows[index + 1].Selected = true;
}
/// <summary>
/// DGVキー押下イベント
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ColumnDgv_KeyDown(object sender, KeyEventArgs e)
{
if (e.Alt && (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down))
{
if (e.KeyCode == Keys.Up)
{
UpButton_Click(sender, e);
}
else
{
DownButton_Click(sender, e);
}
// 既定動作抑止(メニュー呼び/ビープを止める)
e.Handled = true;
e.SuppressKeyPress = true;
}
}
private const string KEY_NAME_DISPLAY_INDEX_CSV = "DisplayIndexCsv";
private const string KEY_NAME_COLUMN_VISIBLE_CSV = "ColumnVisibleCsv";
private const string KEY_NAME_DISPLAY_NAME_CSV = "DisplayNameCsv";
/// <summary>
/// OKボタンクリック時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OkButton_Click(object sender, EventArgs e)
{
//List<int> indexList = new List<int>();
//foreach (DataGridViewRow row in ColumnDgv.Rows) indexList.Add(((DataGridViewColumn)row.Tag).Index);
////MessageBox.Show(string.Join(" ", indexList));
//if (indexList.Max() != ColumnDgv.Rows.Count - 1)
if (_colNameToRowIndexDic.Values.Max() != ColumnDgv.Rows.Count - 1)
{
MessageBox.Show(this, $"_colNameToRowIndexDicがおかしい![_colNameToRowIndexDicのMax:{_colNameToRowIndexDic.Values.Max()},グリッド行数:{ColumnDgv.Rows.Count}]");
this.DialogResult = DialogResult.None;
return;
}
// 表示順
int[] displayIndexArray = new int[ColumnDgv.Rows.Count];
foreach (DataGridViewRow row in ColumnDgv.Rows)
displayIndexArray[_colNameToRowIndexDic[((DataGridViewRow)row).Cells[0].Value.ToString()]]
= ((DataGridViewRow)row).Index;
// INI書き込み
IniHelper.WriteValue(null, _targetDgv.Parent.Name + "." + _targetDgv.Name, KEY_NAME_DISPLAY_INDEX_CSV, string.Join(",", displayIndexArray));
// 表示非表示
bool[] visibleArray = new bool[ColumnDgv.Rows.Count];
foreach (DataGridViewRow row in ColumnDgv.Rows)
visibleArray[_colNameToRowIndexDic[((DataGridViewRow)row).Cells[0].Value.ToString()]]
= IsVisible(((DataGridViewRow)row).Index);
// INI書き込み
IniHelper.WriteValue(null, _targetDgv.Parent.Name + "." + _targetDgv.Name, KEY_NAME_COLUMN_VISIBLE_CSV, string.Join(",", visibleArray));
// 表示名称
string[] nameArray = new string[ColumnDgv.Rows.Count];
foreach (DataGridViewRow row in ColumnDgv.Rows)
{
var name = string.Empty;
if (row.Cells[COL_NAME_DISPLAY_NAME].Value.ToString() != row.Cells[COL_NAME_DEFAULT_DISPLAY_NAME].Value.ToString())
{
name = row.Cells[COL_NAME_DISPLAY_NAME]
.Value
.ToString()
.Replace(",", "@@@COMMA@@@");
}
nameArray[_colNameToRowIndexDic[((DataGridViewRow)row).Cells[0].Value.ToString()]] = name;
}
// INI書き込み
IniHelper.WriteValue(null, _targetDgv.Parent.Name + "." + _targetDgv.Name, KEY_NAME_DISPLAY_NAME_CSV, string.Join(",", nameArray));
}
/// <summary>
/// フォーム閉じるとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DgvColumnEditorForm_FormClosing(object sender, FormClosingEventArgs e)
{
// バウンズの保存
FormHelper.SaveFormBounds(this);
// DGV列幅保存
DgvHelper.DgvSaveColWidth(ColumnDgv);
}
/// <summary>
/// 表示名の編集メニュークリック
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void EditDisplayNameMenu_Click(object sender, EventArgs e)
{
if (ColumnDgv.SelectedRows.Count < 1) return;
var rowIndex = ColumnDgv.SelectedRows[0].Index;
var text = ColumnDgv[COL_NAME_DISPLAY_NAME,rowIndex].Value.ToString();
var defaultText= ColumnDgv[COL_NAME_DEFAULT_DISPLAY_NAME, rowIndex].Value.ToString();
if (string.IsNullOrEmpty(text)) text = defaultText;
using (var form = new TextInputForm(text))
{
if (form.ShowDialog() == DialogResult.Cancel) return;
ColumnDgv[COL_NAME_DISPLAY_NAME, rowIndex].Value = form.EditedText == defaultText ? "" : form.EditedText;
}
}
private void VisibleMenu_Click(object sender, EventArgs e)
{
if (ColumnDgv.SelectedRows.Count < 1) return;
var rowIndex = ColumnDgv.SelectedRows[0].Index;
SetVisible(rowIndex, !IsVisible(rowIndex));
}
private bool IsVisible(int rowIndex)
{
if (rowIndex < 0 || rowIndex >= ColumnDgv.Rows.Count) return true;
if (ColumnDgv.Rows[rowIndex].DefaultCellStyle == null) return true;
if (ColumnDgv.Rows[rowIndex].DefaultCellStyle.Font == null) return true;
var style = ColumnDgv.Rows[rowIndex].DefaultCellStyle.Font.Style;
return !((style & FontStyle.Strikeout) == FontStyle.Strikeout);
}
private void SetVisible(int rowIndex, bool visible)
{
if (rowIndex < 0 || rowIndex >= ColumnDgv.Rows.Count) return;
if (ColumnDgv.Rows[rowIndex].DefaultCellStyle == null) return;
Font font;
if (ColumnDgv.Rows[rowIndex].DefaultCellStyle.Font != null)
{
font = ColumnDgv.Rows[rowIndex].DefaultCellStyle.Font;
}
else
{
font = ColumnDgv.DefaultCellStyle.Font;
}
var style = font.Style;
if (visible)
{
style &= ~FontStyle.Strikeout;
}
else
{
style |= FontStyle.Strikeout;
}
ColumnDgv.Rows[rowIndex].DefaultCellStyle.Font = new Font(font, style);
// 背景色
ColumnDgv.Rows[rowIndex].DefaultCellStyle.BackColor = visible ? Color.Empty : Color.DarkGray;
}
}
}
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="colPropertyName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="colDefaultDisplayName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="colDisplayName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="PopupMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>
+332
View File
@@ -0,0 +1,332 @@
using DocumentFormat.OpenXml.Office2010.CustomUI;
using KssSmaPlaLib.Forms.ContextMenuFunction;
using KssSmaPlaLib.Ini;
using KssSmaPlaLib.Input.Gamen;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Windows.UI.Popups;
namespace KssSmaPlaLib.Forms
{
public static class ContextMenuHelper
{
private const string INI_KEY_TITLE = "Title";
private const string INI_KEY_BACK_COLOR = "BackColor";
/// <summary>
/// フォーム背景色選択
/// </summary>
/// <param name="form"></param>
public static void SelectBackColor(Form form)
{
using (ColorDialog cd = new ColorDialog())
{
// 今の色を初期選択にしておく
cd.Color = form.BackColor;
if (cd.ShowDialog() == DialogResult.OK)
{
// 1. フォームの背景色を即時変更
form.BackColor = cd.Color;
// 2. 🔥 Colorを16進数(#RRGGBB)に変換してINIに書き込み
string colorHex = ColorTranslator.ToHtml(cd.Color);
IniHelper.WriteValue(null, form.Name, INI_KEY_BACK_COLOR, colorHex);
}
}
}
/// <summary>
/// 背景色をINIから取得して設定
/// </summary>
/// <param name="form"></param>
public static void SetBackColorFromIni(Form form)
{
var colorHex = IniHelper.ReadValue(null, form.Name, INI_KEY_BACK_COLOR, ColorTranslator.ToHtml(form.BackColor));
form.BackColor = ColorTranslator.FromHtml(colorHex);
}
/// <summary>
/// INIに登録されている色選択
/// </summary>
/// <param name="form"></param>
public static void SelectIniColor(Form form, string key, Color defaultColor)
{
var colorHex = IniHelper.ReadValue(null, form.Name, key, ColorTranslator.ToHtml(defaultColor));
var color = ColorTranslator.FromHtml(colorHex);
using (ColorDialog cd = new ColorDialog())
{
// 今の色を初期選択にしておく
cd.Color = color;
if (cd.ShowDialog() == DialogResult.OK)
{
// 2. 🔥 Colorを16進数(#RRGGBB)に変換してINIに書き込み
colorHex = ColorTranslator.ToHtml(cd.Color);
IniHelper.WriteValue(null, form.Name, key, colorHex);
}
}
}
/// <summary>
/// INIに設定されている色取得
/// </summary>
/// <param name="form"></param>
/// <param name="key"></param>
/// <param name="defaultColor"></param>
/// <returns></returns>
public static Color GetIniColor(Form form, string key, Color defaultColor)
{
var colorHex = IniHelper.ReadValue(null, form.Name, key, ColorTranslator.ToHtml(defaultColor));
return ColorTranslator.FromHtml(colorHex);
}
/// <summary>
/// チェックボックスのメニュー項目のチェック値のロード
/// </summary>
/// <param name="frm"></param>
/// <param name="menuItem"></param>
public static void MenuCheckBoxItemLoad(Form frm, ToolStripMenuItem menuItem)
{
menuItem.Checked =
//bool.TryParse(
// IniHelper.ReadValue(
// null, frm.Name, menuItem.Name, false.ToString()),
// out var boolVal) ? boolVal : false;
IniHelper.ReadBoolValue(null, frm.Name, menuItem.Name, false);
}
public static void LoadAllMenuStates(Form form, ContextMenuStrip contextMenu)
{
foreach (ToolStripItem item in contextMenu.Items)
ProcessMenuItemLoad(form, item);
}
private static void ProcessMenuItemLoad(Form form, ToolStripItem item)
{
// ToolStripMenuItem かつ CheckOnClick が true の場合のみ処理
if (item is ToolStripMenuItem menuItem && menuItem.CheckOnClick)
{
// 🚀 ご提示のヘルパーメソッドを1行呼び出すだけ!
ContextMenuHelper.MenuCheckBoxItemLoad(form, menuItem);
}
// 子メニュー(階層)がある場合は再帰的に奥まで処理
if (item is ToolStripDropDownItem dropDownItem && dropDownItem.HasDropDownItems)
{
foreach (ToolStripItem subItem in dropDownItem.DropDownItems)
ProcessMenuItemLoad(form, subItem);
}
}
/// <summary>
/// メニューのチェックボックスアイテムのクリック時
/// </summary>
/// <param name="frm"></param>
/// <param name="menuItem"></param>
public static void MenuCheckBoxItemClick(Form frm, ToolStripMenuItem menuItem)
{
menuItem.Checked = !menuItem.Checked;
IniHelper.WriteValue(null, frm.Name, menuItem.Name, menuItem.Checked.ToString());
}
/// <summary>
/// メニューのフォント設定
/// ※フォームロードでCallしてください
/// </summary>
/// <param name="form"></param>
/// <param name="menu"></param>
public static void SetFont(Form form, ContextMenuStrip menu)
{
menu.Font = FontUxer.GetFont(form.Name, menu.Name + ".Font", menu.Font);
}
/// <summary>
/// コントロール(ラベル等)のフォント設定
/// ※フォームロードでCallしてください
/// </summary>
/// <param name="form"></param>
/// <param name="menu"></param>
public static void SetControlFont(Form form, Control control)
{
control.Font = FontUxer.GetFont(form.Name, control.Name + ".Font", control.Font);
}
/// <summary>
/// メニュー自身のフォント選択
/// </summary>
/// <param name="form"></param>
/// <param name="menu"></param>
public static void SelectMenuFont(Form form, ContextMenuStrip menu)
{
if (FontUxer.SelectFont(form, form.Name, menu.Name + ".Font", menu.Font, out var font))
menu.Font = font;
}
/// <summary>
/// コントロール(ラベル等)のフォント選択
/// </summary>
/// <param name="form"></param>
/// <param name="control"></param>
public static void SelectControlFont(Form form, Control control)
{
if (FontUxer.SelectFont(form, form.Name, control.Name + ".Font", control.Font, out var font))
control.Font = font;
}
/// <summary>
/// フォームのタイトル設定
/// </summary>
/// <param name="form"></param>
public static void SetFormTitle(Form form)
{
var title = IniHelper.ReadValue(null, form.Name, INI_KEY_TITLE, form.Text);
using (var dlg = new TextInputForm(title))
{
if (dlg.ShowDialog() == DialogResult.Cancel) return;
IniHelper.WriteValue(null, form.Name, INI_KEY_TITLE, dlg.EditedText);
form.Text = IniHelper.ReadValue(null, form.Name, INI_KEY_TITLE, form.Text);
}
}
/// <summary>
/// バージョン表示
/// </summary>
/// <param name="menuItem"></param>
public static void DispVersion(ToolStripMenuItem menuItem, bool isDispCraftedBy = true)
{
// バージョン
menuItem.Font = new Font(menuItem.Font.FontFamily, 8.0f);
menuItem.Enabled = false;
menuItem.Text = "Ver." + Application.ProductVersion + (isDispCraftedBy ? " | Crafted by のぼぼ工房" : "");
}
/// <summary>
/// ショートカットキーで動作するようにする
/// ※フォームのKeyPreviewをtrueにして、
///  フォームのKeyDownイベントでCallしてください。
///  private void Form1_KeyDown(object sender, KeyEventArgs e)
//  {
//   // メニューのEnabledを制御している場合、そのOpeningを呼んでおく。
//   contextMenuStrip1_Opening(contextMenuStrip1, new System.ComponentModel.CancelEventArgs());
//
//   if (ExecuteShortcutWithEnabledCheck(contextMenuStrip1.Items, e.KeyData))
//   {
//   e.Handled = true;
//   }
//  }
/// </summary>
/// <param name="items"></param>
/// <param name="keyData"></param>
/// <returns></returns>
public static bool ExecuteShortcutWithEnabledCheck(ToolStripItemCollection items, Keys keyData)
{
foreach (ToolStripItem item in items)
{
if (item is ToolStripMenuItem menuItem)
{
// ショートカットが一致し、かつ「有効(Enabled)」な時だけ実行!
if (menuItem.ShortcutKeys == keyData && menuItem.Enabled)
{
menuItem.PerformClick();
return true;
}
if (menuItem.HasDropDownItems)
{
if (ExecuteShortcutWithEnabledCheck(menuItem.DropDownItems, keyData)) return true;
}
}
}
return false;
}
/// <summary>
/// コンボメニューの選択値をINIに保存
/// ToolStripComboBoxのSelectedIndexChangedでCallしてね!💖)
/// </summary>
/// <param name="form"></param>
/// <param name="combo"></param>
public static void SaveComboMenuToIni(Form form, ToolStripComboBox combo)
{
IniHelper.WriteValue(null, form.Name, combo.Name + ".Values", combo.Text);
}
/// <summary>
/// コンボメニューの選択値をINIから復元
/// FormのLoadでCallしてね💖)
/// </summary>
/// <param name="form"></param>
/// <param name="combo"></param>
/// <param name="defaultValue"></param>
public static void LoadComboMenuFromIni(Form form, ToolStripComboBox combo, string defaultValue)
{
combo.Text = IniHelper.ReadValue(null, form.Name, combo.Name + ".Values", defaultValue);
}
/// <summary>
/// コンボメニューの選択値をINIから取得
/// </summary>
/// <param name="form"></param>
/// <param name="combo"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public static string ComboMenuFromIni(Form form, ToolStripComboBox combo, string defaultValue)
{
return IniHelper.ReadValue(null, form.Name, combo.Name + ".Values", defaultValue);
}
/// <summary>
/// フォームガチャ
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public static async Task FormGacha(Form form)
{
// 現在の状態をバックアップ
var originalState = form.Controls.Cast<Control>()
.Select(c => new { Ctrl = c, Loc = c.Location, Txt = c.Text, Font = c.Font }).ToList();
var formLoc = form.Location;
Random rng = new Random();
// 5秒間の狂宴(癒し)
for (int i = 0; i < 100; i++)
{
// 1. フォーム自体が震える
form.Location = new Point(formLoc.X + rng.Next(-20, 21), formLoc.Y + rng.Next(-20, 21));
foreach (var item in originalState)
{
// 2. コントロールがバラバラに踊る
item.Ctrl.Location = new Point(item.Loc.X + rng.Next(-15, 16), item.Loc.Y + rng.Next(-15, 16));
// 3. ラベルが叫び、ボタンが点滅する
if (item.Ctrl is System.Windows.Forms.Label) item.Ctrl.Text = "がちゃ!";
item.Ctrl.BackColor = Color.FromArgb(rng.Next(256), rng.Next(256), rng.Next(256));
}
// 4. マウスカーソルを中央に拘束(逃がさない)
Cursor.Position = form.PointToScreen(new Point(form.Width / 2, form.Height / 2));
await Task.Delay(30);
}
// 祭りのあと(完全復旧)
form.Location = formLoc;
foreach (var item in originalState)
{
item.Ctrl.Location = item.Loc;
item.Ctrl.Text = item.Txt;
item.Ctrl.Font = item.Font;
item.Ctrl.BackColor = SystemColors.Control;
}
}
}
}
+79
View File
@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace KssSmaPlaLib.Forms.Controls
{
public class PoiDgv : DataGridView
{
private IContainer components;
[Category("Poi拡張"), Description("共通メニューを自動で追加するかどうか")]
public bool EnablePoiMenu { get; set; } = true;
/// <summary>
/// コンストラクタ
/// </summary>
public PoiDgv()
{
// これが今回の本命!
this.DoubleBuffered = true;
// DGB初期設定
this.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
this.RowHeadersVisible = false;
this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
this.AllowUserToAddRows = false;
this.AllowUserToDeleteRows = false;
this.AllowUserToOrderColumns = true;
this.AllowUserToResizeColumns = true;
this.AllowUserToResizeRows = false;
this.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;
}
/// <summary>
/// コンテキストメニュー設定時
/// </summary>
/// <param name="e"></param>
protected override void OnContextMenuStripChanged(EventArgs e)
{
base.OnContextMenuStripChanged(e);
// ユーザーがデザイナーでセットしたメニューに「Poi」のスパイスを加える
if (EnablePoiMenu && this.ContextMenuStrip != null)
{
var menu = this.ContextMenuStrip;
//// 重複防止チェック
//if (menu.Items.Find("poiItem_AutoFit", true).Length == 0)
//{
// if (menu.Items.Count > 0) menu.Items.Add(new ToolStripSeparator());
// var fitItem = new ToolStripMenuItem("列幅をぽいっと自動調整");
// fitItem.Name = "poiItem_AutoFit";
// fitItem.Click += (s, ev) => this.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
// menu.Items.Add(fitItem);
//}
}
}
private void InitializeComponent()
{
((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
this.SuspendLayout();
//
// PoiDgv
//
this.RowTemplate.Height = 21;
((System.ComponentModel.ISupportInitialize)(this)).EndInit();
this.ResumeLayout(false);
}
}
}
+123
View File
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>
+588
View File
@@ -0,0 +1,588 @@
using DocumentFormat.OpenXml.VariantTypes;
using KssSmaPlaLib.Ini;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Windows.Media.Streaming.Adaptive;
namespace KssSmaPlaLib.Forms.Controls
{
/// <summary>
/// Poiメインフォーム
/// </summary>
public class PoiMainForm : Form
{
protected Label TitleLabel;
protected PoiDgv MainDgv;
protected ContextMenuStrip MainContextMenu;
protected ToolStripMenuItem TitleVisibleCheckMenu;
private ToolStripMenuItem _versionMenu;
private NumericUpDown _titleLabelYohakuHorizonNumeric = new NumericUpDown { Width = 60 };
private NumericUpDown _titleLabelYohakuVarticalNumeric = new NumericUpDown { Width = 60 };
// 👇遅延書き込みしようかと思ったがやめた!
//private Timer _saveDelayTimer = new Timer { Interval = 1000 };
private string _title = string.Empty;
/// <summary>
/// コンストラクタ
/// </summary>
public PoiMainForm(string title = "", string name = "")
{
// Name
this.Name = string.IsNullOrEmpty(name) ? this.GetType().Name : name;
// タイトル
_title = string.IsNullOrEmpty(title) ? this.Name : title;
this.Text = _title;
// フォームイベント
this.Load += PoiMainForm_Load;
this.FormClosing += PoiMainForm_FormClosing;
// コントロールの生成と配置
TitleLabel = new Label
{
Name = "TitleLabel",
AutoSize = true,
Dock = DockStyle.Top,
Text = "サンプルです💖",
};
TitleLabel.Padding = new Padding(20, 5, 8, 20);
MainDgv = new PoiDgv
{
Name = "MainDgv",
Dock = DockStyle.Fill
};
MainContextMenu = new ContextMenuStrip
{
Name = "MainContextMenu",
};
this.ContextMenuStrip = MainContextMenu;
this.Controls.Add(MainDgv);
this.Controls.Add(TitleLabel);
// デフォルトのメニュー項目登録
CreateDefaultMenu();
DgvHelper.DgvLoadFontFromIni(MainDgv);
//ContextMenuHelper.MenuCheckBoxItemLoad(this, TitleVisibleCheckMenu);
TitleLabel.Visible = TitleVisibleCheckMenu.Checked;
ContextMenuHelper.SetControlFont(this, TitleLabel);
ContextMenuHelper.DispVersion(_versionMenu);
// ソート無効
foreach (DataGridViewColumn column in MainDgv.Columns) column.SortMode = DataGridViewColumnSortMode.NotSortable;
//// 日の列はセンタリング
//for (var day = 1; day <= 31; day++) MainDgv.Columns[$"ColDay{day}"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
// TOTAL列は右寄せ
//MainDgv.Columns["ColTotal"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
// ヘッダ行のデザインはOSのデザインを反映させる機能を解除
MainDgv.EnableHeadersVisualStyles = false;
// 👇やろうかと思ったけどやめた!
//// 遅延書き込みタイマー
//_saveDelayTimer.Tick += (s, e) =>
//{
// _saveDelayTimer.Stop(); // タイマーを止める
// // 👑 ここでINIファイルへの書き込みを実行!
// // スピンボタン連打が「止まってから1秒後」に1回だけ綺麗に保存されます。
// aaaaaaa
//};
//LoadDataAsync();
// タイトル文字列設定
//TitleLabel.Text = GetTitle();
}
/// <summary>
/// フォームロード時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private void PoiMainForm_Load(object sender, EventArgs e)
{
FormHelper.LoadFormBounds(this);
DgvHelper.DgvLoadFontFromIni(MainDgv);
DgvHelper.DgvLoadColWidth(MainDgv);
DgvHelper.DgvLoadHeaderHeight(MainDgv);
DgvHelper.DgvLoadColVisible(MainDgv);
DgvHelper.DgvLoadColDisplayIndex(MainDgv);
DgvHelper.DgvLoadColFrozen(MainDgv);
DgvHelper.DgvSetHeaderBackColorFromIni(this, MainDgv);
//DgvHelper.DgvSetHeaderBackColorFromIniWithNameMatch(this, MainDgv, "ColDay*", INI_KEY_HEADER_DAY_COLUMN_BACK_COLOR);
// 👇これはLoadでやらないと、子で作ってるメニューアイテムに反映されない!
ContextMenuHelper.LoadAllMenuStates(this, MainContextMenu);
ContextMenuHelper.SetFont(this, MainContextMenu);
var vertical =
int.TryParse(
IniHelper.ReadValue(null, this.Name, _titleLabelYohakuVarticalNumeric.Name, "0"),
out var intVal) ? intVal : 0;
var horizon =
int.TryParse(
IniHelper.ReadValue(null, this.Name, _titleLabelYohakuHorizonNumeric.Name, "0"),
out intVal) ? intVal : 0;
TitleLabel.Padding = new Padding(horizon, vertical, horizon, vertical);
}
/// <summary>
/// フォーム閉じるとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private void PoiMainForm_FormClosing(object sender, FormClosingEventArgs e)
{
FormHelper.SaveFormBounds(this);
DgvHelper.DgvSaveFontToIni(MainDgv);
DgvHelper.DgvSaveColWidth(MainDgv);
DgvHelper.DgvSaveHeaderHeight(MainDgv);
DgvHelper.DgvSaveColVisible(MainDgv);
DgvHelper.DgvSaveColDisplayIndex(MainDgv);
DgvHelper.DgvSaveColFrozen(MainDgv);
IniHelper.WriteValue(
null, this.Name,
_titleLabelYohakuHorizonNumeric.Name,
_titleLabelYohakuHorizonNumeric.Value.ToString());
IniHelper.WriteValue(
null, this.Name,
_titleLabelYohakuVarticalNumeric.Name,
_titleLabelYohakuVarticalNumeric.Value.ToString());
}
/// <summary>
/// デフォルトのメニュー項目(表示設定など)を作成
/// </summary>
private void CreateDefaultMenu()
{
// 仕切り線
MainContextMenu.Items.Add(new ToolStripSeparator());
// 表示設定(親)
var hyojiSetteiMenu = new ToolStripMenuItem()
{
Name = "HyojiSetteiMenu",
Text = "表示設定",
};
MainContextMenu.Items.Add(hyojiSetteiMenu);
// メニューのフォント(クリック動作)
var menuFontMenu = new ToolStripMenuItem()
{
Name = "MenuFontMenu",
Text = "メニューのフォント...",
};
menuFontMenu.Click += (s, e)=>
{
ContextMenuHelper.SelectMenuFont(this, MainContextMenu);
};
hyojiSetteiMenu.DropDownItems.Add(menuFontMenu);
// グリッドメニュー(親)
var gridMenu = new ToolStripMenuItem()
{
Name = "GridMenu",
Text = "グリッド",
};
hyojiSetteiMenu.DropDownItems.Add(gridMenu);
// グリッドフォントメニュー(クリック動作)
var gridFontMenu = new ToolStripMenuItem()
{
Name = "GridFontMenu",
Text = "フォント...",
};
gridFontMenu.Click += (s, e) =>
{
DgvHelper.DgvSelectFont(this, MainDgv);
};
gridMenu.DropDownItems.Add(gridFontMenu);
// グリッド列非表示メニュー(クリック動作)
var gridColumnDisabledMenu = new ToolStripMenuItem()
{
Name = "GridColumnDisabledMenu",
Text = "この列を非表示にする",
};
gridColumnDisabledMenu.Click += (s, e) =>
{
if (MainDgv.CurrentCell is null) return;
MainDgv.Columns[MainDgv.CurrentCell.ColumnIndex].Visible = false;
};
gridMenu.DropDownItems.Add(gridColumnDisabledMenu);
// グリッド列再表示メニュー(クリック動作)
var gridColumnRedispMenu = new ToolStripMenuItem()
{
Name = "GridColumnRedispMenu",
Text = "列の再表示",
};
gridColumnRedispMenu.Click += (s, e) =>
{
DgvHelper.DgvRedispAllColumn(MainDgv);
};
gridMenu.DropDownItems.Add(gridColumnRedispMenu);
// グリッド列幅自動調整メニュー(クリック動作)
var columnWidthAutoAdjustMenu = new ToolStripMenuItem()
{
Name = "ColumnWidthAutoAdjustMenu",
Text = "列幅の自動調整",
};
columnWidthAutoAdjustMenu.Click += (s, e) =>
{
DgvHelper.DgvColumnWidthAutoAdjust(MainDgv);
};
gridMenu.DropDownItems.Add(columnWidthAutoAdjustMenu);
// グリッド列固定メニュー(クリック動作)
var gridColumnFixMenu = new ToolStripMenuItem()
{
Name = "GridColumnFixMenu",
Text = "列固定",
};
gridColumnFixMenu.Click += (s, e) =>
{
DgvHelper.DgvColumnFix(MainDgv);
};
gridMenu.DropDownItems.Add(gridColumnFixMenu);
// グリッド列固定解除メニュー(クリック動作)
var gridColumnFixKaijoMenu = new ToolStripMenuItem()
{
Name = "GridColumnFixKaijoMenu",
Text = "列固定解除",
};
gridColumnFixKaijoMenu.Click += (s, e) =>
{
DgvHelper.DgvColumnFixKaijo(MainDgv);
};
gridMenu.DropDownItems.Add(gridColumnFixKaijoMenu);
// グリッドのヘッダ(親)
//this.GridHeaderMenu});
var gridHeaderMenu = new ToolStripMenuItem()
{
Name = "GridHeaderMenu",
Text = "ヘッダ",
};
gridMenu.DropDownItems.Add(gridHeaderMenu);
// グリッドヘッダ背景色(クリック動作)
var gridHeaderBackColorMenu = new ToolStripMenuItem()
{
Name = "GridHeaderBackColorMenu",
Text = "ヘッダ背景色...",
};
gridHeaderBackColorMenu.Click += (s, e) =>
{
DgvHelper.DgvSelectHeaderBackColor(this, MainDgv);
};
gridHeaderMenu.DropDownItems.Add(gridHeaderBackColorMenu);
// グリッドヘッダの日の背景色(クリック動作)
var gridHeaderDayBackColorMenu = new ToolStripMenuItem()
{
Name = "GridHeaderDayBackColorMenu",
Text = "日の背景色...",
Enabled = false, // デフォルトは非活性
};
gridHeaderDayBackColorMenu.Click += (s, e) =>
{
DgvHelper.DgvSelectHeaderBackColorWithNameMatch(this, MainDgv, "ColDay*", gridHeaderDayBackColorMenu.Name);
};
gridHeaderMenu.DropDownItems.Add(gridHeaderDayBackColorMenu);
// グリッドヘッダの非稼働日の背景色(クリック動作)
var gridHeaderHikadobiBackColorMenu = new ToolStripMenuItem()
{
Name = "GridHeaderHikadobiBackColorMenu",
Text = "非稼働日の背景色...",
Enabled = false, // デフォルトは非活性
};
gridHeaderHikadobiBackColorMenu.Click += (s, e) =>
{
// Comming soon!
MessageBox.Show(this,"カミングスーン!",this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
};
gridHeaderMenu.DropDownItems.Add(gridHeaderHikadobiBackColorMenu);
//this.GridHeaderMenu.DropDownItems.AddRange(
// new System.Windows.Forms.ToolStripItem[] {
//this.,
//this.,
//this.});
// グリッドメニューを開くときの制御
gridMenu.DropDownOpening += (s, e) =>
{
// 「この列の非表示」はアクティブセルがあるときだけ有効
gridColumnDisabledMenu.Enabled = !(MainDgv.CurrentCell is null);
// 「列の再表示」は非表示列があるときだけ有効
gridColumnRedispMenu.Enabled =
MainDgv.Columns.GetColumnCount(DataGridViewElementStates.Visible)
!= MainDgv.Columns.Count;
// 「列固定」はアクティブセルがあるときだけ有効
gridColumnFixMenu.Enabled = !(MainDgv.CurrentCell is null);
// 「列固定解除」は列固定がされているときだけ有効
gridColumnFixKaijoMenu.Enabled = MainDgv.Columns
.Cast<DataGridViewColumn>()
.Any(c => c.Frozen);
};
// タイトル(親)
var titleMenu =
new ToolStripMenuItem()
{
Name = "TitleMenu",
Text = "タイトル",
};
hyojiSetteiMenu.DropDownItems.Add(titleMenu);
// タイトル表示(チェックボックス)
TitleVisibleCheckMenu =
new ToolStripMenuItem()
{
Name = "TitleVisibleCheckMenu",
Text = "タイトル表示?",
CheckOnClick = true,
};
TitleVisibleCheckMenu.CheckedChanged += (s, e) =>
{
var mi = (ToolStripMenuItem)s;
IniHelper.WriteBoolValue(null, this.Name, mi.Name,
mi.Checked);
TitleLabel.Visible = mi.Checked;
};
titleMenu.DropDownItems.Add(TitleVisibleCheckMenu);
// タイトルフォントメニュー(クリック動作)
var titleFontMenu =
new ToolStripMenuItem()
{
Name = "TitleFontMenu",
Text = "フォント...",
};
titleFontMenu.Click += (s, e) =>
{
ContextMenuHelper.SelectControlFont(this, TitleLabel);
};
titleMenu.DropDownItems.Add(titleFontMenu);
// タイトル余白(親)
var titleYohakuMenu = new ToolStripMenuItem()
{
Name = "TitleYohakuMenu",
Text = "余白",
};
titleMenu.DropDownItems.Add(titleYohakuMenu);
// 余白設定パネル
titleYohakuMenu.DropDownItems.Add(CreateYohakuPanelMenu());
// 仕切り線
MainContextMenu.Items.Add(new ToolStripSeparator());
// バージョン表示メニュー(表示のみ)
_versionMenu =
new ToolStripMenuItem()
{
Name = "VersionMenu",
Text = "バージョン情報",
};
MainContextMenu.Items.Add(_versionMenu);
//titleMenu.DropDownItems.Add(CreateYohakuPanelMenu());
//MainContextMenu.Items.Add(CreateYohakuPanelMenu());
// 実験!✨✨✨
//// NumericUpDownを生成して設定
//NumericUpDown numericUpDown = new NumericUpDown();
//numericUpDown.Minimum = 0;
//numericUpDown.Maximum = 100;
//numericUpDown.Value = 10;
//numericUpDown.Width = 60; // メニュー内で邪魔にならない幅に調整
//// ToolStripControlHostでラップしてメニューに追加
//ToolStripControlHost host = new ToolStripControlHost(numericUpDown);
//MainContextMenu.Items.Add(host); // または menuStrip1.Items.Add(host);
}
/// <summary>
/// 余白調整パネルメニュー作成
/// </summary>
/// <returns></returns>
private ToolStripControlHost CreateYohakuPanelMenu()
{
// 1. 土台となるパネル(縦方向にコントロールを並べる)
TableLayoutPanel miniDialog = new TableLayoutPanel();
miniDialog.ColumnCount = 2; // 2列のグリッド
miniDialog.RowCount = 2; // 3; // 3行
miniDialog.AutoSize = true;
miniDialog.Padding = new Padding(10); // 周囲に余白を作ってダイアログ感を出す
miniDialog.BackColor = Color.FromArgb(240, 240, 240); // ほんのり背景色を変える
// 2. コントロール達を作成
Label lblX = new Label { Text = "横余白:", AutoSize = true, Anchor = AnchorStyles.Left };
_titleLabelYohakuHorizonNumeric = new NumericUpDown
{
Name = "TitleLabelYohakuHorizonNumeric",
Width = 60,
TextAlign = HorizontalAlignment.Right,
};
_titleLabelYohakuHorizonNumeric.ValueChanged += (sender, e) =>
{
TitleLabel.Padding = new Padding(
(int)_titleLabelYohakuHorizonNumeric.Value,
TitleLabel.Padding.Top,
(int)_titleLabelYohakuHorizonNumeric.Value,
TitleLabel.Padding.Bottom);
// 🔥🌊INI書きはFormClosingで行う!
};
Label lblY = new Label { Text = "縦余白:", AutoSize = true, Anchor = AnchorStyles.Left };
_titleLabelYohakuVarticalNumeric = new NumericUpDown
{
Name = "TitleLabelYohakuVarticalNumeric",
Width = 60,
TextAlign = HorizontalAlignment.Right,
};
_titleLabelYohakuVarticalNumeric.ValueChanged += (sender, e) =>
{
TitleLabel.Padding = new Padding(
TitleLabel.Padding.Left,
(int)_titleLabelYohakuVarticalNumeric.Value,
TitleLabel.Padding.Right,
(int)_titleLabelYohakuVarticalNumeric.Value);
// 🔥🌊INI書きはFormClosingで行う!
};
//CheckBox chkAuto = new CheckBox { Text = "自動調整", AutoSize = true };
// 3. グリッドパネルに綺麗に配置
miniDialog.Controls.Add(lblX, 0, 0);
miniDialog.Controls.Add(_titleLabelYohakuHorizonNumeric, 1, 0);
miniDialog.Controls.Add(lblY, 0, 1);
miniDialog.Controls.Add(_titleLabelYohakuVarticalNumeric, 1, 1);
//// チェックボックスは2列分ぶち抜きで配置
//miniDialog.Controls.Add(chkAuto, 0, 2);
//miniDialog.SetColumnSpan(chkAuto, 2);
// 4. メニューにホストする
//ToolStripControlHost dialogHost = new ToolStripControlHost(miniDialog);
//MainContextMenu.Items.Add(dialogHost);
// 開くときの処理
var tssh = new ToolStripControlHost(miniDialog);
tssh.OwnerChanged += (sender, e) =>
{
// 親となるメニュー(ToolStripDropDown)を取得
if (tssh.Owner is ToolStripDropDown parentMenu)
{
// 親の「開く直前イベント」に、自分のメソッドをこっそり登録する!
parentMenu.Opening -= ParentMenu_Opening; // 二重登録防止
parentMenu.Opening += ParentMenu_Opening;
}
};
return tssh;
}
/// <summary>
/// 親メニュー開くときに追加する処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ParentMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
//// フォントを親に合わせる(階層深いと反映されないため)
//_titleLabelYohakuHorizonNumeric.Font =
// _titleLabelYohakuVarticalNumeric.Font =
// MainContextMenu.Font;
// パネルの合わせ、再レイアウト(パネルが階層奥深くだと、なぜか反映されていなかったりする)
var parent = _titleLabelYohakuHorizonNumeric.Parent;
if (parent != null)
{
parent.Font = MainContextMenu.Font;
parent.PerformLayout();
}
// タイトルラベルの現在の余白をNumericUpDownに設定
_titleLabelYohakuHorizonNumeric.Value = TitleLabel.Padding.Left;
_titleLabelYohakuVarticalNumeric.Value = TitleLabel.Padding.Top;
}
/// <summary>
/// メニュー項目追加
/// </summary>
/// <param name="text"></param>
/// <param name="name"></param>
/// <param name="defaultValue"></param>
/// <param name="onCheckedChanged"></param>
/// <returns></returns>
protected ToolStripMenuItem AddCheckMenuItem(
int index,
string text,
string name,
bool defaultValue,
Action<bool> onCheckedChanged = null)
{
var item = new ToolStripMenuItem(text);
// チェックボックスとして機能する
item.CheckOnClick = true;
item.Name = name;
// 3. CheckedChanged イベントをここで一括で仕込む!
item.CheckedChanged += (sender, e) =>
{
var mi = (ToolStripMenuItem)sender;
// チェック値をINI保存
IniHelper.WriteBoolValue(null, this.Name, mi.Name, mi.Checked);
// イベントプロシージャ(あるとき)
onCheckedChanged?.Invoke(mi.Checked);
};
MainContextMenu.Items.Insert(index, item);
return item;
}
public enum PoiAlign
{
Left,
Right,
Center,
}
private DataGridViewContentAlignment GetDgvAlign(PoiAlign align)
{
switch (align)
{
case PoiAlign.Left: return DataGridViewContentAlignment.MiddleLeft;
case PoiAlign.Center: return DataGridViewContentAlignment.MiddleCenter;
case PoiAlign.Right: return DataGridViewContentAlignment.MiddleRight;
default: return DataGridViewContentAlignment.MiddleLeft;
}
}
/// <summary>
/// 列追加
/// </summary>
/// <param name="name"></param>
/// <param name="text"></param>
/// <param name="align"></param>
protected void AddColumn(string name, string text, PoiAlign align = PoiAlign.Left)
{
var col = new DataGridViewTextBoxColumn()
{
Name = name,
HeaderText = text,
ReadOnly = true,
};
col.DefaultCellStyle.Alignment = GetDgvAlign(align);
MainDgv.Columns.Add(col);
}
}
}
+172
View File
@@ -0,0 +1,172 @@
using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace KssSmaPlaLib.Forms
{
public static class DataGridViewExporter
{
/// <summary>
/// DataGridView の「可視列・可視行のみ」を Excel へエクスポートします。
/// 列順は DisplayIndex 順/ヘッダー付き/自動調整/簡易罫線付き。
/// </summary>
public static void ExportVisibleToExcel(
DataGridView dgv,
string filePath,
string sheetName = "Export")
{
if (dgv == null) throw new ArgumentNullException(nameof(dgv));
if (string.IsNullOrWhiteSpace(filePath)) throw new ArgumentException("filePath is required.", nameof(filePath));
// 可視列のみ、DisplayIndex順に並べ替え
var visibleCols = dgv.Columns
.Cast<DataGridViewColumn>()
.Where(c => c.Visible)
.OrderBy(c => c.DisplayIndex)
.ToList();
// 可視行のみ(新規行除外)
var visibleRows = dgv.Rows
.Cast<DataGridViewRow>()
.Where(r => r.Visible && !r.IsNewRow)
.ToList();
using (var wb = new XLWorkbook())
{
var ws = wb.Worksheets.Add(string.IsNullOrWhiteSpace(sheetName) ? "Sheet1" : sheetName);
int row = 1;
int col = 1;
// ヘッダー行
foreach (var c in visibleCols)
{
ws.Cell(row, col).Value = c.HeaderText ?? c.Name;
// すこし見やすく
ws.Cell(row, col).Style.Font.Bold = true;
ws.Cell(row, col).Style.Fill.BackgroundColor = XLColor.LightGray;
ws.Cell(row, col).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
col++;
}
// データ行
row = 2;
foreach (var r in visibleRows)
{
col = 1;
foreach (var c in visibleCols)
{
var cell = r.Cells[c.Index];
var val = cell.Value;
if (val == null || val == DBNull.Value)
{
ws.Cell(row, col).Clear(); // 空セル(文字列""よりこちらが自然)
}
else
{
// ValueType を優先し、なければ実オブジェクトの型で判定
var vt = cell.ValueType ?? val.GetType();
if (vt == typeof(int) || vt == typeof(long) || vt == typeof(short) || vt == typeof(byte))
{
ws.Cell(row, col).Value = Convert.ToInt64(val); // 長整数に寄せると安全
// 必要なら桁区切りなど: ws.Cell(row, col).Style.NumberFormat.Format = "#,##0";
}
else if (vt == typeof(decimal))
{
ws.Cell(row, col).Value = Convert.ToDecimal(val);
// 書式例: ws.Cell(row, col).Style.NumberFormat.Format = "#,##0.####";
}
else if (vt == typeof(double) || vt == typeof(float))
{
ws.Cell(row, col).Value = Convert.ToDouble(val);
}
else if (vt == typeof(DateTime))
{
ws.Cell(row, col).Value = Convert.ToDateTime(val);
// 例: ws.Cell(row, col).Style.DateFormat.Format = "yyyy-mm-dd hh:mm";
}
else if (vt == typeof(TimeSpan))
{
// Excelは TimeSpan を日数として扱うので適宜書式を
var ts = (TimeSpan)val;
ws.Cell(row, col).Value = ts;
ws.Cell(row, col).Style.NumberFormat.Format = "[h]:mm:ss";
}
else if (vt == typeof(bool))
{
ws.Cell(row, col).Value = Convert.ToBoolean(val);
}
else
{
// それ以外は文字列にフォールバック(トリム等はお好みで)
ws.Cell(row, col).Value = val.ToString();
}
}
col++;
}
row++;
}
//// データ行
//row = 2;
//foreach (var r in visibleRows)
//{
// col = 1;
// foreach (var c in visibleCols)
// {
// object val = r.Cells[c.Index].Value;
// // Excel側でよく見えるように値をそのままセット
// ws.Cell(row, col).Value = val == null ? string.Empty : val.ToString();
// col++;
// }
// row++;
//}
// テーブルっぽく罫線&自動調整
var lastRow = row - 1;
var lastCol = visibleCols.Count;
if (lastCol > 0 && lastRow > 0)
{
var rng = ws.Range(1, 1, lastRow, lastCol);
rng.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
rng.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
rng.SetAutoFilter(); // フィルターもあると便利
ws.Columns(1, lastCol).AdjustToContents();
}
// 保存
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
wb.SaveAs(filePath);
}
}
/// <summary>
/// SaveFileDialog 付きの簡便版。UIからサクッと呼び出す用。
/// </summary>
public static void ExportVisibleToExcelWithDialog(DataGridView dgv, string defaultFileName = "export.xlsx")
{
using (var sfd = new SaveFileDialog()
{
Filter = "Excel Workbook (*.xlsx)|*.xlsx",
FileName = defaultFileName,
OverwritePrompt = true,
AddExtension = true
})
{
if (sfd.ShowDialog() == DialogResult.OK)
{
ExportVisibleToExcel(dgv, sfd.FileName);
MessageBox.Show("Excelにエクスポートしました。", "完了", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}
}
+58
View File
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace KssSmaPlaLib.Forms
{
public static class DatePickerHelper
{
public static DateTime? ShowDateDialog(
DateTime? defaultDate = null,
string title = "日付を選択してください")
{
// 1. その場限りのフォームを作成
using (Form form = new Form())
{
DateTimePicker dtp = new DateTimePicker();
Button btnOk = new Button();
// フォームの設定
form.Text = title;
form.Size = new System.Drawing.Size(240, 120);
form.FormBorderStyle = FormBorderStyle.FixedDialog;
form.StartPosition = FormStartPosition.CenterParent;
form.MaximizeBox = false;
form.MinimizeBox = false;
// DateTimePickerの設定
dtp.Location = new System.Drawing.Point(10, 10);
dtp.Size = new System.Drawing.Size(200, 20);
dtp.Format = DateTimePickerFormat.Short;
// ★ ここで初期値をセット!
// 引数がnullなら今日の値を、あればその値をセットします。
dtp.Value = defaultDate ?? DateTime.Today;
// OKボタンの設定
btnOk.Text = "OK";
btnOk.DialogResult = DialogResult.OK; // これを押すとフォームが閉じる
btnOk.Location = new System.Drawing.Point(135, 45);
form.AcceptButton = btnOk; // EnterキーでOK
// コントロールを追加
form.Controls.Add(dtp);
form.Controls.Add(btnOk);
// 2. ダイアログとして表示
if (form.ShowDialog() == DialogResult.OK)
{
return dtp.Value; // 選択された日付を返す
}
}
return null; // キャンセルされた場合
}
}
}
+726
View File
@@ -0,0 +1,726 @@
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("他には見つかりませんでしたにゃ!🐾");
}
}
}
+175
View File
@@ -0,0 +1,175 @@
using KssSmaPlaLib.Ini;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace KssSmaPlaLib.Forms
{
public static class FormHelper
{
/// <summary>
/// フォームのコンストラクタで行う処理集
/// </summary>
/// <param name="frm"></param>
public static void FormConstructorProc(Form frm)
{
frm.KeyPreview = true;
FormHelper.SetTitle(frm);
foreach (var control in frm.Controls)
{
if (control is DataGridView)
{
// DGVで行う共通処理
var dgv = control as DataGridView;
dgv.Dock = DockStyle.Fill;
dgv.AllowUserToResizeRows = false;
dgv.AllowUserToAddRows = false;
dgv.AllowUserToDeleteRows = false;
dgv.AllowUserToOrderColumns = false;
dgv.AllowUserToResizeColumns = true;
dgv.AllowUserToResizeRows = false;
dgv.ReadOnly = true;
dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dgv.RowHeadersVisible = false;
dgv.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
// 列ヘッダのユーザによるソートを無効化する
foreach (DataGridViewColumn col in dgv.Columns) col.SortMode = DataGridViewColumnSortMode.NotSortable;
}
}
}
/// <summary>
/// フォームのロードで行う処理集
/// </summary>
/// <param name="frm"></param>
/// <param name="isDgvColAutoFit"></param>
public static void FormLoadProc(Form frm, ContextMenuStrip[] menus, bool isDgvColAutoFit = false)
{
FormHelper.LoadFormBounds(frm);
foreach (var control in frm.Controls)
{
if (control is DataGridView)
{
// DGVで行う共通処理
var dgv = control as DataGridView;
DgvHelper.DgvLoadColVisible(dgv);
DgvHelper.DgvLoadColDisplayIndex(dgv);
DgvHelper.DgvLoadColFrozen(dgv);
if (isDgvColAutoFit)
{
DgvHelper.DgvColumnWidthAutoAdjust(dgv);
}
else
{
DgvHelper.DgvLoadColWidth(dgv);
}
DgvHelper.DgvLoadFontFromIni(dgv);
}
}
foreach (var menu in menus)
{
ContextMenuHelper.SetFont(frm, menu);
}
}
/// <summary>
/// フォーム閉じるときの処理集
/// </summary>
/// <param name="frm"></param>
public static void FormClosingProc(Form frm)
{
FormHelper.SaveFormBounds(frm);
foreach (var control in frm.Controls)
{
if (control is DataGridView)
{
// DGVで行う共通処理
var dgv = control as DataGridView;
DgvHelper.DgvSaveColVisible(dgv);
DgvHelper.DgvSaveColDisplayIndex(dgv);
DgvHelper.DgvSaveColFrozen(dgv);
DgvHelper.DgvSaveColWidth(dgv);
DgvHelper.DgvSaveFontToIni(dgv);
}
}
}
private const string INI_KEY_RESTORE_BOUNDS_CSV = "RestoreBoundsCsv";
/// <summary>
/// フォームのバウンズ保存
/// ※フォームのClosingイベントでCallしてください。
/// </summary>
/// <param name="form"></param>
public static void SaveFormBounds(Form form)
{
var csv = string.Empty;
if (form.WindowState == FormWindowState.Maximized || form.WindowState == FormWindowState.Minimized)
{
csv = $"{form.RestoreBounds.Width},{form.RestoreBounds.Height},{form.RestoreBounds.Top},{form.RestoreBounds.Left},{form.WindowState == FormWindowState.Maximized}";
}
else
{
csv = $"{form.Bounds.Width},{form.Bounds.Height},{form.Bounds.Top},{form.Bounds.Left},{form.WindowState == FormWindowState.Maximized}";
}
IniHelper.WriteValue(null, form.Name, INI_KEY_RESTORE_BOUNDS_CSV, csv);
}
/// <summary>
/// フォームのバウンズ復元
/// ※フォームのLoadイベントでCallしてください。
/// </summary>
/// <param name="form"></param>
public static void LoadFormBounds(Form form)
{
var csv = IniHelper.ReadValue(null, form.Name, INI_KEY_RESTORE_BOUNDS_CSV);
if (string.IsNullOrEmpty(csv)) return;
var items = csv.Split(',');
if (items.Length != 5) return;
if (!int.TryParse(items[0], out var width)) return;
if (!int.TryParse(items[1], out var height)) return;
if (!int.TryParse(items[2], out var top)) return;
if (!int.TryParse(items[3], out var left)) return;
if (!bool.TryParse(items[4], out var isMaximum)) return;
form.Bounds = new System.Drawing.Rectangle(left, top, width, height);
if (isMaximum) form.WindowState = FormWindowState.Maximized;
}
private const string KEY_NAME_TITLE = "Title";
/// <summary>
/// タイトル設定(INI優先)
/// ※フォームのコンストラクタでCallしてください
/// </summary>
/// <param name="form"></param>
public static void SetTitle(Form form)
{
form.Text = IniHelper.ReadValue(null, form.Name, KEY_NAME_TITLE, form.Text);
}
/// <summary>
/// フォームの画面キャプチャを取得し画像ファイルに保存する
/// </summary>
/// <param name="form"></param>
/// <param name="bitmapFilePath"></param>
public static void GetCaptureAndSaveBitmap(Form form, string bitmapFilePath)
{
// フォームと同じサイズのBitmapを作成
Bitmap bitmap = new Bitmap(form.Width, form.Height);
// Bitmapにフォームの内容を描画
form.DrawToBitmap(bitmap, new Rectangle(0, 0, form.Width, form.Height));
// 画像ファイルとして保存
bitmap.Save(bitmapFilePath, System.Drawing.Imaging.ImageFormat.Jpeg);
// リソースの解放
bitmap.Dispose();
}
}
}
+181
View File
@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace KssSmaPlaLib.Forms
{
public static class ToastNotifier
{
private const int WS_EX_NOACTIVATE = 0x08000000;
private const int WS_EX_TOPMOST = 0x00000008;
//public static void Show(Form owner, string message, Color backColor, int durationMs = 3000)
//{
// // 1. フォームの基本設定
// Form toast = new Form
// {
// FormBorderStyle = FormBorderStyle.None,
// StartPosition = FormStartPosition.Manual,
// BackColor = backColor,
// ShowInTaskbar = false,
// TopMost = true,
// Size = new Size(300, 60),
// Opacity = 0,
// Enabled = false // トースト自体は操作不能にしておく
// };
// Label lbl = new Label
// {
// Text = message,
// ForeColor = Color.White,
// Font = new Font("MS UI Gothic", 10, FontStyle.Bold),
// Dock = DockStyle.Fill,
// TextAlign = ContentAlignment.MiddleCenter
// };
// toast.Controls.Add(lbl);
// // オーナーの右下に配置
// toast.Location = new Point(
// owner.Location.X + owner.Width - toast.Width - 20,
// owner.Location.Y + owner.Height - toast.Height - 20
// );
// // 2. タイマーでアニメーションを管理
// Timer timer = new Timer { Interval = 10 };
// DateTime startTime = DateTime.Now;
// timer.Tick += (s, e) =>
// {
// double elapsed = (DateTime.Now - startTime).TotalMilliseconds;
// if (elapsed < 200) // 最初の0.2秒でフェードイン
// {
// toast.Opacity = elapsed / 200.0;
// }
// else if (elapsed < 200 + durationMs) // 指定時間はそのまま
// {
// toast.Opacity = 1.0;
// }
// else if (elapsed < 200 + durationMs + 200) // 最後の0.2秒でフェードアウト
// {
// toast.Opacity = 1.0 - (elapsed - (200 + durationMs)) / 200.0;
// }
// else // 終了処理
// {
// timer.Stop();
// timer.Dispose();
// toast.Close();
// toast.Dispose();
// }
// };
// // 3. 表示開始(ここを通った瞬間にメソッドは終了し、呼び出し元に戻ります)
// toast.Show();
// timer.Start();
//}
//public static async Task ShowAsync(Form owner, string message, Color backColor, int durationMs = 3000)
//{
// // 1. フォームの作成
// using (var toast = new Form
// {
// FormBorderStyle = FormBorderStyle.None,
// StartPosition = FormStartPosition.Manual,
// BackColor = backColor,
// ShowInTaskbar = false,
// TopMost = true,
// Size = new Size(300, 60),
// Opacity = 0
// })
// {
// // フォーカスを奪わないための設定(WinAPIをラップしたCreateParamsの上書き)
// // ※簡易的にリフレクションで設定するか、Formを継承したクラスを作るのが定石です
// Label lbl = new Label
// {
// Text = message,
// ForeColor = Color.White,
// Font = new Font("MS UI Gothic", 10, FontStyle.Bold),
// Dock = DockStyle.Fill,
// TextAlign = ContentAlignment.MiddleCenter
// };
// toast.Controls.Add(lbl);
// toast.Location = new Point(
// owner.Location.X + owner.Width - toast.Width - 20,
// owner.Location.Y + owner.Height - toast.Height - 20
// );
// // 表示(フォーカスを奪わずに表示)
// toast.Show();
// // フォーカスが移るのを防ぐための念押し(Activateさせない)
// // 2. アニメーション(async/await の真骨頂)
// for (int i = 0; i < 10; i++) { toast.Opacity += 0.1; await Task.Delay(10); }
// await Task.Delay(durationMs);
// for (int i = 0; i < 10; i++) { toast.Opacity -= 0.1; await Task.Delay(10); }
// toast.Close();
// }
//}
public static async Task ShowAsync(Form owner, string message, Color backColor, int durationMs = 3000)
{
// 【重要】SignalRなどの別スレッドから呼ばれても安全なように、UIスレッドへ強制バトンタッチ
if (owner.InvokeRequired)
{
owner.BeginInvoke(new Action(() => { _ = ShowAsync(owner, message, backColor, durationMs); }));
return;
}
// 1. フォームの作成(usingは使わず、Closeに任せる)
// フォーカスを奪わない(WS_EX_NOACTIVATE = 0x08000000)を仕込んだカスタムフォームをその場で定義
var toast = new NoActivateForm
{
FormBorderStyle = FormBorderStyle.None,
StartPosition = FormStartPosition.Manual,
BackColor = backColor,
ShowInTaskbar = false,
TopMost = true,
Size = new Size(300, 60),
Opacity = 0
};
Label lbl = new Label
{
Text = message,
ForeColor = Color.White,
Font = new Font("MS UI Gothic", 10, FontStyle.Bold),
Dock = DockStyle.Fill,
TextAlign = ContentAlignment.MiddleCenter
};
toast.Controls.Add(lbl);
// 右下のベストポジションに配置(オーナーフォーム基準)
toast.Location = new Point(
owner.Location.X + owner.Width - toast.Width - 20,
owner.Location.Y + owner.Height - toast.Height - 20
);
// 表示(フォーカスを絶対に奪わない)
toast.Show();
// 2. アニメーション(async/await の真骨頂!)
for (int i = 0; i < 10; i++) { toast.Opacity += 0.1; await Task.Delay(10); }
await Task.Delay(durationMs);
for (int i = 0; i < 10; i++) { toast.Opacity -= 0.1; await Task.Delay(10); }
// 閉じると同時に、WinFormsが自動でメモリを綺麗に解放(Dispose)してくれます
toast.Close();
}
}
// 【職人技】フォーカスを絶対に奪わないフォーム(CreateParamsの上書き)
public class NoActivateForm : Form
{
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x08000000; // WS_EX_NOACTIVATE(クリックしてもフォーカスが移らない魔法のフラグ)
return cp;
}
}
}
}