Windows Presentation Foundation (WPF) に入門してみる part 1


はじめに

今までC#GUIアプリを作る時はWindowsフォームアプリケーションを使用してきました。今後WPFに切り替えたいと思い新たに勉強する決意をしました。

xamlファイルを全く記述しないでC#のコードだけでGUIを構築するのが目標です。ほかの人にとってはあまり参考にならないかもしれません。

以前PyQtを使ったことがあり同じようなものを想像していますがそのようなことはWPFで可能なのでしょうか?

ぼちぼちと進めていく予定です。備忘録として学習した内容をブログに残していきます。

開発環境

Windows 10
Visual Studio Community 2019
.NET Core 5.0

学習内容

最初の画面

wpf_challenge」という名前で新しいプロジェクトを作りました。
出てきた画面がこちらです。
f:id:touch-sp:20210922182520p:plain:w600
どうやら「MainWindow.xaml.cs」という名前のファイルにコードを書けば良さそうです。左上のタブに出ているのでそこから選択しましょう。

プロジェクトを作成した直後はこのようになっています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace wpf_challenge
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

見にくいので右クリックから「Usingの削除と並び替え」を選択して現時点で不要なusingを消しました。コメントアウトされた3行も消しました。

このようにシンプルになりました。

using System.Windows;

namespace wpf_challenge
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

この状態でデバッグすると当たり前ですが真っ白のWindowが出てきます。

ボタンを一つ配置する

ボタンを配置するところから始めます。

using System.Windows;
using System.Windows.Controls;

namespace wpf_challenge
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            Button myButton = new Button() { Content = "ボタンだよ" };
            myButton.Click += pushButton;

            Content = myButton;
        }

        private void pushButton(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("押された!");
        }
    }
}

このようにすると画面いっぱいにボタンが配置されます。クリックにもちゃんと反応してくれます。
f:id:touch-sp:20210923100545p:plain:w500

ボタンを三つ配置する

先ほどはMainWIndowに直接ボタンを配置しました。

Content = myButton;


しかし複数のボタンを直接配置することはできません。

contentcontrol のコンテンツは、単一要素である必要があります。

MainWIndowに配置できるものは一つです。Grid やStackPanelというものを先に配置すれば複数のボタンを配置することができるようになります。

using System.Windows;
using System.Windows.Controls;

namespace wpf_challenge
{
    public partial class MainWindow : Window
    {
        Button[] buttons = new Button[3];

        public MainWindow()
        {
            InitializeComponent();

            //3行1列のグリッドを作成する
            Grid myGrid = new Grid();
            for (int i = 0; i < 3; i++)
            {
                myGrid.RowDefinitions.Add(new RowDefinition());
            }

            //MainWindowにグリッドを配置する
            Content = myGrid;

            //ボタンを三つ作成する
            for (int i = 0; i < 3; i++)
            {
                buttons[i] = new Button();
                buttons[i].Content = $"{i + 1}番目のボタンだよ";
                buttons[i].Name = $"button{i + 1}";
                buttons[i].Click += pushButton;
            }

            //グリッドのそれぞれの行にボタンを一つずつ配置する
            for (int i = 0; i < 3; i++)
            {
                Grid.SetRow(buttons[i], i);
                myGrid.Children.Add(buttons[i]);
            }
        }

        private void pushButton(object sender, RoutedEventArgs e)
        {
            string button_context = ((Button)sender).Name.ToString();
            MessageBox.Show(button_context + "が押されたよ!");
        }
    }
}


ボタンは3つありますがClickイベントは1つしか書いていません。押されたボタンの判別にはボタンのNameを使いました。

f:id:touch-sp:20210922225227p:plain:w500

上のコード内のループは一つにまとめることが可能です。

using System.Windows;
using System.Windows.Controls;

namespace wpf_challenge
{
    public partial class MainWindow : Window
    {
        Button[] buttons = new Button[3];

        public MainWindow()
        {
            InitializeComponent();

            //グリッドを作成して配置する
            Grid myGrid = new Grid();
            Content = myGrid;

            //グリッド内にボタンを配置する
            for (int i = 0; i < 3; i++)
            {
                buttons[i] = new Button();
                buttons[i].Content = $"{i + 1}番目のボタンだよ";
                buttons[i].Name = $"button{i + 1}";
                buttons[i].Click += pushButton;

                myGrid.RowDefinitions.Add(new RowDefinition());

                Grid.SetRow(buttons[i], i);
                myGrid.Children.Add(buttons[i]);
            }
        }

        private void pushButton(object sender, RoutedEventArgs e)
        {
            string button_context = ((Button)sender).Name.ToString();
            MessageBox.Show(button_context + "が押されたよ!");
        }
    }
}


画面いっぱいにボタンが配置されるのが嫌ならマージンを設定して下さい。

buttons[i].Margin = new Thickness(20);

このように変わります。
f:id:touch-sp:20210922232412p:plain:w500

using System.Windows;
using System.Windows.Controls;

namespace wpf_challenge
{
    public partial class MainWindow : Window
    {
        Button[] buttons = new Button[3];

        public MainWindow()
        {
            InitializeComponent();

            //グリッドを作成して配置する
            Grid myGrid = new Grid();
            Content = myGrid;

            //グリッド内にボタンを配置する
            for (int i = 0; i < 3; i++)
            {
                buttons[i] = new Button();
                buttons[i].Content = $"{i + 1}番目のボタンだよ";
                buttons[i].Name = $"button{i + 1}";
                buttons[i].Margin = new Thickness(20);
                buttons[i].Click += pushButton;

                myGrid.RowDefinitions.Add(new RowDefinition());

                Grid.SetRow(buttons[i], i);
                myGrid.Children.Add(buttons[i]);
            }
        }

        private void pushButton(object sender, RoutedEventArgs e)
        {
            string button_context = ((Button)sender).Name.ToString();
            MessageBox.Show(button_context + "が押されたよ!");
        }
    }
}

画像とボタンを二つずつ配置する

画像を表示させるためにはImageを使えば良いようです。
まずは画像を二つ用意しましょう。(今回は「1.jpg」と「2.jpg」です)
画像をexeファイルが存在するディレクトリに移しておきます。
自分の場合には、
wpf_challenge >> bin >> Debug >> net5.0-windows」になっています。

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

namespace wpf_challenge
{
    public partial class MainWindow : Window
    {
        Button[] buttons = new Button[2];
        Image[] pictures = new Image[2];

        public MainWindow()
        {
            InitializeComponent();

            pictures[0] = new Image();
            pictures[0].Source = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "1.jpg"));

            pictures[1] = new Image();
            pictures[1].Source = new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "2.jpg"));

            //グリッドを作成して配置する
            Grid myGrid = new Grid();
            Content = myGrid;

            //グリッド内にボタンとイメージを配置する
            for (int i = 0; i < 2; i++)
            {
                buttons[i] = new Button();
                buttons[i].Content = $"{i + 1}番目のボタンだよ";
                buttons[i].Name = $"button{i + 1}";
                buttons[i].Margin = new Thickness(20);
                buttons[i].Click += pushButton;

                myGrid.RowDefinitions.Add(new RowDefinition());
                myGrid.ColumnDefinitions.Add(new ColumnDefinition());

                Grid.SetRow(buttons[i], i);
                Grid.SetColumn(buttons[i], 1);
                myGrid.Children.Add(buttons[i]);

                pictures[i].Margin = new Thickness(20);
                Grid.SetRow(pictures[i], i);
                Grid.SetColumn(pictures[i], 0);
                myGrid.Children.Add(pictures[i]);
            }
        }
        private void pushButton(object sender, RoutedEventArgs e)
        {
            string button_context = ((Button)sender).Name.ToString();
            MessageBox.Show(button_context + "が押されたよ!");
        }
    }
}

上記を実行するとこのようなWindowが作成されます。
f:id:touch-sp:20210923095546p:plain:w500

さいごに

今回はここまでとします。これからぼちぼち勉強を進めていきます。

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

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