やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Raspbian stretch MonoDevelop Eto.Forms XAML TextBox.Text と WebView.Url のデータ・バインディングができない

 できるの? どうやるの?

対象環境

前回

手順

  1. プロジェクト作成
  2. TextBox追加
  3. 実行

1. プロジェクト作成

  1. メニュー→ファイル新しいソリューション
    f:id:ytyaru:20181201194454p:plain
  2. マルチプラットフォームアプリEto Application f:id:ytyaru:20181202083840p:plain
  3. 名前などを適当に入力し、Xamlを選択する
    f:id:ytyaru:20181202083922p:plain
    f:id:ytyaru:20181202083950p:plain
  4. 場所を入力する
    f:id:ytyaru:20181201194509p:plain
  5. プロジェクトが作成される
    f:id:ytyaru:20181202084143p:plain

2. TextBox追加

  1. Xamlファイルを開く(MainForm.xeto)
  2. <TextBox x:Name="textBox1" Text="https://www.google.co.jp" Width="800" KeyDown="HandleInputUrl" />を追記する
  3. csファイルを開く(MainForm.xeto.cs)
  4. 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. 実行

  1. Ctrl+F5で実行
  2. 怒られた
    f:id:ytyaru:20181202084218p:plain
  3. ファイルパスを辿ってexeファイルを直接叩くと実行できた
    f:id:ytyaru:20181204080023p:plain

 なお、値が変化したとき、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でイベント駆動だったらやり方がすぐにわかるのに。