rksoftware

Visual Studio とか C# とかが好きです

まだまだ現役! Windows フォームアプリの新機能を確認する (7)

devblogs.microsoft.com

最近あまり面白い話題が少ない感じでしたがついに来ました。みんな大好き Windows フォームアプリのお話です。
↑ のブログ記事。見ていきましょう!

※ 現在 WPF でなく WinForms を愛用している日本の IT 技術者が WinForms アプリを MVVM で作りたいと思っているかは別として。

■ デザイナでバインディング設定

ついにこの時が来ました。デザイナでバインディングを設定していきましょう!
デザイナがなければ WinForms とは言えません。デザイナこそが WinForms かもしれません。
※ 私の想像の中の WinFormser の価値観です。

やってみましょう。

■ ViewModel のコード

こんな ViewModel でやっていきます。BindableBase クラス、Command クラスは省略します。以前の記事を見てください。

public class ViewModel2 : BindableBase
{
    private string _text;
    public string Text { get => _text; private set => SetProperty(ref _text, value); }
    public System.Windows.Input.ICommand ButtonCommand { get; init; }

    public ViewModel2()
    {
        ButtonCommand = new Command(((parameter) => Text += $"2:Button clicked: {parameter?.ToString()}\n"));
    }
}

■ デザイナでデータソースを追加

まずは、ボタンの Command プロパティへバインディングしようとします。
最初は選択肢が出てこないので、 [ オブジェクト データソースを追加します ] をクリック。

クラスの一覧が出てくるので、ViewModel のクラスを選択します。ここには public なクラスが並ぶようです。

選択して [ OK ] で何か生まれました。

<?xml version="1.0" encoding="utf-8"?>
<!--
    This file is automatically generated by Visual Studio. It is 
    used to store generic object data source configuration information.  
    Renaming the file extension or editing the content of this file may   
    cause the file to be unrecognizable by the program.
-->
<GenericObjectDataSource DisplayName="ViewModel2" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
  <TypeInfo>ViewModel2, WinFormsApp1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
</GenericObjectDataSource>

■ いよいよバインディング


変わりません!


別のプロパティでは出てくるので先の手順はあっていそうです。とりあえず困ったときにすること、そうリビルドです。リビルドしてみます。


でてきました。設定しています。

続けて、Text もバインドします。

■ CommandParameter

ボタンの CommandParameter にはリテラル値は設定できないようです。怒られが発生しました。

バインドにしてみます。

設定できました。

■ 実行

実行します。

ボタンを押しても何も起きません!

実は、一行だけコードを書く必要があるようです。

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
        viewModel2BindingSource.DataSource = new ViewModel2();
    }
}

この部分を追加しました。なるほど。

viewModel2BindingSource.DataSource = new ViewModel2();

確かに、実際のインスタンスの設定がデザイナ上でできているのか不安に思ったのですが、できないようですね。

■ もう一度、実行


計画通り! バインディングできました。

■ 生まれたコード

コードはこんな感じになっていました。

namespace WinFormsApp1
{
    partial class Form2
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.button1 = new System.Windows.Forms.Button();
            this.viewModel2BindingSource = new System.Windows.Forms.BindingSource(this.components);
            this.label1 = new System.Windows.Forms.Label();
            ((System.ComponentModel.ISupportInitialize)(this.viewModel2BindingSource)).BeginInit();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.DataBindings.Add(new System.Windows.Forms.Binding("Command", this.viewModel2BindingSource, "ButtonCommand", true));
            this.button1.DataBindings.Add(new System.Windows.Forms.Binding("CommandParameter", this.viewModel2BindingSource, "Text", true));
            this.button1.Location = new System.Drawing.Point(9, 14);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(94, 29);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            // 
            // viewModel2BindingSource
            // 
            this.viewModel2BindingSource.DataSource = typeof(ViewModel2);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.viewModel2BindingSource, "Text", true));
            this.label1.Location = new System.Drawing.Point(16, 60);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(50, 20);
            this.label1.TabIndex = 1;
            this.label1.Text = "label1";
            // 
            // Form2
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.button1);
            this.Name = "Form2";
            this.Text = "Form2";
            ((System.ComponentModel.ISupportInitialize)(this.viewModel2BindingSource)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private Button button1;
        private Label label1;
        private BindingSource viewModel2BindingSource;
    }
}

■ コードの要点

この部分ですね。

this.components = new System.ComponentModel.Container();

this.viewModel2BindingSource = new System.Windows.Forms.BindingSource(this.components);
this.button1.DataBindings.Add(new System.Windows.Forms.Binding("Command", this.viewModel2BindingSource, "ButtonCommand", true));
this.button1.DataBindings.Add(new System.Windows.Forms.Binding("CommandParameter", this.viewModel2BindingSource, "Text", true));
this.label1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.viewModel2BindingSource, "Text", true));
private BindingSource viewModel2BindingSource;

■ 気になる点

this.viewModel2BindingSource.DataSource = typeof(ViewModel2);

このコードはちょっと気になりますね。このコードあるのに、手で追加したコードは必要なのか、と。

Windows フォームアプリでも MVVM

これからは Windows フォームアプリでも心置きなく MVVM が使える時代です!
日本 1 億 2 千万人の WinFormser の皆さん! 安心してください。これからはなんでも全部 MVVM にしていきましょう!