Windows Presentation Foundation (WPF) に入門してみる part 5 テンキー付きの計算ドリルを作る


はじめに

xamlファイルを全く記述しないでC#のコードだけでGUIを構築するのが目標です。
今回も「XAML」「バインディング」などの知識ゼロのまま進めていきます。

前回Viewboxを使った正方形について学習しました。
Windows Presentation Foundation (WPF) に入門してみる part 4 Viewboxを使う - パソコン関連もろもろ


正方形が維持できることが分かったので今回はボタンを並べてテンキー風にしたいと思います。それぞれのボタンは正方形が崩れないようにします。

今回の目的

このような計算ドリルを作っていきます。
f:id:touch-sp:20210928212211p:plain:w500


方法

今回もGUI部分に関係するコードと計算ドリルに関係するコードをそれぞれ別のファイルに書きました。

GUI部分のコード

コントロール(ButtonやImage)を複数ならべて配置する場合はforやforeachによるループが使用できます。
コントロールを配置する位置やサイズを具体的に数値で指定する必要はありません。

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;

namespace calc_gui
{
    public partial class MainWindow : Window
    {
        BitmapImage star = new(new Uri(AppDomain.CurrentDomain.BaseDirectory + "/star.png"));
        TextBlock yourInput = new() { Text = "" };
        TextBlock l_question = new() { Text = "" };
        TextBlock c_question = new() { Text = "" };
        TextBlock r_question = new() { Text = "" };

        Button startButton = new()
        {
            Margin = new Thickness(10),
            Content = new Viewbox() { Child = new Label() { Content = "はじめる" } }
        };

        Button[] numberkeys = new Button[11];
        Image[] starImage = new Image[20];

        private void WindowSetting()
        {
            //WindowStyle = WindowStyle.None;
            WindowState = WindowState.Maximized;
            Title = "けいさん";

            Grid mainGrid = new();
            mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(2, GridUnitType.Star) });
            mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(8, GridUnitType.Star) });
            Content = mainGrid;

            Grid upperPanel = new() { Margin = new Thickness(3) };
            for(int i = 0; i < 2 ; i++)
            {
                upperPanel.RowDefinitions.Add(new RowDefinition());
            }
            for(int i = 0; i < 10; i++)
            {
                upperPanel.ColumnDefinitions.Add(new ColumnDefinition());
            }
            Grid.SetRow(upperPanel, 0);
            mainGrid.Children.Add(upperPanel);

            for(int i = 0; i < 20; i++)
            {
                starImage[i] = new();
                starImage[i].Source = null;
                Grid.SetColumn(starImage[i], i % 10);
                Grid.SetRow(starImage[i], i / 10);
                upperPanel.Children.Add(starImage[i]);
            }

            Grid lowerPanel = new();
            lowerPanel.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(7, GridUnitType.Star) });
            lowerPanel.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(2, GridUnitType.Star) });
            Grid.SetRow(lowerPanel, 1);
            mainGrid.Children.Add(lowerPanel);

            Grid leftPanel = new();
            leftPanel.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(4, GridUnitType.Star) });
            leftPanel.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
            Grid.SetColumn(leftPanel, 0);
            lowerPanel.Children.Add(leftPanel);

            Grid questionBox = new();
            questionBox.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(2, GridUnitType.Star) });
            questionBox.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
            questionBox.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(2, GridUnitType.Star) });
            Grid.SetRow(questionBox, 0);
            leftPanel.Children.Add(questionBox);

            Viewbox l_questionBox = new() { Child = l_question };
            Viewbox c_questionBox = new() { Child = c_question };
            Viewbox r_questionBox = new() { Child = r_question };
            
            Grid.SetColumn(questionBox, 0);
            Grid.SetColumn(c_questionBox, 1);
            Grid.SetColumn(r_questionBox, 2);

            questionBox.Children.Add(l_questionBox);
            questionBox.Children.Add(c_questionBox);
            questionBox.Children.Add(r_questionBox);

            Grid startButtonGrid = new();
            startButtonGrid.ColumnDefinitions.Add(new ColumnDefinition());
            startButtonGrid.ColumnDefinitions.Add(new ColumnDefinition());
            startButtonGrid.ColumnDefinitions.Add(new ColumnDefinition());
            Grid.SetRow(startButtonGrid, 1);
            leftPanel.Children.Add(startButtonGrid);

            startButton.Click += PushStartButton;
            Grid.SetColumn(startButton, 1);
            startButtonGrid.Children.Add(startButton);
            
            Grid rightPanel = new();
            rightPanel.RowDefinitions.Add(new RowDefinition());
            rightPanel.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
            Grid.SetColumn(rightPanel, 1);
            lowerPanel.Children.Add(rightPanel);

            //右上
            Viewbox upperRightBox = new();
            upperRightBox.Child = yourInput;
            Grid.SetRow(upperRightBox, 0);
            rightPanel.Children.Add(upperRightBox);

            //右下
            Grid lowerRight = new() { Width = 300, Height = 400 };
            Viewbox numberKeysBox = new() { Child = lowerRight };
            Grid.SetRow(numberKeysBox, 1);
            rightPanel.Children.Add(numberKeysBox);

            //数字ボタン
            for(int i = 0; i < 3; i++)
            {
                lowerRight.ColumnDefinitions.Add(new ColumnDefinition());
            }
            for(int i = 0; i < 4; i++)
            {
                lowerRight.RowDefinitions.Add(new RowDefinition());
            }
            for (int i = 0; i < 10; i++)
            {
                numberkeys[i] = new() { Margin = new Thickness(3) };
                numberkeys[i].Content = new Viewbox() { Child = new Label() { Content = i.ToString() } };
                numberkeys[i].Name = $"button_{i}";
                numberkeys[i].Click += PushNumberKey;
                Grid.SetRow(numberkeys[i], (9 - i) / 3);
                if (i == 0)
                {
                    Grid.SetColumn(numberkeys[i], 0);
                }
                else
                {
                    Grid.SetColumn(numberkeys[i], (i + 2) % 3);
                }
                lowerRight.Children.Add(numberkeys[i]);
            }
            numberkeys[10] = new() { 
                Margin = new Thickness(3), 
                Content = new Viewbox() { Child = new Label() { Content = "けす" } } 
            };
            numberkeys[10].Click += PushClearKey;
            Grid.SetColumn(numberkeys[10], 1);
            Grid.SetColumnSpan(numberkeys[10], 2);
            Grid.SetRow(numberkeys[10], 3);
            lowerRight.Children.Add(numberkeys[10]);

            foreach (Button each in numberkeys) each.IsEnabled = false;
        }
        private void PushClearKey(object sender, RoutedEventArgs e)
        {
            yourInput.Text = "";
        }
    }
}



計算ドリル部分のコード

using System;
using System.Diagnostics;
using System.Linq;
using System.Media;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace calc_gui
{
    public partial class MainWindow : Window
    {
        int l_number;
        int r_number;
        int answer;
        string center_char = "+";

        Random rnd;

        SoundPlayer ok = new(AppDomain.CurrentDomain.BaseDirectory + "/sounds/ok.wav");
        SoundPlayer finished = new(AppDomain.CurrentDomain.BaseDirectory + "/sounds/yattane.wav");
        SoundPlayer dummy = new(AppDomain.CurrentDomain.BaseDirectory + "/sounds/dummy.wav");

        Stopwatch sw;
        TimeSpan ts;

        int solved_count;

        public MainWindow()
        {
            InitializeComponent();
            WindowSetting();
        }
        private void createNewQuestion()
        {
            l_number = rnd.Next(10, 16);
            r_number = rnd.Next(1, 20 - l_number);
            answer = l_number + r_number;

            l_question.Text = String.Format("{0, 2}", l_number);
            c_question.Text = center_char;
            r_question.Text = String.Format("{0, -2}", r_number);
        }
        private void PushStartButton(object sender, RoutedEventArgs e)
        {
            dummy.Play();

            sw = new();
            sw.Start();
            
            startButton.IsEnabled = false;
            foreach (Image each in starImage) each.Source = null;
            foreach (Button each in numberkeys) each.IsEnabled = true;
            rnd = new();
            solved_count = 0;
            createNewQuestion();
        }
        private async void PushNumberKey(object sender, RoutedEventArgs e)
        {
            string button_number = ((Button)sender).Name.ToString().Replace("button_", "");
            if (yourInput.Text.Count() == 0 && button_number == "0") return;
            foreach (Button each in numberkeys) each.IsEnabled = false;
            if (yourInput.Text.Count() < 2)
            {
                yourInput.Text = yourInput.Text + button_number;
            }

            int your_anser = int.Parse(yourInput.Text);
            if(your_anser == answer)
            {
                ok.Play();
                await Task.Delay(300);
                yourInput.Text = "";
                starImage[solved_count].Source = star;
                if (solved_count < 19)
                {
                    solved_count += 1;
                    createNewQuestion();
                }
                else
                {
                    //game finish
                    sw.Stop();
                    ts = sw.Elapsed;

                    l_question.Text = ts.ToString("%m");
                    r_question.Text = ts.ToString("%s");
                    c_question.Text = ":";

                    await Task.Delay(200);
                    finished.Play();
                    await Task.Delay(800);
                    startButton.IsEnabled = true;
                    return;
                }  
            }
            foreach (Button each in numberkeys) each.IsEnabled = true;
        }
    }
}



さいごに

間違いや改善点があればコメント頂けましたら幸いです。

前回までの記事

Windows Presentation Foundation (WPF) に入門してみる part 1 - パソコン関連もろもろ
Windows Presentation Foundation (WPF) に入門してみる part 2 Imageを並べる - パソコン関連もろもろ
Windows Presentation Foundation (WPF) に入門してみる part 3 ComboBoxを使う - パソコン関連もろもろ
Windows Presentation Foundation (WPF) に入門してみる part 4 Viewboxを使う - パソコン関連もろもろ