できるの? どうやるの?
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch(9.0) 2018-06-27
- Mono 5.16.0
- MonoDevelop 7.6 build 711
- Eto.Forms 2.4.1 拡張機能, NuGetパッケージ
前回
- http://ytyaru.hatenablog.com/entry/2020/01/29/000000
- http://ytyaru.hatenablog.com/entry/2020/01/28/000000
- http://ytyaru.hatenablog.com/entry/2020/01/27/000000
- http://ytyaru.hatenablog.com/entry/2020/01/26/000000
- http://ytyaru.hatenablog.com/entry/2020/01/25/000000
- http://ytyaru.hatenablog.com/entry/2020/01/24/000000
手順
- プロジェクト作成
- TextBox追加
- 実行
1. プロジェクト作成
- メニュー→ファイル→新しいソリューション
  
- マルチプラットフォーム→- アプリ→- Eto Application 
- 名前などを適当に入力し、Xamlを選択する
  
  
- 場所を入力する
  
- プロジェクトが作成される
  
2. TextBox追加
- Xamlファイルを開く(MainForm.xeto)
- <TextBox x:Name="textBox1" Text="https://www.google.co.jp" Width="800" KeyDown="HandleInputUrl" />を追記する
- csファイルを開く(MainForm.xeto.cs)
- HandleInputUrlメソッドを追加する
ソースコード抜粋
MainForm.xeto
空っぽにする。データバインディングはC#でしか実装できないので、それに引きづられてすべてC#で実装することになる。XAMLとは一体……。MVVMとは一体……。全然M,V,Cが分離できてなくね?
<?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="800, 600" Padding="10"> <StackLayout> <!-- <TextBox x:Name="textBox1" Width="800" Text="{Binding Url}" /> <WebView Width="800" Height="600" Url="{Binding Url}" /> --> </StackLayout> </Form>
 {Binding Url}とすればバインディングできるようだが、今回は型が違うためか実行すると強制終了される。
- Text.Text: String型
- WebView.Url: Uri型
この型変換をする方法をどうやってXAMLで実装するのか。そもそもできるのか。公式にあるData-Bindingでは、一切XAMLが出てこなかった。仕方ないのでコードだけで書く。
UrlModel.cs
モデルクラス。前回はString型だったが、今回はWebView.Urlの型にあわせてUri型にした。
using System;
using System.ComponentModel; // INotifyPropertyChanged
using System.Runtime.CompilerServices; // CallerMemberName
namespace HelloXamlBinding
{
    public class UrlModel : INotifyPropertyChanged
    {
        //public string url;
        //public string Url { get { return url; } set { this.url = value; OnPropertyChanged(); } }
        Uri url;
        public Uri Url { get { return url; } set { this.url = value; OnPropertyChanged(); } }      
        void OnPropertyChanged([CallerMemberName] string memberName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(memberName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}
MainForm.xeto.cs
色々試してみたが、連動できなかった。モデルとTextBoxは連動したが、WebViewとは連動できず。どうやるのさ?
using System;
using System.Collections.Generic;
using Eto.Forms;
using Eto.Drawing;
using Eto.Serialization.Xaml;
namespace HelloXamlBinding
{
    public class MainForm : Form
    {
        public MainForm()
        {
            XamlReader.Load(this);
            Create();
            //var model = new UrlModel { Url = new Uri("https://www.google.co.jp") };
            //DataContext = model;
            //textBox1.TextBinding.BindDataContext(
            //    Binding.Property((MyModel m) => m.MyEnum)
            //    .Convert(r => r.ToString(), v => (MyEnum)Enum.Parse(typeof(MyEnum), v))
            //);
        }
        private void Create() {
            Width = 800;
            Height = 600;
            var textBox = new TextBox() { Width = 800 };
            var webView = new WebView() { Width = 800, Height = 600 };
            //textBox.TextBinding.BindDataContext((UrlModel m) => m.Url.ToString());
            Content = new StackLayout {
                Items = {textBox, webView}
            };
            var model = new UrlModel { Url = new Uri("https://www.google.co.jp") };
            textBox.TextBinding.BindDataContext(
                Binding.Property((UrlModel m) => m.Url)
                .Convert(r => r.ToString(), v => new Uri(v))
            );
            //webView.BindDataContext((UrlModel m) => m.Url, (UrlModel m) => m.Url);
            //webView.BindDataContext(
            //    Binding.Property((UrlModel m) => m.Url)
            //    .Convert(r => r.ToString(), v => new Uri(v))
            //);
            //textBox.TextBinding.BindDataContext(
            //    Binding.Property((UrlModel m) => m.Url)
            //    .Convert(r => r.ToString(), v => new Uri(v))
            //    //.Convert(r => r.ToString(), v => new Uri(v))
            //);
            //model.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) =>
            //{
            //   webView.Url = (e as UrlModel).Url;
            //};
            //webView.Bind(c => c.Enabled, model, m => m.Url);
            DataContext = model;
        }
    }
}
3. 実行
- Ctrl+F5で実行
- 怒られた
  
- ファイルパスを辿ってexeファイルを直接叩くと実行できた
  
 なお、値が変化したとき、URLでない文字列だと例外が発生して強制終了する。1文字でも変化すると発生してしまうので、一発で有効なURLを入力する必要がある。これを回避するためにはテキストボックスで全選択し、https://www.yahoo.co.jpなど有効なURLをペーストする。
所感
 WPFならIValueConverterとか使うみたいだけど、Eto.Formsには無いっぽい。公式によるとConvert()を使うっぽいが、どうやって今回の要件を実装するのかわからん。それっぽいコードは書いたつもりだけど、なんか間違ってる?
 あとWebViewのバインドってどうやるの? XAMLならUrl="{BInding Url}"とやれば書けるのだが、型変換を実装する方法がわからん。TextBoxにはTextBinding()があったので、WebViewにはUrlBinding()みたいのがあることを期待してたんだけど無い。Bind()はbool型じゃないとダメとか怒られるし。
もうEto.FormsでMVVMプログラミングできる気がしない。やりたいことをどうやるのか、情報が見つけられない。MVCでイベント駆動だったらやり方がすぐにわかるのに。