前回までで、
1.(整数の)四則演算式の解を得るモジュール(.NET 10用に作り直したCalc_Int.dllが行います)
2.ウィンドウUI(要すれば、ユーザーが使う「電卓ウィンドウ」の外観部分)
が出来上がったので、この「電卓」に何を行わせるのか、を整理します。
【Win_Calc.exeの行う仕事(メソッド)の整理】
(1)先ず四則演算式を入力しますが、これはウィンドウに張り付けたButtonのキーパッドのみならず、キーボードのキーからも入力できるようにします。また、できればキーボードから入力する際、ウィンドウのキーパッドが「押されたように見える」と格好いいですね。(尚、これらから入力したデータが式表示TextBoxにあらわれるので、二重入力を避ける意味でTextBox自体の入力機能は殺しておく必要があります。)
(2)演算実行キーは「=」であり、この時にTextBoxの式の文字列を取り出し、Clac_int.dllに渡し、戻ってきた解を以下(4)に渡します。またエラーが出ればそれを通知します。尚、「=」を押したならばそれ以上の式の編集をできなくするために式文字列を消去しますが、すぐに消去すると式と解を見比べられなくなるので、「次の式の入力があったら、先ずTextBoxをクリアーしてね」という状態にする必要があります。
(3)誤った入力を行った場合の為のBack Spaceキーと入力内容を白紙にするClear Entry(CE)キーを用意しましたので、これらを機能させます。
(4)演算結果はTextBoxの下にあるListBoxに順次表示します。またこの演算結果を選択+ダブルクリックで再入力できるようにします。
(5)ひとしきり演算を行って終了する(ウィンドウ右上の「X」(システム)キーで行います)場合、今までの演算式と結果を残したい、と思うかもしれません。再利用も考えてテキストファイルで残すかどうか、意向確認を行ってファイル出力を行うようにします。なお、出力ファイルは出力日時をファイル名とし、内容は「計算式=解」となるようにしましょう。(↓が現在のファイル出力例)
<ファイル出力例>
こうして必要なメソッドを整理したので、後は実装するだけですが、「できればキーボードから入力する際、ウィンドウのキーパッドが「押されたように見える」と格好いいですね。」について"(ボタン).PerformClick();"を使ったところ、見た目は全然何もしていないのに気が付きました。いろいろと調べたのですが、あまり良い情報がないので、またまたCopilot君に相談します。
【Copilot君との相談】
Q: 新しい質問です。 C#で文字キー入力をKeyPressで拾い、その文字に合わせてボタンが押されたようにふるまわせるために"ボタン.PerformClick();"を使いましたが、ボタンの挙動は変わりませんでした。実際に押されたようなUI挙動を起こさせる方法がありますか?
A: 🎯 PerformClick() は “クリックイベントを発火するだけ” (解説:ボタンを押された場合のメソッドを呼ぶだけ、の意味)で、UI の押し込みアニメーションは行いません。
🧩 では「押されたように見せる」にはどうする
✅ 方法1:SendMessage で BM_SETSTATE を送る(最も Win32 的で確実)
✅ 方法2:FlatStyle.Popup を使って視覚効果を自作する
❌ 方法3:PerformClick() だけでは絶対に押し込みは起きない
🧠 (私の名) さん向けの歴史的補足
Win32 の頃は:
- ボタンは USER32 の純粋なウィンドウクラス
- BM_SETSTATE で確実に押し込みが描画された
しかし Windows XP 以降のテーマ描画(UxTheme.dll)では:
- ボタンの描画はテーマエンジンが担当
- BM_SETSTATE はテーマ描画では無視される
- WinForms はテーマ描画をラップしているだけ
という構造になっています。つまり、Win32 の頃の「押し込み」は、現代の Windows では OS が描画しないということです。
Q: 一応「方法1」のWin32API(SendMessage)型と「方法2」のFlatStyle型を試したのですが、前者は元々のボタンスタイルの所為か変化が分かりませんし、後者は(WPFのような形状のフラットなボタンでしたが)ボタンスタイルが昔のWindowsのスタイル(色がグレーになり、縁取りが黒になり、3Dスタイルになる)に変わってしまいます。なかなか難しいですね。
A: なるほど、ここまで試してうまくいかない理由は WinForms の Button が「見た目の押し込み」をほぼ完全に OS に依存しているためです。そして Windows 10/11 のテーマでは、BM_SETSTATE の押し込みアニメーションがほぼ無効化されているので、SendMessage を送っても視覚変化が出ません。
さらに、FlatStyle を変えると テーマ描画(Visual Styles)を無効化してしまうため、昔の Windows 95/98 風の 3D ボタンに戻ってしまうわけです。
これは WinForms の限界に近い部分です。
🎯 結論:WinForms の標準 Button では「押し込みアニメーション」を再現するのはほぼ不可能
理由は次の通りです:
- Windows 10/11 のテーマでは ボタン押下の視覚効果が非常に薄い
- WinForms の Button は OS のテーマ描画に完全依存
- FlatStyle を変えると テーマ描画が無効化され、古い 3D ボタンになる
- BM_SETSTATE は テーマ描画では無視される
つまり、「押されたように見える」視覚効果を WinForms の標準 Button で再現するのは難しいというのが現実です。
🎯 最適解
あなたの目的が「押されたように見せたい」なら:
✔ Button を継承して自前描画
または
✔ WPF Button を使う
このどちらかが最も確実です。
Q: 私の目的は「可能なら、押されたように見せたい」なので、「あきらめる」が最適解です。(諺にも「人間、あきらめが肝心」といいます。)
A: 🧠 そして「諦める」という選択は、実は最適化
技術者にとって「諦める」は敗北ではなく、“投資対効果の最適化” です。
- WinForms の Button は OS テーマ依存
- 押し込みアニメーションは現代 Windows ではほぼ無効
- FlatStyle を変えると古い 3D ボタンになる
- WPF は学習コストが高い
- 目的は「押し込みアニメーション」だけ
これらを総合すると、「そこに時間を使う価値はない」という判断は完全に正しいです。
結局、C#のWinFormsではキー入力でコントロールのボタンを押されたように見せるシミュレートは行うことができず、その為だけに[dllimport]で生のWin32 APIを使ったり、WPFを使ったりすることは主旨ではないので、やめました。
なお、次回からは「ユーザーコードの量」を見ていただくために、Form_Calc.csのコードをお見せしますが、今回はVisualStudioが作成するProgram.csとForm_Calc.designer.csのコードをご参考までに後掲します。(私が手をかけたのはコントロールのID名を修正し、コメントを追加しただけです。)
【ご参考-Program.cs】
/////////////////////////////////////////////////
// Win_Calc.cs - A window program for Calc class
/////////////////////////////////////////////////
namespace Win_Calc
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new Form_Calc());
}
}
}
【ご参考-Form_Calc.designer.cs】
namespace Win_Calc
{
partial class Form_Calc
{
/// <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()
{
components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form_Calc));
button0 = new Button();
button1 = new Button();
button2 = new Button();
button3 = new Button();
button4 = new Button();
button5 = new Button();
button6 = new Button();
button7 = new Button();
button8 = new Button();
button9 = new Button();
buttonLE = new Button();
buttonRE = new Button();
buttonPlus = new Button();
buttonMinus = new Button();
button1Mult = new Button();
buttonDiv = new Button();
buttonBs = new Button();
buttonCe = new Button();
buttonEq = new Button();
formulaBox = new TextBox();
resultBox = new ListBox();
toolTip = new ToolTip(components);
SuspendLayout();
//
// button0
//
button0.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button0.Font = new Font("Yu Gothic UI", 14F);
button0.Location = new Point(12, 461);
button0.Name = "button0";
button0.Size = new Size(48, 48);
button0.TabIndex = 0;
button0.Text = "0";
button0.UseVisualStyleBackColor = true;
button0.Click += button0_Click;
//
// button1
//
button1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button1.Font = new Font("Yu Gothic UI", 14F);
button1.Location = new Point(12, 407);
button1.Name = "button1";
button1.Size = new Size(48, 48);
button1.TabIndex = 1;
button1.Text = "1";
button1.UseVisualStyleBackColor = true;
button1.Click += button1_Click;
//
// button2
//
button2.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button2.Font = new Font("Yu Gothic UI", 14F);
button2.Location = new Point(75, 407);
button2.Name = "button2";
button2.Size = new Size(48, 48);
button2.TabIndex = 2;
button2.Text = "2";
button2.UseVisualStyleBackColor = true;
button2.Click += button2_Click;
//
// button3
//
button3.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button3.Font = new Font("Yu Gothic UI", 14F);
button3.Location = new Point(140, 407);
button3.Name = "button3";
button3.Size = new Size(48, 48);
button3.TabIndex = 3;
button3.Text = "3";
button3.UseVisualStyleBackColor = true;
button3.Click += button3_Click;
//
// button4
//
button4.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button4.Font = new Font("Yu Gothic UI", 14F);
button4.Location = new Point(12, 353);
button4.Name = "button4";
button4.Size = new Size(48, 48);
button4.TabIndex = 4;
button4.Text = "4";
button4.UseVisualStyleBackColor = true;
button4.Click += button4_Click;
//
// button5
//
button5.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button5.Font = new Font("Yu Gothic UI", 14F);
button5.Location = new Point(75, 353);
button5.Name = "button5";
button5.Size = new Size(48, 48);
button5.TabIndex = 5;
button5.Text = "5";
button5.UseVisualStyleBackColor = true;
button5.Click += button5_Click;
//
// button6
//
button6.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button6.Font = new Font("Yu Gothic UI", 14F);
button6.Location = new Point(140, 353);
button6.Name = "button6";
button6.Size = new Size(48, 48);
button6.TabIndex = 6;
button6.Text = "6";
button6.UseVisualStyleBackColor = true;
button6.Click += button6_Click;
//
// button7
//
button7.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button7.Font = new Font("Yu Gothic UI", 14F);
button7.Location = new Point(12, 299);
button7.Name = "button7";
button7.Size = new Size(48, 48);
button7.TabIndex = 7;
button7.Text = "7";
button7.UseVisualStyleBackColor = true;
button7.Click += button7_Click;
//
// button8
//
button8.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button8.Font = new Font("Yu Gothic UI", 14F);
button8.Location = new Point(75, 299);
button8.Name = "button8";
button8.Size = new Size(48, 48);
button8.TabIndex = 8;
button8.Text = "8";
button8.UseVisualStyleBackColor = true;
button8.Click += button8_Click;
//
// button9
//
button9.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button9.Font = new Font("Yu Gothic UI", 14F);
button9.Location = new Point(140, 299);
button9.Name = "button9";
button9.Size = new Size(48, 48);
button9.TabIndex = 9;
button9.Text = "9";
button9.UseVisualStyleBackColor = true;
button9.Click += button9_Click;
//
// buttonLE
//
buttonLE.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
buttonLE.Font = new Font("Yu Gothic UI", 14F);
buttonLE.Location = new Point(75, 461);
buttonLE.Name = "buttonLE";
buttonLE.Size = new Size(48, 48);
buttonLE.TabIndex = 10;
buttonLE.Text = "(";
buttonLE.UseVisualStyleBackColor = true;
buttonLE.Click += buttonLe_Click;
//
// buttonRE
//
buttonRE.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
buttonRE.Font = new Font("Yu Gothic UI", 14F);
buttonRE.Location = new Point(140, 461);
buttonRE.Name = "buttonRE";
buttonRE.Size = new Size(48, 48);
buttonRE.TabIndex = 11;
buttonRE.Text = ")";
buttonRE.UseVisualStyleBackColor = true;
buttonRE.Click += buttonRe_Click;
//
// buttonPlus
//
buttonPlus.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
buttonPlus.Font = new Font("Yu Gothic UI", 14F);
buttonPlus.Location = new Point(204, 299);
buttonPlus.Name = "buttonPlus";
buttonPlus.Size = new Size(48, 48);
buttonPlus.TabIndex = 12;
buttonPlus.Text = "+";
buttonPlus.UseVisualStyleBackColor = true;
buttonPlus.Click += buttonPlus_Click;
//
// buttonMinus
//
buttonMinus.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
buttonMinus.Font = new Font("Yu Gothic UI", 14F);
buttonMinus.Location = new Point(204, 353);
buttonMinus.Name = "buttonMinus";
buttonMinus.Size = new Size(48, 48);
buttonMinus.TabIndex = 13;
buttonMinus.Text = "-";
buttonMinus.UseVisualStyleBackColor = true;
buttonMinus.Click += buttonMinus_Click;
//
// button1Mult
//
button1Mult.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
button1Mult.Font = new Font("Yu Gothic UI", 14F);
button1Mult.Location = new Point(204, 407);
button1Mult.Name = "button1Mult";
button1Mult.Size = new Size(48, 48);
button1Mult.TabIndex = 14;
button1Mult.Text = "*";
button1Mult.UseVisualStyleBackColor = true;
button1Mult.Click += buttonMult_Click;
//
// buttonDiv
//
buttonDiv.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
buttonDiv.Font = new Font("Yu Gothic UI", 14F);
buttonDiv.Location = new Point(204, 461);
buttonDiv.Name = "buttonDiv";
buttonDiv.Size = new Size(48, 48);
buttonDiv.TabIndex = 15;
buttonDiv.Text = "/";
buttonDiv.UseVisualStyleBackColor = true;
buttonDiv.Click += buttonDiv_Click;
//
// buttonBs
//
buttonBs.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
buttonBs.Font = new Font("Yu Gothic UI", 14F);
buttonBs.Location = new Point(268, 299);
buttonBs.Name = "buttonBs";
buttonBs.Size = new Size(48, 48);
buttonBs.TabIndex = 16;
buttonBs.Text = "BS";
buttonBs.UseVisualStyleBackColor = true;
buttonBs.Click += buttonBs_Click;
//
// buttonCe
//
buttonCe.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
buttonCe.Font = new Font("Yu Gothic UI", 14F);
buttonCe.Location = new Point(269, 353);
buttonCe.Name = "buttonCe";
buttonCe.Size = new Size(48, 48);
buttonCe.TabIndex = 17;
buttonCe.Text = "CE";
buttonCe.UseVisualStyleBackColor = true;
buttonCe.Click += buttonCe_Click;
//
// buttonEq
//
buttonEq.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
buttonEq.Font = new Font("Yu Gothic UI", 14F);
buttonEq.Location = new Point(268, 407);
buttonEq.Name = "buttonEq";
buttonEq.Size = new Size(48, 102);
buttonEq.TabIndex = 18;
buttonEq.Text = "=";
buttonEq.UseVisualStyleBackColor = true;
buttonEq.Click += buttonEq_Click;
//
// formulaBox
//
formulaBox.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
formulaBox.BackColor = SystemColors.Window;
formulaBox.Font = new Font("Yu Gothic UI", 12F);
formulaBox.Location = new Point(12, 12);
formulaBox.Multiline = true;
formulaBox.Name = "formulaBox";
formulaBox.ReadOnly = true;
formulaBox.RightToLeft = RightToLeft.No;
formulaBox.Size = new Size(304, 124);
formulaBox.TabIndex = 19;
toolTip.SetToolTip(formulaBox, "整数の四則演算式を表示します。");
//
// resultBox
//
resultBox.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
resultBox.BackColor = SystemColors.Control;
resultBox.FormattingEnabled = true;
resultBox.Location = new Point(12, 146);
resultBox.Name = "resultBox";
resultBox.Size = new Size(304, 139);
resultBox.TabIndex = 20;
toolTip.SetToolTip(resultBox, "演算式の答えが記録されます。また、ダブルクリックで演算式に追加できます。");
resultBox.MouseDoubleClick += resultBox_MouseDoubleClick;
//
// Form_Calc
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(329, 521);
Controls.Add(buttonCe);
Controls.Add(buttonBs);
Controls.Add(resultBox);
Controls.Add(formulaBox);
Controls.Add(buttonEq);
Controls.Add(buttonDiv);
Controls.Add(button1Mult);
Controls.Add(buttonMinus);
Controls.Add(buttonPlus);
Controls.Add(buttonRE);
Controls.Add(buttonLE);
Controls.Add(button9);
Controls.Add(button8);
Controls.Add(button7);
Controls.Add(button6);
Controls.Add(button5);
Controls.Add(button4);
Controls.Add(button3);
Controls.Add(button2);
Controls.Add(button1);
Controls.Add(button0);
FormBorderStyle = FormBorderStyle.Fixed3D;
Icon = (Icon)resources.GetObject("$this.Icon");
KeyPreview = true;
MaximizeBox = false;
Name = "Form_Calc";
SizeGripStyle = SizeGripStyle.Hide;
StartPosition = FormStartPosition.CenterScreen;
Text = "Win_Calc";
TopMost = true;
FormClosed += Form_Calc_FormClosed;
KeyPress += Form1_KeyPress;
ResumeLayout(false);
PerformLayout();
}
#endregion
private Button button0; //'0'ボタン
private Button button1; //'1'ボタン
private Button button2; //'2'ボタン
private Button button3; //'3'ボタン
private Button button4; //'4'ボタン
private Button button5; //'5'ボタン
private Button button6; //'6'ボタン
private Button button7; //'7'ボタン
private Button button8; //'8'ボタン
private Button button9; //'9'ボタン
private Button buttonLE; //'('ボタン
private Button buttonRE; //')'ボタン
private Button buttonPlus; //'+'ボタン
private Button buttonMinus; //'-'ボタン
private Button button1Mult; //'*'ボタン
private Button buttonDiv; //'/'ボタン
private Button buttonBs; //'BS'ボタン
private Button buttonCe; //formulaBoxのクリア
private Button buttonEq; //'='ボタン
private TextBox formulaBox; //四則演算式表示用
private ListBox resultBox; //四則演算結果履歴
private ToolTip toolTip; //formulaBoxとresultBoxのツールチップ
}
}
