[C#入門]コード付。電卓の作り方。使用する構文も丁寧に解説。

スポンサーリンク
【C# 入門】 Windows Form Applicationで 電卓を作成してみよう! プログラミング

独習C#第2版をひと通り終え、C#の構文定着のために「電卓」を作りました。同じC#初学者の方が参考になればと思い、記事化しました。コード全文と作り方、構文の解説もありますので、ご活用ください。

▼独習C#第二版▼

▼独習C#第三版▼

では早速ですが、完成したコードをご確認ください。

完成した電卓のコード

Visual StudioのWindowsフォームアプリケーションで作成したため、コピペしただけでは動かせないと思います。作り方の手順は次章で解説しますので、コードエラーが出た場合などはこちらで全体をご確認ください。

▼Form_Calculator[デザイン]▼

電卓のフォーム画像

▼Calculator_Program.cs▼

using System;
using System.Windows.Forms;
namespace Calculater_ver1
{
    class Calculator_Program
    {
        public static void Main()
        {
            Application.Run(new Form_calculator());
        }
    }
}

▼Form_Calculator.cs▼

using System;
using System.Windows.Forms;
namespace Calculater_ver1
{
    public partial class Form_calculator : Form //Formクラスを継承
    {
       private bool Label_overwrite = true; //表示ラベルの上書き
    private bool Num_Plus = true; // +/- ボタンの正負判定
    private bool Num_Dot = false; // .ドット の有無判定
    private double dNum; //ラベルをdouble型に格納する変数
    private double dNum_Pool; //ラベルの数字をプールする変数
    private enum MarksType //列挙子
    {
      NON,
      PLUS,
      MINUS,
      MULTIPLIED,
      DEVIDED,
      PERCENT,  
    }
    private MarksType mType = MarksType.NON;
    public Form_calculator() //Form作成時に自動生成
    {
      InitializeComponent();
    }
    /* 0~9のクリックイベント*/
    private void zero_Click(object sender, EventArgs e)
    {
    if (Label_overwrite == true)
    {
      Num_Label.Text = zero.Text;
    }
    else
    {
      Num_Label.Text += zero.Text;
    }
  }
    private void one_Click(object sender, EventArgs e)
    {
      if (Label_overwrite == true) {
        Num_Label.Text = one.Text;
        Label_overwrite = false;
      }
      else {
        Num_Label.Text += one.Text;
      }
    }
    private void two_Click(object sender, EventArgs e)
    {
      if (Label_overwrite == true)
      {
        Num_Label.Text = two.Text;
        Label_overwrite = false;
      }
      else
      {
        Num_Label.Text += two.Text;
      }
    }
    private void three_Click(object sender, EventArgs e)
    {
      if (Label_overwrite == true)
      {
        Num_Label.Text = three.Text;
        Label_overwrite = false;
      }
      else
      {
        Num_Label.Text += three.Text;
      }
    }
    private void four_Click(object sender, EventArgs e)
    {
      if (Label_overwrite == true)
      {
        Num_Label.Text = four.Text;
        Label_overwrite = false;
      }
      else
      {
        Num_Label.Text += four.Text;
      }
    }
    private void five_Click(object sender, EventArgs e)
    {
      if (Label_overwrite == true)
      {
        Num_Label.Text = five.Text;
        Label_overwrite = false;
      }
      else
      {
        Num_Label.Text += five.Text;
      }
    }
    private void six_Click(object sender, EventArgs e)
    {
      if (Label_overwrite == true)
      {
        Num_Label.Text = six.Text;
        Label_overwrite = false;
      }
      else
      {
        Num_Label.Text += six.Text;
      }
    }
    private void seven_Click(object sender, EventArgs e)
    {
      if (Label_overwrite == true)
      {
        Num_Label.Text = seven.Text;
        Label_overwrite = false;
      }
      else
      {
        Num_Label.Text += seven.Text;
      }
    }
    private void eight_Click(object sender, EventArgs e)
    {
      if (Label_overwrite == true)
      {
        Num_Label.Text = eight.Text;
        Label_overwrite = false;
      }
      else
      {
        Num_Label.Text += eight.Text;
      }
    }
    private void nine_Click(object sender, EventArgs e)
    {
      if (Label_overwrite == true)
      {
        Num_Label.Text = nine.Text;
        Label_overwrite = false;
      }
      else
      {
        Num_Label.Text += nine.Text;
      }
    }
    private void all_clear_Click(object sender, EventArgs e)//AC(AllClear)ボタン
    {
      Num_Label.Text = "0";
      Label_overwrite = true;
      Num_Dot = false;
    }
    private void sign_Click(object sender, EventArgs e)// +/- ボタン
    {
      if (Num_Plus == true)
      {
        Num_Label.Text = "-" + Num_Label.Text;
        Num_Plus = false;
      }
      else {
        Num_Label.Text=Num_Label.Text.Replace("-", "");
        Num_Plus = true;
      }
    }
    private void dot_Click(object sender, EventArgs e)// .ドットボタン
    {
      if (Num_Dot == false)
      {
        Num_Label.Text = Num_Label.Text + ".";
        Num_Dot = true;
        Label_overwrite = false;
      }
    }
    private void plus_Click(object sender, EventArgs e)// +ボタン
    {
      Num_Dot = false;
      Label_overwrite = true;
      Num_PoolMethod();
      mType = MarksType.PLUS;
    }
    private void minus_Click(object sender, EventArgs e)// -ボタン
    {
      Num_Dot = false;
      Label_overwrite = true;
      Num_PoolMethod(); 
      mType = MarksType.MINUS;
    }
    private void multiplied_Click(object sender, EventArgs e)// ×ボタン
    {
      Num_Dot = false;
      Label_overwrite = true;
      Num_PoolMethod();
      mType = MarksType.MULTIPLIED;
    }
    private void divided_Click(object sender, EventArgs e)// ÷ボタン
    {
      Num_Dot = false;
      Label_overwrite = true;
      Num_PoolMethod();
      mType = MarksType.DEVIDED;
    }
    private void percent_Click(object sender, EventArgs e)// %ボタン
    {
      Num_Dot = false;
      Label_overwrite = true;
      mType = MarksType.PERCENT;
      Num_PoolMethod();
      mType = MarksType.NON;
    }
    private void equal_Click(object sender, EventArgs e)// =ボタン
    {
      Num_PoolMethod();
      mType = MarksType.NON;
      Num_Dot = false;
      Label_overwrite = true;
    }
    private void Num_PoolMethod()// + ー × ÷ % 演算子の計算処理
    {
      dNum = double.Parse(Num_Label.Text);
      switch (mType)
      {
        case MarksType.NON:
          dNum_Pool = dNum;
          break;
        case MarksType.PLUS:
          dNum_Pool += dNum;
          break;
        case MarksType.MINUS:
          dNum_Pool -= dNum;
          break;
        case MarksType.MULTIPLIED:
          dNum_Pool *= dNum;
          break;
        case MarksType.DEVIDED:
          dNum_Pool /= dNum;
          break;
        case MarksType.PERCENT:
          dNum_Pool = dNum * 0.01;
          break;
      }
      Num_Label.Text = dNum_Pool.ToString();
    }
    }
}

作り方のワークフロー

C#入門のため、初心者の方が誰でも作れるように細かい手順を全て解説しています。必要のない部分は適宜飛ばして読み進めてください。

  1. Visual Studio2022のインストール&プロジェクト作成
  2. フォームに部品を配置
  3. 内部処理の基準

①Visual Studio2022のインストール&プロジェクト作成

インストールがまだの方はこちらの記事を参考にインストールしてみてください。WindowsやMacなど、PCのスペックによってインストールするファイルが変わる可能性があるので、自分のPCに合ったものをインストールするようにしてください。

Visual Studio 2022のインストールとサンプルアプリ作成
C#で、Windowsアプリを作成してみます。手始めに準備として、インストール手順をまとめてみました。

当記事では、趣旨と逸れてしまうため、インストール方法の解説は省きます。インストールが完了したら、Visual Studio 2022を開き、プロジェクトを作成します。


「Windowsのスタート」→「Visual Studio 2022」と検索して開く。

visual Studio2022を開く

 


新しいプロジェクトの作成をダブルクリック。

新しいプロジェクトを作成

 

 


プロジェクトテンプレートで「Windows フォームアプリケーション(.NET Framework)」を選択し、次へと進みます。

Winformを開く

 


プロジェクト名とソリューション名を設定し、作成を押します。

プロジェクト名 Calculator-ver1
ソリューション名 Calculator-ver1

プロジェクト名とソリューション名を正しく入力しないと、コードをコピペしても動かない可能性があります。注意して設定してください。

プロジェクト名を設定する


作成が出来たら、アプリを実行してみましょう。「開始」をクリックしてください。無地のフォームが表示されたら問題なく作成できています。

開始」はデバッグ実行と言って、プログラムコードの処理を確認しつつ、アプリの挙動を確認できる機能です。アプリを作成する際は、ブレイクポイントを設定し、デバッグ実行で正しい処理が記述できているか、確認します。

今回作成している電卓アプリはWindows Form Applicationを使って作成します。(WinFormsと略して呼びます。)WinFormsはボタンやテキストボックスなどシステム開発で頻繫に使われる部品(コンポーネントやコントロールという)をドラッグ&ドロップで視覚的に配置し、アプリを開発できるプロジェクトテンプレートです。

WinFormsは2000年にリリースされたPC用ネイティブアプリ開発向けテンプレートで、リリース後、20年以上数多くの開発者に利用されてきたため、インターネットで様々な情報を得られます。WinFormsの後継にWPFもありますが、初心者がC#を初めて勉強するなら、WinFormsが適しているのではないかと思います。

②フォームに部品を配置する

通常、プログラムを作るには、UI(ユーザインタフェース)と内部処理の両方をプログラミングする必要があります。しかし、WinFormsでは、以下のようにツールボックスからコンポーネントをドラッグ&ドロップすることでUIを作成できます。開発者は四則演算の処理といった、内部処理のコーディングに集中できるのです。

ツールボックスとフォーム

では、電卓のUIを作成していきましょう。

まずは、ボタンが並べられるようにFormのプロパティを設定します。

プロパティとは、属性という意味ですが、フォームの詳細情報設定と理解してください。例えると、スマホの明るさ設定、音量設定はそれぞれ、明るさプロパティ、音量プロパティと言えます。明るさプロパティが100、音量プロパティが50などプロパティの値によってスマホの表示が変わるといった使い方をします。
デザイナーでForm1.csを選択し、右下辺りににプロパティウィンドウが表示されることを確認してください。

Formのプロパティウィンドウ

Sizeプロパティを651, 820と手入力で編集してください。そうすると、デザイナーのフォームの形が電卓っぽく変わったと思います。

※1 もし、デザイナーを閉じてしまった場合はソリューションエクスプローラーのForm1.csを右クリックして「デザイナーの表示」を選択することで、再度開くことができます。

※2 プロパティウィンドウが表示されていない場合は画面上部のナビメニュー「表示(V)」からプロパティウィンドウを選択して表示できます。


同様に(Name)プロパティとTextプロパティを「Form_calculator」に設定してください。

Name/Textプロパティの違いはNameが部品名でTextは表示名であることです。プログラミングで処理対象として部品を扱うには部品名の指定をしますが、ユーザが実際に目にするのはTextプロパティです。
ついでにForm1.csのファイル名も修正しておきましょう。ソリューションエクスプローラーのForm1.csを右クリックして「名前の変更」から「Form_calculator.cs」に変更してください。

では続いて、フォームに部品を配置していきます。ツールボックスのコモンコントロールにButtonとLabelがあることを確認してください。これらは一度クリックすると、カーソルが+に変わり、フォーム上にクリックすることで配置できます。

下の画像のように、Buttonを19個、Labelを2つ配置してください。

左側のツールボックスのButtonとLabelを画像のように並べます。ボタンは作成順に数字が付けられますが、次の工程でプロパティを変更するため、順番を気にする必要はありません。

部品を配置


各プロパティを以下のように設定します。Nameプロパティは今後、内部処理の記述で使用しますので、間違えないように入力もしくはコピペしてください。

電卓上の表示 部品種別 Nameプロパティ Textプロパティ
0 Button zero 0
1 Button one 1
2 Button two 2
3 Button three 3
4 Button four 4
5 Button five 5
6 Button six 6
7 Button seven 7
8 Button eight 8
9 Button nine 9
. Button dot .
AC Button all_clear AC
+/ー Button sign +/ー
% Button percent %
Button plus
Button minus
× Button multiplied ×
÷ Button divided ÷
Button equal
= Label mark =
0 Label Num_Label 0

 


各プロパティの設定が出来たら、「開始」からアプリを実行してみましょう。電卓UIの完成画像

このように電卓っぽいフォームが出来ていますでしょうか?この時点で各ボタンを押しても何も反応がないはずです。ここからはボタンが押されたときにLabelに処理が反映されるよう、内部処理を記述していきたいと思います。

③内部処理の記述

電卓の内部処理を実装するにあたって、どのように電卓を再現するのか、様々な方法がありますが、今回は以下のような方法で実装したいと思います。

  1. 押された数字をLabelのTextプロパティに渡す
  2. 次に押された数字を記号の通りに演算し、LabelのTextプロパティに渡す

①,②を繰り返し、「=」が押された時点でその時点の演算結果をLabelに表示するまでを実装していきたいとおもいます。では、それぞれの処理を実装していきましょう。

①押された数字をLabelのTextプロパティに渡す

電卓は押された数字がどんどん貯まっていきますよね。例えば「302」を入力する場合、「3→0→2」と続けて、各数字のボタンを押します。電卓の表示としては、「0」→「3」→「30」→「302」と順に数字が増えていくのが分かると思います。つまり、①0の場合は3に上書きする処理②3という文字に0を付け加えたら30になる処理の2つを記述すればいいことが分かります。

デザイナーからoneボタンをダブルクリックしてみてください。そうすると、「private void one_Click…」というようなコードが画面に表示されているでしょうか。

ダブルクリックによって、oneボタンのクリックイベントを作成しました。クリックイベントはクリックされた際の処理を記述します。oneが押されたときはNum_Labelに1を付け加えたいですよね。この場合、以下のような処理を記述します。

private bool Label_overwrite = true; //Num_Labelの上書きをする場合はtrue
private void one_Click(object sender, EventArgs e)
{
  if (Label_overwrite == true)//Num_Labelを上書きするか?
  {
    //真の場合
    Num_Label.Text = one.Text;
    Label_overwrite = false;
  }
  else
  {
    //偽の場合
    Num_Label.Text += one.Text;
  }
}

2通りの処理を記述する場合はif文で処理を分岐させます。if文の条件式にLabel_overwriteを使用しており、①のNum_Labelの上書きをする処理はNum_Label.Text = one.Textで上書き処理します。上書き後は②の処理をしたいので、Label_overwriteをfalseにし、以降、Num_Label.Text+=one.Textと数字が文字列として末尾に加えられるようにします。

では、同様にzeroボタン、twoボタン、threeボタン…もクリックイベントを作成しましょう。

public bool Label_overwrite = true; //Num_Labelの上書きをする場合はtrue
private void zero_Click(object sender, EventArgs e)
{
  if (Label_overwrite == true)
  {
    Num_Label.Text = zero.Text;
  }
  else
  {
    Num_Label.Text += zero.Text;
  }
}
private void one_Click(object sender, EventArgs e)
{
  if (Label_overwrite == true)
  {
    Num_Label.Text = one.Text;
    Label_overwrite = false;
  }
  else
  {
    Num_Label.Text += one.Text;
  }
}
private void two_Click(object sender, EventArgs e)
{
  if (Label_overwrite == true)
  {
    Num_Label.Text = two.Text;
    Label_overwrite = false;
  }
  else
  {
    Num_Label.Text += two.Text;
  }
}
//同様に3~9のクリックイベントも作成

※もし、処理が分からなくなった場合は完成したコードを参考にしてください。

処理を記述できたら、アプリをテストしてみましょう。「開始」を押して1~9のボタンを順にクリックしてください。Num_Labelに123456789と表示されていることが確認できると思います。

②次に押された数字を記号の通りに演算し、LabelのTextプロパティに渡す

続いて+ ー × ÷ % =の処理を考えていきます。電卓は「302」→「+」と押されただけでは、たし算されず、「302」→「+」→「5」→「ー」の時点で「302ー5」の計算結果が表示されます。そのため、単に+が押されたからたし算をするという訳にはいきません。

どの記号が押されたとしても、演算結果を正しく表示するために、今回はNum_PoolMethod()というメソッドを作成しましょう。(演算結果をプールするメソッドです)

private double dNum; //ラベルをdouble型に格納する変数
private double dNum_Pool; //ラベルの数字をプールする変数
private enum MarksType //列挙子
{
  NON,
  PLUS,
  MINUS,
  MULTIPLIED,
  DEVIDED,
  PERCENT
}
private MarksType mType = MarksType.NON;
private void Num_PoolMethod()// + ー × ÷ % 演算子の計算処理
{
  dNum = double.Parse(Num_Label.Text);
  switch (mType)
  {
   case MarksType.NON://=
    dNum_Pool = dNum;
    break;
   case MarksType.PLUS://+
    dNum_Pool += dNum;
    break;
   case MarksType.MINUS://ー
    dNum_Pool -= dNum;
    break;
   case MarksType.MULTIPLIED://×
    dNum_Pool *= dNum;
    break;
   case MarksType.DEVIDED://÷
    dNum_Pool /= dNum;
    break;
   case MarksType.PERCENT://%
    dNum_Pool = dNum * 0.01;
    break;
  }
  Num_Label.Text = dNum_Pool.ToString();//表示が更新される
}

dNumにはNum_Label.Textに表示されている値が格納され、表示された値は記号によって演算結果がdNum_Poolに格納されます。= + ー × ÷ %によってdNum_Poolに処理される値が異なっていることを確認してください。switch文の処理後はNum_Label.TextにdNum_Pool.ToString()を出力します。

では、このメソッドを使って、記号ボタンのクリックイベントを作成していきます。

private bool Num_Dot = false; // .ドット の有無判定

private void plus_Click(object sender, EventArgs e)// +ボタン
{
  Num_Dot = false;
  Label_overwrite = true;
  Num_PoolMethod();
  mType = MarksType.PLUS;
}
private void minus_Click(object sender, EventArgs e)// -ボタン
{
  Num_Dot = false;
  Label_overwrite = true;
  Num_PoolMethod();
  mType = MarksType.MINUS;
}
private void multiplied_Click(object sender, EventArgs e)// ×ボタン
{
  Num_Dot = false;
  Label_overwrite = true;
  Num_PoolMethod();
  mType = MarksType.MULTIPLIED;
}
private void divided_Click(object sender, EventArgs e)// ÷ボタン
{
  Num_Dot = false;
  Label_overwrite = true;
  Num_PoolMethod();
  mType = MarksType.DEVIDED;
}
private void percent_Click(object sender, EventArgs e)// %ボタン
{
  Num_Dot = false;
  Label_overwrite = true;
  mType = MarksType.PERCENT;
  Num_PoolMethod();
  mType = MarksType.NON;
}
private void equal_Click(object sender, EventArgs e)// =ボタン
{
  Num_PoolMethod();
  mType = MarksType.NON;
  Num_Dot = false;
  Label_overwrite = true;
}

Num_dotに関しては後ほど、「.」のクリックイベントで解説しますので、スルーしてください。

各記号の処理詳細を確認していきます。まず、「+ ー × ÷ 」と「%」と「=」で処理の流れが違うことが分かります。「+ ー × ÷」はLabel_overwrite=trueにしています。これは記号をクリックされた後、Num_Labelに新しい数字が表示されるよう、Label_overwriteを初期化しているのです。

「+ ー × ÷」は演算結果をプールするNum_PoolMethod()の後にmType=MarksType.○○となっている点がポイントです。「302」→「+」と押された時点でMarkType.NONになっています。そのため、dNum_Pool=302が代入されています。「302」→「+」→「3」→「ー」が押された時点では、MarkType.PLUSになっているため、dNum_Poolには305が代入されます。「302」→「+」→「3」→「ー」→「100」→「=」が押された時点ではMarkType.MINUSになっており、dNum_Poolには205が代入され、ToString()でNum_Label.Textに代入されます。Num_PoolMethod()の後にmType=MarksType.○○とすることで各記号の演算タイミングをワンステップ遅らせているのです。

「%」は押されたと同時に表示中のNum_Labelの値を×0.01したものを表示します。そのため、mType=MarksType.○○の後にNum_PoolMethod()が記述されています。

「=」は押された時点での計算結果を出力する処理を記述しています。そのため、mType=MarksType.○○の後にNum_PoolMethod()とすることでNum_Labelにプールされた演算結果を出力しています。

以上で大まかな電卓の処理が作成できましたね。では、アプリを「開始」でテストしてみましょう。

その他の処理

残りの「AC」「.」「+/ー」の処理も記述しておきましょう。

まずは「AC」ですが、これは簡単ですね。Num_Labelの値を0に上書きし、Label_overwriteをリセットします。

private void all_clear_Click(object sender, EventArgs e)//AC(AllClear)ボタン
{
  Num_Label.Text = "0";
  Label_overwrite = true;
  Num_Dot = false;
}

ちなみにACはall_clearの略だそうです。全然知りませんでした…。(公共広告機構しか知らん…w)

「.」は数字の処理に似ています。Num_Label.Textに表示されている値の後ろに「.」を付け加えれば完了です。しかし、「302.」の後に再度「.」が押された場合、「302..」となってしまう可能性があります。そのため、Num_Dotフラグを作成し、1度ドットボタンが押された後は、再度押されてもドットが加えられないように、if文で処理します。

先ほど、「+ ー × ÷」が押された時点でNum_Dotをfalseにしていたのはドットボタンが押せるようにリセットしていたのです。

private void dot_Click(object sender, EventArgs e)// .ドットボタン
{
  if (Num_Dot == false)
  {
    Num_Label.Text = Num_Label.Text + ".";
    Num_Dot = true;
    Label_overwrite = false;
  }
}

最後に「+/ー」はNum_Labelを符号違いの絶対値に変換する処理です。これも非常にシンプルで、Num_Label.Textに「ー」が含まれている場合、それをReplace()で削除し、含まれていない場合はNum_Label.Textの先頭に文字列として「-」を足しています。

private void sign_Click(object sender, EventArgs e)// .ドットボタン
{
  if (Num_Label.Text.Contains("-"))
  {
    Num_Label.Text = Num_Label.Text.Replace("-", "");
  }
  else
  {
    Num_Label.Text = "-" + Num_Label.Text;
  }
}

以上で電卓の作成が完成しました。最後にアプリをテストしてみましょう。「開始」をクリックしてください。

試しに、挙動に問題がないか、以下の計算をしてみましょう。

  • 302ー402=ー100
  • ー100を「+/ー」で100に
  • 100×3÷1.5=200
  • 200を「%」→2
  • AC→0

まとめ

普段、何気なく使っている機器でも、いざロジックを考えると意外にも苦戦することってありますよね。もちろん、ネットで調べれば、やり方は直ぐに分かりますが、自分の頭でロジックを考える練習をすることもいい勉強になるのではないかと思います。

自分もまだまだ勉強中ですが、初心者の頃に知りたかったアプリ開発のノウハウなどを詳しく解説していきますので、よかったら他の記事も読んでいってください!

以上です。お疲れ様でした!また、別の記事でお会いしましょう!近ため管理人のしゅんでした。

コメント

タイトルとURLをコピーしました