WPFって何だぜ(^~^)?

ばびゃー(^◇^) まぶー(^~^) 公開下書き

20210918wpf100.png
(👆 2021年11月に .NET 6 MAUI が出てくれば、WPFは廃れていくレガシー技術? 知らんけど)

ramen-tabero-futsu2.png
「 WPFって何だぜ?」

kifuwarabe-futsu.png
「 お父んが無計画に手を広げていて つら……」

ohkina-hiyoko-futsu2.png
「 2010年頃に流行り出した デスクトップ アプリのフレームワークね」

📖 What is .NET MAUI?

ohkina-hiyoko-futsu2.png
「 👆 2021年11月に .NET 6 の登場とともに アプリのフレームワーク MAUI が出てくれば
そっちを使う人がいるかどうかは まだ はっきりしないわね」

ramen-tabero-futsu2.png
「 何をしたら WPF なのか コーディングだけ教えてくれだぜ」

LivetCask

📖 LivetCask 3.2.3.2

ohkina-hiyoko-futsu2.png
「 👆 Visual Studio 2019 などを使って WPFアプリケーションのプロジェクトを新規作成して、 NuGetを使って LivetCask をインストールしなさい。
NuGet って何?っていう人は ここで諦めなさい」

20210828shogi93.png

📖 shogi-clock-wpf

ramen-tabero-futsu2.png
「 👆 そして時間は飛ぶが でけた」

kifuwarabe-futsu.png
「 じゃあ もう1個 アプリを作ってくれだぜ」

20210828shogi94a1.png

ramen-tabero-futsu2.png
「 👆 じゃあ プロジェクトを作る所から……」

20210828shogi95a1.png

ramen-tabero-futsu2.png
「 👆 上に WPF アプリ(.NET Framework) があって、下に WPF App(.NET) があって、
どっちを使えばいいのか分からないが、試しに WPF App(.NET) を使ってみるぜ」

kifuwarabe-futsu.png
「 不安だぜ」

20210828shogi96.png

ramen-tabero-futsu2.png
「 👆 あとで GitHub に上げることを考えて 予め ローカル リポジトリを先に作っておいて、そこにプロジェクトを置こうぜ」

20210828shogi97.png

ramen-tabero-futsu2.png
「 👆 こんな感じで 画面が出てくると思うが、最初にやることは……」

20210828shogi98a1.png

ramen-tabero-futsu2.png
「 👆 NuGet の管理画面へ行けだぜ」

kifuwarabe-futsu.png
「 こんな画面見せられて 最初に NuGet の管理画面に行くのは 素人お断りな デザインだと思うぜ」

20210828shogi99a1.png

ramen-tabero-futsu2.png
「 👆 LivetCask をクリックしろだぜ」

ohkina-hiyoko-futsu2.png
「 似た名前が 多すぎ!」

20210828shogi100a1.png

ramen-tabero-futsu2.png
「 👆 インストールしろだぜ」

20210828shogi101a1.png

ramen-tabero-futsu2.png
「 👆 あれっ? 参照が無(ね)?」

kifuwarabe-futsu.png
「 .NET Framework と .NET では違うのでは?」

ramen-tabero-futsu2.png
「 は~~あん?
じゃあ ほっといて 先に進むか……」

20210828shogi102a1.png

ramen-tabero-futsu2.png
「 👆 新しいフォルダー を追加しろだぜ」

20210828shogi103.png

ramen-tabero-futsu2.png
「 👆 [Ctrl] + [C] でコピーして [Ctrl] + [V] 連打で複製しろだぜ」

20210828shogi104a1.png

ramen-tabero-futsu2.png
「 👆 Models, ViewModels, Views の3つのフォルダーを作れだぜ。
もしかすると これは ローカルのやり方かもしれないが、各自 勝手に調べろだぜ」

20210829shogi105a1.png

ramen-tabero-futsu2.png
「 👆 最初に飛び出るウィンドウがどれかというのは、 App.xaml の Applicationタグの StartupUri 属性にファイルパスが書いてあるから
分かれだぜ」

20210829pg106a1.png

ramen-tabero-futsu2.png
「 👆 Window のような 見た目をレイアウトしたいようなものは Views フォルダーの下に置けだぜ。
StartupUri 属性の値も変えろだぜ」

ramen-tabero-futsu2.png
「 F5キーで Run してもいいが、初回は 10分ぐらいビルドにかかるから、カップ麺を食べるなら今だぜ」

20210829pg107.png

ramen-tabero-futsu2.png
「 👆 Windowだけでてきても 嬉しくないな」

20210829pg108a1.png

ramen-tabero-futsu2.png
「 👆 そこで ユーザーコントロールを作れだぜ」

20210829pg109.png

ramen-tabero-futsu2.png
「 👆 今回は 整ったレイアウトとか何もイメージできてないんで UsserControl1 という名前でいいだろ。
そこに ごちゃごちゃ 追加していこう」

20210829pg110a1.png

ramen-tabero-futsu2.png
「 👆 そのあと すぐビルド」

20210829pg111a1.png

ramen-tabero-futsu2.png
「 👆 すると 左上の方を見て分かる通り UserControl1 は デザイン画面で 置けるアイテムになったぜ。
こうやって 部品を作って行けだぜ」

20210829pg112.png

ramen-tabero-futsu2.png
「 👆 例えば MainWindow の上に UserControl を配置できるわけだぜ。
この デザイナー画面、よく不調になって出てこなくなるので Visual Studio 2019 を再起動しろだぜ」

kifuwarabe-futsu.png
「 わらう」

UserControl のレイアウト

20210829pg113a1.png

ramen-tabero-futsu2.png
「 👆 デザイナーの使い方は 初見では無理。
グッドラック!」

ohkina-hiyoko-futsu2.png
「 この画面、カスタマイズできるから このようにせよ、という 定跡を組めないのよ。
デフォルトの状態で 使い続けるのも それはそれで 非効率だし」

20210829pg114a1.png

ramen-tabero-futsu2.png
「 👆 デザイナに置いたものが 重なってしまって ウギギギギ! となったら 画面左上の ドキュメント アウトライン
開けだぜ。 ツリー構造になってる。 あとは分かれ」

kifuwarabe-futsu.png
「 左上にあるとも限らないんだけどな」

20210829pg115a1.png

ramen-tabero-futsu2.png
「 👆 そんなときは [表示] - [その他のウィンドウ] - [ドキュメント アウトライン] で出せだぜ」

ohkina-hiyoko-futsu2.png
「 メニューバーが表示されてない人は どうすんの?」

ramen-tabero-futsu2.png
「 グッドラック」

20210829pg116a1.png

ramen-tabero-futsu2.png
「 👆 テキストボックスを 読取専用 にするにも、 GUI から操作する方法と、
テキストエディターの XML形式ファイルを編集する方法があり、どっちも同期しているから 片方を編集すれば もう片方も編集される。
しかし GUI は カテゴリのところが名前順になってて 『あれ? 無い?』 と思うことがあるから グッドラック」

ramen-tabero-futsu2.png
「 まあ とにかく レイアウト担当は こんなもんだぜ。 データのことは とにかく 考えないこと!
座標を調整するとか、 右揃え、左揃えにするとか、 ウィンドウのサイズを広げたときに どこが伸びるとか、
見た目だけ やれだぜ」

UserControl のデータ

20210829pg117a1.png

ramen-tabero-futsu2.png
「 👆 画面に紐づく データの部分の実装の仕方を説明する。
しかし実際問題 レイアウト:デザイナー会社、 データ:実装会社 の2つに分かれて専門的に分業してるなんてことはなくて
まず詳細設計、次にコード実装 みたいに分かれてる」

kifuwarabe-futsu.png
「 デザイナーが デザイン画面使うとか、 コード担当が テキストエディター使うとかじゃないんだな」

ohkina-hiyoko-futsu2.png
「 デザイナー屋が Visual Studio 2019 開くわけないじゃないの。 Visual Studio 2019 上に存在する作業は全部 コード屋の仕事よ」

20210829pg118a1.png

ramen-tabero-futsu2.png
「 👆 とりあえず 名前の後ろに ViewModel とでも付けとけだぜ」

20210829pg119a1.png

ramen-tabero-futsu2.png
「 👆 見慣れたクラスのスケルトンが作られたと思う」

20210829pg120a1.png

ramen-tabero-futsu2.png
「 👆 で、これから先、 WPF とか MVVM とか言われる何かをするために 変わったことを 追加でやることになるぜ。
その1つが using Livet; を冒頭に書くことと、クラス名の横に : ViewModel を書くことだぜ」

ohkina-hiyoko-futsu2.png
「 そこまで オートでやってほしいわよね」

20210829pg121a1.png

ramen-tabero-futsu2.png
「 👆 普通のプロパティと違うのは セッター の方だぜ。
値が変わっていなければ 変更しない、
値が変わっていれば 変更して、 RaisePropertyChanged(プロパティ名) を呼び出すということだぜ」

kifuwarabe-futsu.png
「 めんどくさ」

20210829pg122a1.png

ramen-tabero-futsu2.png
「 👆 コンボボックスに入れるのは 普通の Dictionary だな。
ディクショナリーのキーが いわゆるコンボボックスの値、 ディクショナリーの値が コンボボックスの表示になるぜ。
これも セッター で Livet の書き方をしている」

ohkina-hiyoko-futsu2.png
「 ゲッターで Dictionaryを取得して、それでディクショナリーの内容を変更したら Livet のコード通らないけど いいの?
イミュータブルじゃないのが気になるなあ」

ramen-tabero-futsu2.png
「 そこのケアは パスだぜ」

20210829pg123a1.png

ramen-tabero-futsu2.png
「 👆 クラス ビューで見てみようぜ。 [表示] - [クラスビュー]

20210829pg124a1.png

ramen-tabero-futsu2.png
「 👆 今回は プロパティ名から TextBox だの ComboBox だの説明くさいものを省いてみるぜ。
理由としては そんな細かくしなくても 分かる程度のアプリだからだぜ」

20210829pg125a1.png

ramen-tabero-futsu2.png
「 👆 クラス ビューのタイトルバーを右クリックして 自動的に隠す を選べだぜ」

20210829pg126a1.png

ramen-tabero-futsu2.png
「 👆 左端に 片付いてくれるぜ」

ramen-tabero-futsu2.png
「 で、これだけだと レイアウトと データの間になんの連携も無いんで、バインド というのを行うぜ」

レイアウトとデータのバインド

20210829pg127a1.png

xmlns:ViewModels="clr-namespace:ShogiEnteringKingScoreingWpf.ViewModels"

ramen-tabero-futsu2.png
「 👆 ユーザーコントロールの冒頭に、上行を追加してくれだぜ。
XMLのnamespaceに ViewModels を追加してると思えだぜ」

20210829pg128a1.png

ramen-tabero-futsu2.png
「 👆 そのあとで DataContext プロパティの [新規] ボタンをクリックしろだぜ」

20210829pg129a1.png

ramen-tabero-futsu2.png
「 👆 すると さっき作った UserControl1ViewModel を認識するようになってるので、選べだぜ」

20210829pg130a1.png

ramen-tabero-futsu2.png
「 👆 XAML にコードが3行書き足されたし、プロパティ ウィンドウにも UserControl1ViewModel クラスのプロパティが表示されたな」

kifuwarabe-futsu.png
「 Unity とか触ってたら こういうの すぐ理解しそうだな」

20210829pg134a1.png

ramen-tabero-futsu2.png
「 👆 じゃあ デザイナーから テキストボックスをクリックして、 プロパティ ウィンドウの Text の、
小さな四角をクリック、 データバインドの作成 を選べだぜ」

kifuwarabe-futsu.png
「 分かりづら」

20210829pg135a1.png

ramen-tabero-futsu2.png
「 👆 URL プロパティを選べだぜ」

20210829pg136a1.png

ramen-tabero-futsu2.png
「 👆 TextBox タグに Text属性が付いたな。これでバインド済みだぜ。残りも全部やれだぜ」

ohkina-hiyoko-futsu2.png
「 ちっちゃな 四角をクリックするのが ストレスねー!」

20210829pg137.png

kifuwarabe-futsu.png
「 👆 コンボボックスに 正しくデータを設定するには どうやったらいいんだぜ?」

20210829pg138a1.png

ramen-tabero-futsu2.png
「 👆 ItemsSource に ディクショナリー型プロパティを バインドして、」

20210829pg139a1.png

ramen-tabero-futsu2.png
「 👆 ComboBox のタグの末尾の きわどいところに キャレット(文字を挿入するカーソル)を合わせて [Ctrl] + [Space] キーを打てだぜ」

kifuwarabe-futsu.png
「 やりづら」

20210829pg140a1.png

SelectedValuePath="Key" DisplayMemberPath="Value"

ramen-tabero-futsu2.png
「 👆 入力候補を使ってスペルミスをしないようにしながら、上記の属性を追加しろだぜ」

ohkina-hiyoko-futsu2.png
「 そこも GUI にしてほしいわねー」

20210829pg141.png

kifuwarabe-futsu.png
「 👆 見た目は 直ってそうだな」

UserControl のコマンド

ramen-tabero-futsu2.png
「 ボタンをクリックしたり、キーボードからショートカットを打鍵したときに 何か動くやつが コマンド だな。
インターネットで調べても チンプンカンプン なので、シンプルなやつを紹介するぜ」

20210829pg142a1.png

ramen-tabero-futsu2.png
「 👆 ICommand インターフェースを実装しろだぜ。 コードの全文は 次に示すぜ」

using System;
using System.Windows.Input;

namespace ShogiEnteringKingScoreingWpf.ViewModels
{
    /// <summary>
    /// 監視を開始するコマンドです
    /// </summary>
    public class MonitoringStart : ICommand
    {
        private bool _isEnabled = true;

        /// <summary>
        /// 実行できますか?
        /// </summary>
        public bool IsEnabled
        {
            get
            {
                return this._isEnabled;
            }
            set
            {
                if (this._isEnabled == value)
                {
                    return;
                }
                this._isEnabled = value;
                this.RaiseCanExecuteChanged();
            }
        }

        /// <summary>
        /// コマンドが実行可能かの状態が変わった時に通知します
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// 実行できますか?
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            return this.IsEnabled;
        }

        /// <summary>
        /// 実行
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            if (!this.IsEnabled)
            {
                return;
            }

            // TODO ここに処理を書く
            System.Windows.MessageBox.Show("実装されていません");
        }

        /// <summary>
        /// 実行可能かどうかの状況が変わった時に呼び出してください
        /// </summary>
        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    }
}

kifuwarabe-futsu.png
「 👆 めんどくさそう」

20210829pg143a1.png

ramen-tabero-futsu2.png
「 👆 データ コンテキストに さっきのコマンドを プロパティで取れるようにしとけだぜ。
そして ビルド しろ」

20210829pg143a1.png

ramen-tabero-futsu2.png
「 👆 コマンドも データ バインドやれだぜ」

20210829pg144.png

ramen-tabero-futsu2.png
「 👆 ビルドしていれば さっき ICommand を実装したやつが リストに居る。選べだぜ」

20210829pg145.png

kifuwarabe-futsu.png
「 👆 おお、ボタンをクリックしたら ダイアログボックスが出たぜ」

ramen-tabero-futsu2.png
「 こっから先が大変なんだけどな」

20210829pg146a1.png

ramen-tabero-futsu2.png
「 👆 実行したら IsEnabled を false にするとして、」

20210829pg147a1.png

ramen-tabero-futsu2.png
「 👆 わたしは もうデータ バインディングしちゃったけど もう1回見せたろ」

20210829pg148a1.png

ramen-tabero-futsu2.png
「 👆 IsEnabled プロパティを選べだぜ。これで OK」

ohkina-hiyoko-futsu2.png
「 ボタンを グレーアウトする命令を書くのではなく、
ボタンの IsEnabledプロパティと MonitoringStartコマンドの IsEnabledプロパティを バインドさせれば
同期するという発想なのね」

kifuwarabe-futsu.png
「 マナーよく作らないと どこかで無限ループしたり 何かいじったりして 破綻しそうだな」

ohkina-hiyoko-futsu2.png
「 マナーは 守られないのよ」

ビヘイビア(Behavior) と アクション(Action)とコマンド(ICommand)

📖 WPFのBehaviorを使おうぜ(^~^)?

ramen-tabero-futsu2.png
「 👆 ビヘイビア(Behavior) と アクション(Action)とコマンド(ICommand)は別記事に書いたぜ」

画面遷移 フレーム(Frame)とページ(Page)

📖 WPFってどうやって画面遷移すんの(^~^)?(Frame, Page 編)

ramen-tabero-futsu2.png
「 👆 画面遷移 フレーム(Frame) と ページ(Page)は別記事に書いたぜ」

別ウィンドウ(Window)の出し方

📖 WPFでWindow出そうぜ(^~^)?

ramen-tabero-futsu2.png
「 👆 別ウィンドウ(Window)の出し方は別記事に書いたぜ」

基本動作 アクティブ ウィンドウとの同期

20210903pg10.png

ramen-tabero-futsu2.png
「 👆 画面中央 上の デザイナーで フォームをいじって 画面中央 下の .xaml を書いているとき、
どの .cs ファイルだったかなと 画面左上の ソリューション エクスプローラーを見たら、
今 どのファイルを編集してるのか 分かんね、と思うときがあるだろ」

kifuwarabe-futsu.png
「 べつに どのファイルかなんか 気にしなくていいのに……」

20210903pg10a1.png

ramen-tabero-futsu2.png
「 👆 そんなとき、ソリューション エクスプローラーのツールボックスに並んでいる [アクティブ ウィンドウとの同期] ボタンをクリックしてみろだぜ」

20210903pg11a1.png

ramen-tabero-futsu2.png
「 👆 今 編集しているファイルが選ばれたな」

ohkina-hiyoko-futsu2.png
「 そんなの Visual Studio Code なら オートでやってくれるわよ?
名前は似てるけど、 Visual Studio より Visual Studio Code の方が 大変 便利よ~」

ramen-tabero-futsu2.png
「 悲しい……」

ramen-tabero-futsu2.png
「 今はまだ 要らないと思うかも知れないが、大量のファイルがあるとき、検索で飛ぶと それが ソリューション エクスプローラーのどのファイルか調べたくなるんで、そんなとき使うぜ」

コード ビハインド

20210914wpf30a1.png

ramen-tabero-futsu2.png
「 👆 なんか .xaml と .cs ファイルが2枚ぺったり くっついてるやつを コードビハインド と呼ぶらしいぜ。
意味としては デザインと ロジックが分かれていていいでしょ、ということらしいぜ」

kifuwarabe-futsu.png
「 しかし コードビハインドの ロジックの方の .cs ファイル、
コードを汚すな! とか 書かれていて ここにロジック書いたら負け! みたいな雰囲気がある……」

ohkina-hiyoko-futsu2.png
「 ここに コードを書け、みたいな 見た目してるのに……」

ViewModel にコマンドを持たせる、その名も ViewModelCommand

ramen-tabero-futsu2.png
「 👇 ViewModelCommand 知らないと 困る。記事 書かな」

📖 wpf-view-model-command-practice

コマンドを使った画面遷移

20210914wpf31.png

ramen-tabero-futsu2.png
「 👆 じゃあ 試しに こういう感じで 画面遷移してみようぜ?
Web系を 15年やってたら ウィンドウの開け閉めは スキル弱ってるよな」

kifuwarabe-futsu.png
「 👇 別記事に分けてやろうぜ」

📖 WPFで画面遷移ってどうやんの(^~^)?(コマンドを使ったWindow編)

リソース ディクショナリー

📖 WPFのリソース ディクショナリって何だぜ(^~^)?

ramen-tabero-futsu2.png
「 👆 リソース ディクショナリーの使い方を覚えてから 画面を作った方が効率的と思うぜ」

Grid, StackPanel, WrapPanel, ...

📖 WPFのGridとかStackPanelとかWrapPanelって何なんだぜ(^~^)?

ramen-tabero-futsu2.png
「 👆 これ もっと早く説明しても いいぐらいだよな」

ウィンドウを消そうぜ

📖 WPFでタイトルバー消そうぜ(^~^)

ramen-tabero-futsu2.png
「 👆 Windowsのルックスから おさらば だぜ」

マウス操作の練習をしようぜ?

📖 WPFでマウス操作の練習をしようぜ(^~^)?

ramen-tabero-futsu2.png
「 👆 マウス操作できないと不便」

ブラシを自作しようぜ?

📖 WPFでブラシの練習をしようぜ(^~^)?

ramen-tabero-futsu2.png
「 👆 タイルパターンだけどな」

ユーザーコントロールを作ろうぜ

📖 WPFでUserControlの練習をしようぜ(^~^)?

ramen-tabero-futsu2.png
「 👆 Windowsのルックスを止める要望 まず最初に出てくるんで」

スキルアップのための課題を作ろうぜ?

ramen-tabero-futsu2.png
「 全然 機能の使い方の説明が終わらん。 これでは アプリケーション開発の全体図がなかなか見えないぜ。
別のやり方も やろうぜ?」

kifuwarabe-futsu.png
「 Microsoft が用意して欲しいよな、チュートリアル」

📖 WPF演習問題 デスクトップ上のちり紙

ramen-tabero-futsu2.png
「 👆 よし、別ページを立てよう」

TODO やらなければいけないこと

  • 年月日時分秒ミリ秒 タイムゾーン
  • 多国語対応
  • 国際化(通貨表記対応)
  • リソース管理(文字列、数、画像、アイコン)
  • データの永続化(ファイル、データベース)
  • 二重起動防止(ミューテックス)
  • プロセス間通信
  • デスクトップアプリとWebアプリの連携
  • ユーザー認証
  • ダイアログボックス、ウィンドウなどを用いた画面遷移
  • 検索結果テーブルの表示とそのページング
  • WindowsフォームっぽくないGUIの作成
    • ラップパネル(フローティングレイアウト)、グリッド、スタックパネル
    • クローム(ウィンドウの閉じるボタンとかを外す)
    • Windowsクラシックとか 影が付いているウィンドウとか混在するのを整える
  • その他

ramen-tabero-futsu2.png
「 👆 くそ-っ。 しかも全部 レガシーだぜ」

kifuwarabe-futsu.png
「 レガシーというか、 Windowsプログラミングだろ」

ohkina-hiyoko-futsu2.png
「 WPFの範囲なのか、 C#の範囲なのかは 切り分けた方がいいんじゃない?」

ramen-tabero-futsu2.png
「 C#のデスクトップ アプリ とでも言うか」

次にお勧めのエグザンプル

📖 wpf-user-control-like-dialog-box-practice

何度でもクリック!→

むずでょ@きふわらべ第29回世界コンピューター将棋選手権一次予選36位

光速のアカウント凍結されちゃったんで……。ゲームプログラムを独習中なんだぜ☆電王戦IIに出た棋士もコンピューターもみんな好きだぜ☆▲(パソコン将棋)WCSC29一次予選36位、SDT5予選42位▲(パソコン囲碁)AI竜星戦予選16位

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

むずでょ@きふわらべ第29回世界コンピューター将棋選手権一次予選36位 の最近の記事