Windows Presentation Foundation (WPF) に入門してみる part 2 Imageを並べる


はじめに

xamlファイルを全く記述しないでC#のコードだけでGUIを構築するのが目標です。

備忘録として学習した内容をブログに残していきます。

前回はボタンと画像をMainWindowに表示するところまで学習しました。
Windows Presentation Foundation (WPF) に入門してみる part 1 - パソコン関連もろもろ

今回はメモリーゲーム(神経衰弱)のGUI部分だけを作ってみます。

画像は以前にPyQt5を使って作ったメモリーゲームです。同じものをC#で作ります。
f:id:touch-sp:20210923124531p:plain:w600
PyQt5 を使ってメモリーゲーム(神経衰弱)を作りました - パソコン関連もろもろ

概略

まずはMainWindowをGridを使って2行に分けます。その際の比率は5:1とします。
上側にはカードを並べるためのGridを入れます。(Gridの中にGridを入れる)
下側にはボタンを並べるGridを入れます。(4列のGridを入れてまんなか2つだけを使う)

言葉で書くと非常に簡単です。

本編

MainWindowを5:1に分割する

//2行のグリッドを作成して配置する(高さは5:1)
Grid mainGrid = new Grid();
mainGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(5, GridUnitType.Star) });
mainGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });

Content = mainGrid;

MainWindowの下にボタンを2つ配置する

先ほど作ったGridの下にさらにGridを入れてボタンを配置します。

Grid lowerGrid = new Grid();
for (int i = 0; i < 4; i++)
{
    lowerGrid.ColumnDefinitions.Add(new ColumnDefinition());
}

Grid.SetRow(lowerGrid, 1);
mainGrid.Children.Add(lowerGrid);

Button startButton = new Button() { Content = "start", Margin = new Thickness(20) };
startButton.Click += PushstartButton;
Grid.SetColumn(startButton, 1);
lowerGrid.Children.Add(startButton);

Button exitButton = new Button() { Content = "exit", Margin = new Thickness(20) };
exitButton.Click += PushexitButton;
Grid.SetColumn(exitButton, 2);
lowerGrid.Children.Add(exitButton);


ついでにクリックイベントも書きました。exitボタンクリックでアプリを終了させます。

private void PushstartButton(object sender, RoutedEventArgs e)
{
    //今後書く
}
private void PushexitButton(object sender, RoutedEventArgs e)
{
    Close();
}

MainWindowの上にカードを並べる

カードの画像はこちらからダウンロードできます。

まずImageの配列を宣言しますが場所に注意して下さい。

namespace wpf_challenge
{
    public partial class MainWindow : Window
    {
        Image[] cards; // <=ここ

        public MainWindow()
        {
            InitializeComponent();


ここで宣言しておかないとボタンのクリックで表示を変更したりできなくなります。

あとはただ並べるだけです。

int tate = 2;
int yoko = 4;

cards = new Image[tate * yoko];
BitmapImage img = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "card.jpg"));
for (int i = 0; i < (tate * yoko); i++)
{
    cards[i] = new Image() { Margin = new Thickness(10) };
    cards[i].Source = img;
}

Grid upperGrid = new Grid();
Grid.SetRow(upperGrid, 0);
mainGrid.Children.Add(upperGrid);

for(int i = 0; i < tate; i++)
{
    upperGrid.RowDefinitions.Add(new RowDefinition());
}

for(int i = 0; i < yoko; i++)
{
    upperGrid.ColumnDefinitions.Add(new ColumnDefinition());
}

for (int i = 0; i < tate; i++)
{
    for (int j = 0; j < yoko; j++)
    {       
        Grid.SetRow(cards[yoko * i + j], i);
        Grid.SetColumn(cards[yoko * i + j], j);
        upperGrid.Children.Add(cards[yoko * i + j]);
    }
}

おまけ(最大化とツールバーの消去)

WindowStyle = WindowStyle.None;
WindowState = WindowState.Maximized;

出来上がったWindow(文字サイズ変更前)

f:id:touch-sp:20210923140330p:plain:w600

ボタンの文字サイズにはなにも手を加えていません。非常に小さくて見にくいです。

文字サイズを変えてみましょう。(ついでに最初の文字を大文字にしました)
このようにすると適当なサイズに調整してくれるようです。

変更前のコード

Button startButton = new Button() { Content = "start", Margin = new Thickness(20) };

変更後のコード

Button startButton = new Button() { Margin = new Thickness(20) };
startButton.Content = new Viewbox() { Stretch = Stretch.Uniform, Child = new Label() { Content = "Start" } };

「Stretch = Stretch.Uniform」の部分は省略可能なのでこれでも良いです。

Button startButton = new Button() { Margin = new Thickness(20) };
startButton.Content = new Viewbox() { Child = new Label() { Content = "Start" } };

出来上がったWindow(文字サイズ変更後)

f:id:touch-sp:20210923145200p:plain:w600

完成したコード

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

namespace wpf_challenge
{
    public partial class MainWindow : Window
    {
        Image[] cards; // <=ここ

        public MainWindow()
        {
            InitializeComponent();

            WindowStyle = WindowStyle.None;
            WindowState = WindowState.Maximized;

            //2行のグリッドを作成して配置する(高さは5:1)
            Grid mainGrid = new Grid();
            mainGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(5, GridUnitType.Star) });
            mainGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });

            Content = mainGrid;

            Grid lowerGrid = new Grid();
            for (int i = 0; i < 4; i++)
            {
                lowerGrid.ColumnDefinitions.Add(new ColumnDefinition());
            }

            Grid.SetRow(lowerGrid, 1);
            mainGrid.Children.Add(lowerGrid);

            Button startButton = new Button() { Margin = new Thickness(20) };
            startButton.Content = new Viewbox() { Child = new Label() { Content = "Start" } };
            startButton.Click += PushstartButton;
            Grid.SetColumn(startButton, 1);
            lowerGrid.Children.Add(startButton);

            Button exitButton = new Button() { Margin = new Thickness(20) };
            exitButton.Content = new Viewbox() { Child = new Label() { Content = "Exit" } };
            exitButton.Click += PushexitButton;
            Grid.SetColumn(exitButton, 2);
            lowerGrid.Children.Add(exitButton);

            int tate = 2;
            int yoko = 4;

            cards = new Image[tate * yoko];
            BitmapImage img = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "card.jpg"));
            for (int i = 0; i < (tate * yoko); i++)
            {
                cards[i] = new Image() { Margin = new Thickness(10) };
                cards[i].Source = img;
            }

            Grid upperGrid = new Grid();
            Grid.SetRow(upperGrid, 0);
            mainGrid.Children.Add(upperGrid);

            for(int i = 0; i < tate; i++)
            {
                upperGrid.RowDefinitions.Add(new RowDefinition());
            }

            for(int i = 0; i < yoko; i++)
            {
                upperGrid.ColumnDefinitions.Add(new ColumnDefinition());
            }

            for (int i = 0; i < tate; i++)
            {
                for (int j = 0; j < yoko; j++)
                {       
                    Grid.SetRow(cards[yoko * i + j], i);
                    Grid.SetColumn(cards[yoko * i + j], j);
                    upperGrid.Children.Add(cards[yoko * i + j]);
                }
            }
        }
        private void PushstartButton(object sender, RoutedEventArgs e)
        {
            //今後書く
        }
        private void PushexitButton(object sender, RoutedEventArgs e)
        {
            Close();
        }
    }
}


ポイントは「tate」「yoko」の変数を変更しても自動的にきれいにカードを並べてくれるところです。

さいごに

つづきはこちら。
Windows Presentation Foundation (WPF) に入門してみる part 3 ComboBoxを使う - パソコン関連もろもろ

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