読者です 読者をやめる 読者になる 読者になる

Eto.FormsでXAMLとデータバインドを使う

C# Eto.Forms

Eto.Formsのテンプレートでは、GUI定義の方法として次の3つを用意しています。

ここでは、XAMLでデータバインドを使う例を示します。

まず、テンプレートに基づいてプロジェクトを作成します。もちろんGUI定義は「XAML」を選びます。

VMを作る

一応バインドしている感を出すため、VMを用意します。単なるサンプルなので、モデルも含みます。

といっても、使うのは、string型プロパティ1つだけです。

//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
using System.ComponentModel; // INotifyPropertyChanged
using System.Runtime.CompilerServices; // CallerMemberName

namespace EtoXamlSample1
{
    public class SampleViewModel : INotifyPropertyChanged
    {
        private string _message = "";
        public string Message
        {
            get { return _message; }
            set { TrySetProperty(ref _message, value); }
        }

        #region VMの基礎

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(
          [CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged == null) return;
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        protected bool TrySetProperty<T>(
          ref T storage, 
          T value, 
          [CallerMemberName] string propertyName = null)
        {
            if (object.Equals(storage, value)) return false;
            storage = value;
            RaisePropertyChanged(propertyName);
            return true;
        }

        #endregion
    }
}

VMの基礎」部分は、下記ページの「自作BindableBase」を使っています。

tech.d-itlab.co.jp

XAMLを書く

テンプレートの、StackLayoutの中身を書き換えて、2つのテキストボックスを作ります。

バインドできるように、Textプロパティには{Binding PropertyName}の書式で指定します。

ここでは、2つのボックスの中身が同期するように、同じプロパティにバインドしてみました。

<?xml version="1.0" encoding="UTF-8"?>
<Form
    xmlns="http://schema.picoe.ca/eto.forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="My Eto Form"
    ClientSize="400, 350"
    >
  <StackLayout>
    <Label>String 1</Label>
    <TextBox Text="{Binding Message}" />
    <Label>String 2</Label>
    <TextBox Text="{Binding Message}" />
  </StackLayout>

  <Form.Menu>
    <MenuBar>
      <ButtonMenuItem Text="F&amp;ile">
        <Command x:Name="clickCommand" MenuText="Click Me!" 
          ToolBarText="Click Me!" Executed="HandleClickMe" />
      </ButtonMenuItem>
      <MenuBar.ApplicationItems>
        <ButtonMenuItem Text="Preferences.." 
          Shortcut="{On Control+O, Mac=Application+Comma}" />
      </MenuBar.ApplicationItems>
      <MenuBar.QuitItem>
        <ButtonMenuItem Text="Quit!" 
          Shortcut="CommonModifier+Q" Click="HandleQuit" />
      </MenuBar.QuitItem>
    </MenuBar>
  </Form.Menu>
  <Form.ToolBar>
    <ToolBar>
      <x:Reference Name="clickCommand"/>
    </ToolBar>
  </Form.ToolBar>
</Form>

コードビハインドを書く

テンプレートに、VMを定義して、Form.DataContextにVMインスタンスを設定しました。

using System;
using System.Collections.Generic;
using Eto.Forms;
using Eto.Drawing;
using Eto.Serialization.Xaml;

namespace EtoXamlSample1
{
    public class MainForm : Form
    {
        SampleViewModel myModel;

        public MainForm()
        {
            XamlReader.Load(this);
            myModel = new SampleViewModel();
            DataContext = myModel;
        }

        protected void HandleClickMe(object sender, EventArgs e)
        {
            MessageBox.Show("I was clicked!");
        }

        protected void HandleQuit(object sender, EventArgs e)
        {
            Application.Instance.Quit();
        }
    }
}

動かす

ビルドして動かします。 片方のテキストボックスで文字を入力・編集すると、即座にもう片方にも反映されます。 IMEでの入力途中や変換途中も全て反映されています。

f:id:mokake:20170106000120p:plain

つまり、本家.NET(WPF,UWPなど)で言うところの「System.Windows.Data.UpdateSourceTrigger.PropertyChanged」でしょうか。

他の選択肢はないようなので、細かい制御が必要な場合は、色々とコードを書く必要がありそうです。