Quantcast
Channel: かずきのBlog@hatena
Viewing all 1388 articles
Browse latest View live

cv::calcOpticalFlowPyrLKを使うときにリンカエラーになる

$
0
0

libたくさんあって、どれをリンクすればいいのかわからないよぉ…ってなったのでメモ

opencv_videoをリンクすればよかった。 ということで以下の行を追加しました。

#pragma comment(lib, "opencv_video2410.lib")

opencvでオプティカルフローメモ。

$
0
0

明日使うのでメモ。

#include "cv.h"#include "highgui.h"#include <iostream>#include <vector>#ifdef _DEBUG#pragma comment(lib, "opencv_imgproc2410d.lib")#pragma comment(lib, "opencv_core2410d.lib")#pragma comment(lib, "opencv_highgui2410d.lib")#pragma comment(lib, "opencv_video2410d.lib")//Release Mode#else#pragma comment(lib, "opencv_imgproc2410.lib")#pragma comment(lib, "opencv_core2410.lib")#pragma comment(lib, "opencv_highgui2410.lib")#pragma comment(lib, "opencv_video2410.lib")#endifint main(int argc, char** argv)
{
    cv::VideoCapture cap(0);

    constint cycle = 1000;

    cv::Mat prevFrame;
    cv::Size frameSize = prevFrame.size();
    cap >> prevFrame;

    cv::waitKey(cycle);

    while (1) {
        cv::Mat frame;
        cap >> frame;

        cv::Mat prevFrameGray;
        cv::Mat currFrameGray;

        cv::cvtColor(prevFrame, prevFrameGray, CV_RGB2GRAY);
        cv::cvtColor(frame, currFrameGray, CV_RGB2GRAY);

        // 特徴点抽出
        std::vector<cv::Point2f> prevCorners;
        std::vector<cv::Point2f> currCorners;

        cv::goodFeaturesToTrack(prevFrameGray, prevCorners, 20, 0.05, 5.0);
        cv::goodFeaturesToTrack(currFrameGray, currCorners, 20, 0.05, 5.0);
        cv::cornerSubPix(prevFrameGray, prevCorners, cv::Size(21, 21), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 30, 0.01));
        cv::cornerSubPix(currFrameGray, currCorners, cv::Size(21, 21), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 30, 0.01));

        std::vector<uchar> featuresFound;
        std::vector<float> featuresErrors;

        cv::calcOpticalFlowPyrLK(
            prevFrameGray,
            currFrameGray,
            prevCorners,
            currCorners,
            featuresFound,
            featuresErrors);

        for (int i = 0; i < featuresFound.size(); i++) {
            cv::Point p1 = cv::Point((int) prevCorners[i].x, (int) prevCorners[i].y);
            cv::Point p2 = cv::Point((int) currCorners[i].x, (int) currCorners[i].y);
            cv::line(frame, p1, p2, cv::Scalar(0, 0, 255), 2);
        }

        cv::imshow("preview", frame);
        prevFrame = frame;
        if (cv::waitKey(cycle) == 27) { break; }
    }
    return0;
}

Xamarin.Android + ReactivePropertyでListViewを使う

$
0
0

特にReactivePropertyでサポートはしてないので自前でやるっきゃないです!ということでこういうクラスを書いてみました。

ReadOnlyReactiveCollection型をIListAdapterに変換するコードです。

publicstaticclass ReadOnlyCollectionExtensions
{
    /// <summary>/// ReadOnlyReactiveCollectionをIListAdapterに変換する/// </summary>/// <typeparam name="T"></typeparam>/// <paramname="self"></param>/// <paramname="createRowView">行のデータを表示するためのViewを作る処理</param>/// <paramname="setRowData">行にデータを設定する処理</param>/// <returns></returns>publicstatic IListAdapter ToAdapter<T>(this ReadOnlyReactiveCollection<T> self, 
        Func<View> createRowView,
        Action<T, View> setRowData)
    {
        returnnew ReadOnlyReactiveCollectionAdapter<T>(self, createRowView, setRowData);
    }
}

/// <summary>/// ReadOnlyReactiveCollection用のAdapterクラス/// </summary>/// <typeparam name="T"></typeparam>class ReadOnlyReactiveCollectionAdapter<T> : BaseAdapter<T>
{
    // もとになるコレクションprivate ReadOnlyReactiveCollection<T> source;
    // 行のデータを表示するためのViewを作る処理private Func<View> createRowView;
    // 行にデータを設定する処理private Action<T, View> setRowData;

    public ReadOnlyReactiveCollectionAdapter(
        ReadOnlyReactiveCollection<T> source,
        Func<View> createRowView,
        Action<T, View> setRowData)
    {
        this.source = source;
        this.createRowView = createRowView;
        this.setRowData = setRowData;
    }

    publicoverride T this[int position]
    {
        get { return source[position]; }
    }

    publicoverrideint Count
    {
        get { returnthis.source.Count; }
    }

    publicoverridelong GetItemId(int position)
    {
        return position;
    }

    publicoverride View GetView(int position, View convertView, ViewGroup parent)
    {
        if (convertView == null)
        {
            convertView = this.createRowView();
        }
        this.setRowData(this[position], convertView);
        return convertView;
    }
}

使い方は簡単です。以下のようなコマンドを実行するとコレクションにデータを追加するだけのViewModelがあったとします。

publicclass MainActivityViewModel
{
    private ObservableCollection<string> source = new ObservableCollection<string> { "a", "b", "c" };
    public ReadOnlyReactiveCollection<string> Items { get; private set; }

    public ReactiveCommand AddItemCommand { get; private set; }

    public MainActivityViewModel()
    {
        this.Items = source.ToReadOnlyReactiveCollection();

        this.AddItemCommand = new ReactiveCommand();
        this.AddItemCommand.Subscribe(_ =>
        {
            this.source.Add("item" + DateTime.Now);
        });
    }
}

そして、ボタンとListViewを置いた画面でさくっと紐づけ。

[Activity(Label = "App1", MainLauncher = true, Icon = "@drawable/icon")]
publicclass MainActivity : Activity
{
    private MainActivityViewModel viewModel = new MainActivityViewModel();

    protectedoverridevoid OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);

        this.FindViewById<Button>(Resource.Id.button1).Click += viewModel.AddItemCommand.ToEventHandler();

        var listView = this.FindViewById<ListView>(Resource.Id.listView1);
        listView.Adapter = viewModel.Items.ToAdapter(
            () => LayoutInflater.FromContext(this).Inflate(Resource.Layout.layout1, null),
            (x, v) => v.FindViewById<TextView>(Resource.Id.textView1).Text = x);
        this.viewModel.Items.CollectionChangedAsObservable().Subscribe(_ => listView.InvalidateViews());
    }
}

一応レイアウトファイルも

main.axml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_height="fill_parent"><Buttonandroid:text="Button"android:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/button1" /><ListViewandroid:minWidth="25px"android:minHeight="25px"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/listView1" /></LinearLayout>

layout1.axml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal"android:layout_width="fill_parent"android:layout_height="fill_parent"><ImageViewandroid:src="@android:drawable/ic_menu_gallery"android:layout_width="wrap_content"android:layout_height="match_parent"android:id="@+id/imageView1" /><TextViewandroid:text="Medium Text"android:textAppearance="?android:attr/textAppearanceMedium"android:layout_width="wrap_content"android:layout_height="match_parent"android:id="@+id/textView1" /></LinearLayout>

Xamarin AndroidでIListやObservableCollectionをListViewに表示する

$
0
0

先日以下のような記事を書きました。

Xamarin.Android + ReactivePropertyでListViewを使う - かずきのBlog@hatena

別にReactivePropertyのコレクションじゃなくて普通のコレクションを対象にしてかけるじゃないということで、仕立て直してnugetに放流しました。毎回作りそうなコードだったので。

NuGet Gallery | Runceel.XamarinAndroid 1.0.0

使い方

IList>T<とObservableCollection>T<とReadOnlyReactiveCollection>T<にToAdapterという拡張メソッドを定義しています。ObservableCollectionとReadOnlyObservableCollectionから生成されるAdapterはコレクションに変更があったときに自動でNotifyDataSetChangedが呼び出されます。

ToAdapter拡張メソッドの引数は以下のような感じになってます。

  • createRowView: positionと要素から行を表示するためのViewを生成する処理
  • setRowData: positionと要素とViewが渡されるのでViewに値を設定する処理
  • getId: psoitionと要素からidを返す処理(省略した場合はpositionがidになります)

こんな感じで使います。

this.list = new ObservableCollection<string>(); 
this.ListAdapter = this.list.ToAdapter( 
    (_, __) => LayoutInflater.FromContext(this).Inflate(Android.Resource.Layout.SimpleListItem1, null), 
    (_, x, v) => v.FindViewById<TextView>(Android.Resource.Id.Text1).Text = x);

ReactiveProperty v1.1.2をリリースしました

$
0
0

人生初のGitHubでのPullRequestが来たので取り込みました。まぁ、これはコードとかプロジェクトの依存関係がクリーンになったので大歓迎でした。

本命は、以下の通りです。

  • XamarinのPCLに追加できるようにしました。

今までは、Xamarin.FormsでPCL作った時にデフォルトのプロファイルではReactivePropertyが追加できませんでした。プロファイルを変更することで対応されてました。

Xamarin.Forms と ReactiveProperty で快適MVVM生活 - Qiita

これを、プロファイルを変更しなくても、さくっと追加できるようにしました。なにをしたってnugetのパッケージの定義をきちんとしてなかったのが原因でした…。

これでXamarin.Formsでも簡単にReactivePropertyが使えるようになります。

コレクション初期化子に渡すオブジェクトをデリゲートを使って組み立てたい

$
0
0

よくわからないタイトルになってしまいましたが、Xamarin.Forms使おうとして出てきた問題です。

Xamarin.Formsは、現時点ではXAMLのデザイナとか提供されてないので、長い目で見るとXAMLで書いておいたほうがいいのは確かなんですが、勉強するためにインテリセンスがないのはちょいと辛い。なので、勉強するときはC#で軽く組んでおいて感覚をつかんでから、デザイナが出てきたらXAMLに乗り換えるといったことを目論んでいます。

そんなときに、Gridにボタンを置いたり、Bindingを設定したオブジェクトを配置したりといったことをしようとすると、以下のように一度変数に入れて書いたりしてました。めんどい。

// 初期化と
Button b = new Button { Text = "Hello" };
Grid.SetRow(b, 1);
Grid.SetColumn(b, 1);
b.SetBinding(Button.CommandProperty, "Hoge");

Content = new Grid
{
    RowDefinitions = { new RowDefinition { Height = GridLength.Auto }, ... },
    Children = 
    {
        b // 配置が分離してしまうよぉぉぉ
    }
};

変数ができてしまうのが気に食わなかったり、めんどくさいと思ってました。

デリゲート使って解決?

こんな風にかけることに気付きました。

Content = new Grid
{
    RowDefinitions = { ...省略... },
    Children =
    {
        new Func<Button>(() =>
        {
            // 配置と初期化処理が同じところにかけるし無駄に変数が増えない!
            var b = new Button { Text = "Hello" };
            Grid.SetRow(b, 1);
            Grid.SetColumn(b, 1);
            b.SetBinding(Button.CommandProperty, "Hoge");
            return b;
        }).Invoke()
    }
};

よりXAMLに近いイメージで書けそう。

ReactivePropertyの便利なINotifyPropertyChangedとINotifyCollectionChangedを監視する拡張メソッド

$
0
0

ReactivePropertyは、XAMLで使うのがしっくりきますが、Codeplex.Reactive.Extensions名前空間には、それ以外にも使える便利なメソッドが詰まってます。その中でも汎用的なINotifyPropertyChangedとINotifyCollectionChangedを監視するメソッドを紹介します。

INotifyPropertyChangedの拡張メソッド

ObservePropertyという拡張メソッドがあります。これはExpressionTreeで監視対象のプロパティを指定することで、そのプロパティを監視するIObservable>T<を作成できます。

例を以下に示します。

このようなPersonクラスがあるとして…

publicclass Person : INotifyPropertyChanged
{
    publicevent PropertyChangedEventHandler PropertyChanged;
    privatevoid OnPropertyChanged([CallerMemberName]string propertyName = null)
    {
        var h = this.PropertyChanged;
        if (h != null) { h(this, new PropertyChangedEventArgs(propertyName)); }
    }

    privatestring name;

    publicstring Name
    {
        get { returnthis.name; }
        set
        {
            this.name = value;
            this.OnPropertyChanged();
        }
    }
}

Nameプロパティを監視するコードはこうなります。

var p = new Person();
p.ObserveProperty(x => x.Name)
    .Subscribe(x => Console.WriteLine("変更されました {0}", x));

p.Name = "tanaka";
p.Name = "kimura";

実行すると以下のようになります。

変更されました
変更されました tanaka
変更されました kimura

最初のSubscribeした時点での値がいらなかったらSkip(1)すればいいです。

INotifyCollectionChangedの拡張メソッド

INotifyCollectionChangedのを監視するのは、CollectionChangedAsObservable拡張メソッドです。

これは、CollectionChangedイベントが発行されるたびに値を通知するIObservable>NotifyCollectionChangedEventArgs<を返します。使い方は以下のようになります。

var c = new ObservableCollection<Person>();

c.CollectionChangedAsObservable()
    .Subscribe(x => Console.WriteLine("コレクションに変更がありました"));

c.Add(new Person { Name = "tanaka" });
c.Add(new Person { Name = "kimura" });

実行するとこんな結果になります。

コレクションに変更がありました
コレクションに変更がありました

ObservableCollection>T<限定になりますが、タイプセーフな監視メソッドも用意されています。

ObserveXXXXChangedメソッドでXXXXにはAddやRemoveやReplaceやMoveやResetなどがあります。これは、NotifyCollectionChangedEventArgsを用途ごとに使いやすいように加工したものになっています。

まとめ

ReactiveProperty全部つかうのが重たかったら、該当メソッドだけ切り出して使うのもありかもしれないですね。OSSですし。

Xamarin.FormsでPrism.Mvvmを使う

$
0
0

Prism.Mvvmは非常にシンプルなMVVMをサポートするライブラリです。Xamarin.Formsでも使わない手はない!ということで使ってみました。

環境設定

Windows Phoneのプロジェクトを消します。日本で出てないし、Prism.Mvvmサポートしてないプラットフォームなので泣く泣く…。この時点で私はAndroidしか試せないのでiOSも消してしまいました。

PCLのプロパティを開いてWindows Phoneのチェックを外しておきます。

NuGetでの導入

Prism.Mvvmで検索してPCLとAndroidのプロジェクト両方にPrism.Mvvmを追加します。

Viewの作成

Viewsという名前のフォルダを作ってForms Xaml Pageを作成します。

Prism.MvvmのVとVMの紐づけ機能を使うには、ViewがIViewインターフェースを実装してないといけないので、これを実装します。DataContextというプロパティが必要になるのでBindingContextをラップする形で実装します。ついでに、コンストラクタにViewModelLocationProviderのAutoWireViewModelChangedを呼び出しておいて、紐づけ機能を有効にしておきます。

using Microsoft.Practices.Prism.Mvvm;
using Xamarin.Forms;

namespace App8.Views
{
    publicpartialclass MainPage : ContentPage, IView
    {
        public MainPage()
        {
            InitializeComponent();
            ViewModelLocationProvider.AutoWireViewModelChanged(this);
        }

        publicobject DataContext
        {
            get
            {
                returnthis.BindingContext;
            }
            set
            {
                this.BindingContext = value;
            }
        }
    }
}

ViewModelの作成

ViewModelsフォルダを作って、そこにViewのクラス名+ViewModelという命名規約でViewModelを作成します。この例ではMainPageViewModelですね。とりあえず、適当に。

using Microsoft.Practices.Prism.Mvvm;
using System.Windows.Input;
using Xamarin.Forms;

namespace App8.ViewModels
{
    publicclass MainPageViewModel : BindableBase
    {
        privatestring mainText;

        publicstring MainText
        {
            get { returnthis.mainText; }
            set { this.SetProperty(refthis.mainText, value); }
        }

        public ICommand HelloCommand { get; private set; }

        public MainPageViewModel()
        {
            this.MainText = "Hello world";

            this.HelloCommand = new Command(() => this.MainText = "こんにちは世界");
        }
    }
}

XAMLの作成

さて、Viewに戻ってViewModelに紐づくXAMLを書いていきます。といっても、デフォルトでMainTextにバインドされてるLabelが用意されてるので、ここではStackLayoutとButtonを追加しただけです。Xamarin Studioだと若干のインテリセンスが効くので精神衛生にいいですが、Visual Studioで書くと発狂しそうになります。ここらへん、そのうち出てくるであろうデザイナに期待したいところですね。

<?xml version="1.0" encoding="utf-8"?><ContentPage xmlns="http://xamarin.com/schemas/2014/forms"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"x:Class="App8.Views.MainPage"><StackLayout><Label Text="{Binding MainText}"VerticalOptions="Center"HorizontalOptions="Center" /><Button Text="Hello"Command="{Binding HelloCommand}" /></StackLayout></ContentPage>

MainPageの設定

App.csのMainPageを、先ほど作成したMainPageクラスに差し替えます。

public App()
{
    // The root page of your application
    MainPage = new MainPage();
}

実行

実行すると以下のようになります。

f:id:okazuki:20150210214703p:plain

f:id:okazuki:20150210214727p:plain

Unity(DIコンテナのほう)との連携

Prism.MvvmのVとVMの連携は、UnityによるDIと連携するとうれしいと個人的に思ってます。UnityをプロジェクトにNuGetから追加します。

ついでに、Prism.PubSubEventsも追加しておきます。Prism.PubSubEventsのクラスをVMにDIしてみたいと思います。

App.csのコンストラクタでUnityの初期化とViewModelのインスタンス化の処理の書き換えを行います。

public App()
{
    var c = new UnityContainer();
    // EventAggregatorをシングルトンで管理してもらう
    c.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
    // ViewModelをUnityから取得するようにする
    ViewModelLocationProvider.SetDefaultViewModelFactory(t => c.Resolve(t));

    MainPage = new MainPage();
}

ViewModelのコンストラクタなどでIEventAggregatorを受け取るようにするとUnityが勝手にインスタンスを設定してくれます。

public MainPageViewModel(IEventAggregator eventAggregator)

Modelのルート要素をDIしてもらったり、設定情報を保持するクラスをDIしてもらったりとか色々便利です。オブジェクトのインスタンスの管理をお任せできるので個人的に気に入ってます。

まとめ

Prism.Mvvm + UnityはXamarin.Formsでも使える(ただしWindows Phoneは除く)


Xamarin.FormsでPrism.MvvmとReactivePropertyを使ったサンプル

$
0
0

ということで、コードレシピにアップしました。疲れた疲れた。

Xamarin.FormsでPrism.MvvmとReactivePropertyを使ったサンプル in C# for Visual Studio 2013

Xamarin.Formsにインテリセンスがきた!!

$
0
0

ソファーでうたたねしてて目覚めたらこんな記事が!

【速報】Visual Studio でも Xamarin.Forms の XAML で IntelliSence が使えるように!(3rd Party の拡張機能ですが) - Xamarin 日本語情報

これはきた!!!新規作成したプロジェクトで試したらインテリセンスが効いたけど、既存プロジェクトでは効かなかったから、寝て起きたら、そこらへん調査してみよう。寝て起きたら既存プロジェクトでも効くようになってないかなぁ。

Xamarin.FormsのNavigationPageのアイコンの変え方が知りたい

$
0
0

続き書きました

Xamarin.FormsのNavigationPageのアイコンの変え方(Android) - かずきのBlog@hatena

本文

デフォルトのXamarinのアイコンじゃなくて独自アイコンにしようと思ってNavigationPage内のページにNavigationPage.TitleIcon添付プロパティをセットしたんです。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage 
  xmlns="http://xamarin.com/schemas/2014/forms"  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  xmlns:viewModels="clr-namespace:App10.ViewModels;assembly=App10"  x:Class="App10.Views.MainPage"
  NavigationPage.TitleIcon="bird.png">
  <StackLayout>
    <Label Text="okokokokook" />
  </StackLayout>
</ContentPage>

Androidで実行するとアイコンが左じゃなくて真ん中の変な位置に…。

f:id:okazuki:20150211135432p:plain

左にこないのはバグなのか、やりかたが間違ってるのか悩む…。

Xamarin.FormsのNavigationPageのアイコンの変え方(Android)

$
0
0

以前に、こんな記事を書きました。

Xamarin.FormsのNavigationPageのアイコンの変え方が知りたい - かずきのBlog@hatena

この記事では、アイコンの表示位置がおかしくなるということを言ってたのですが、私が用意したアイコンが大きかったのが原因でしたorz

72x72のサイズのアイコンを用意するとちゃんとなりました。ということで改めて。

アイコンを用意する

Resources/drawableにデフォルトで用意されてるIcon.pngと同じサイズの画像を用意します。ここではbird.pngという画像を72x72ピクセルで用意しました。ビルドアクションは、AndroidResourceにします。

NavigationPageを使うようにする

App.csを書き換えてNavigationPageを使うようにします。

今回用意したアイコンが黒で絵が描いてあるものだったのでバーの色を緑にしてみました。

public App()
{
    // The root page of your application
    MainPage = new NavigationPage(new MainPage())
    {
        BarBackgroundColor = Color.Green,
    };
}
BarTextColorプロパティが効かない気がするのですが、また別の話。

MainPageを用意する

XAMLで(XAMLじゃなくてもいいんですが)MainPageを用意します。MainPageにNavigationPage.TitleIcon添付プロパティ(添付プロパティって呼び名あってるのかな)を設定します。先ほど用意したbird.pngを設定しておきます。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage 
  xmlns="http://xamarin.com/schemas/2014/forms"  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  xmlns:viewModels="clr-namespace:App10.ViewModels;assembly=App10"  x:Class="App10.Views.MainPage"
  NavigationPage.TitleIcon="bird.png"
  Title="こんにちは">
  <ContentPage.ToolbarItems>
    <ToolbarItem Text="Hello world" />
  </ContentPage.ToolbarItems>
  <StackLayout>
    <Label Text="okokokokook" />
    <Button Text="Hello" Clicked="Button_Clicked" />
  </StackLayout>
</ContentPage>```

画面の中身は適当で重要なのはTitleIconをセットしてるところです。

実行して動作確認

実行すると以下のように鳥のアイコンがきちんと表示されます。めでたしめでたし。

f:id:okazuki:20150211204602p:plain

Xamarin.Androidでテーマを変更する

$
0
0

assemblyにApplicationAttributeをつけて、Themeプロパティを設定するらしい。

Lolipopのマテリアルの白にしたいなら、こんな感じ。

[assembly: Application(Icon = "@drawable/bird", Theme = "@android:style/Theme.Material.Light")]

ここらへんのテーマの一覧ってどこにあるんだろう…。軽く探した感じだとわからなかったorz

Xamarin.FormsでMVVM Light ToolkitとReactivePropertyを使ってみた

$
0
0

Prism.Mvvmのほうが好みなんですが、こいつがSilverlight for Windows Phone 8をサポートしないので、SL for WP8もサポートしてる(すごいよね…)MVVM Light Toolkitを試してみました。

ViewModelLocatorの作成

SimpleIoCというDIコンテナがついてるけど、個人的にUnity推しなので、Unity使うようにしてViewModelLocatorを作りました。こんな感じで。

using App13.ViewModels;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Unity;

namespace App13
{
    publicstaticclass ViewModelLocator
    {
        static ViewModelLocator()
        {
            var c = new UnityContainer();
            ServiceLocator.SetLocatorProvider(new ServiceLocatorProvider(() => new UnityServiceLocator(c)));
        }

        publicstatic MainViewModel Main
        {
            get { return ServiceLocator.Current.GetInstance<MainViewModel>(); }
        }
    }
}

ViewModelの作成

コマンド押したらダイアログが出るイメージです。

using Codeplex.Reactive;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;
using System;

namespace App13.ViewModels
{
    publicclass MainViewModel : ViewModelBase
    {
        public ReactiveProperty<string> Label { get; private set; }

        public ReactiveCommand AlertCommand { get; private set; }

        public MainViewModel()
        {
            this.Label = new ReactiveProperty<string>("Hello world");

            this.AlertCommand = new ReactiveCommand();
            this.AlertCommand.Subscribe(_ => this.MessengerInstance.Send(new NotificationMessage("こんにちは!")));
        }
    }
}

ダイアログ表示用ビヘイビアの作成

これでいいのか自信がないけどつくってみました。

using GalaSoft.MvvmLight.Messaging;
using Xamarin.Forms;

namespace App13.Services
{
    publicclass DialogBehavior : Behavior<Page>
    {
        private Page page;
        protectedoverridevoid OnAttachedTo(Page bindable)
        {
            base.OnAttachedTo(bindable);
            this.page = bindable;
            Messenger.Default.Register<NotificationMessage>(this, this.Alert);
        }

        publicvoid Alert(NotificationMessage message)
        {
            this.page.DisplayAlert("メッセージ", message.Notification, "OK");
        }

        protectedoverridevoid OnDetachingFrom(Page bindable)
        {
            base.OnDetachingFrom(bindable);
            Messenger.Default.Unregister<NotificationMessage>(this);
        }
    }
}

Viewの作成

XAMLでさくっとね。

<?xml version="1.0" encoding="utf-8"?><ContentPage xmlns="http://xamarin.com/schemas/2014/forms"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:local="clr-namespace:App13;assembly=App13"xmlns:services="clr-namespace:App13.Services;assembly=App13"x:Class="App13.Views.MainPage"BindingContext="{x:Static local:ViewModelLocator.Main}"><ContentPage.Padding><OnPlatform x:TypeArguments="Thickness"iOS="0, 20, 0, 0" /></ContentPage.Padding><ContentPage.Behaviors><services:DialogBehavior /></ContentPage.Behaviors><StackLayout><Label Text="{Binding Label.Value}" /><Button Text="こんにちは"Command="{Binding AlertCommand}" /></StackLayout></ContentPage>

App.csの書き換え

App.csのMainPageの設定を上で作ったページに書き換えます。これは忘れがち。

using App13.Views;
using Xamarin.Forms;

namespace App13
{
    publicclass App : Application
    {
        public App()
        {
            // The root page of your application
            MainPage = new MainPage();
        }

        protectedoverridevoid OnStart()
        {
            // Handle when your app starts
        }

        protectedoverridevoid OnSleep()
        {
            // Handle when your app sleeps
        }

        protectedoverridevoid OnResume()
        {
            // Handle when your app resumes
        }
    }
}

実行して動作確認

実行してボタンを押すとダイアログが出た!

f:id:okazuki:20150211233002p:plain

ソースコード

runceel/Xamarin.Forms-MVVMLight-RxProp-HelloWorld · GitHub

Xamarin.AndroidでReactivePropertyを使いやすくするライブラリを試作してみた

$
0
0

とりあえず。

以下のようなVMがあったとして

using Codeplex.Reactive;
using System;
using System.Reactive.Linq;

namespace App15
{
    publicclass MainPageViewModel
    {
        public ReactiveProperty<string> Input { get; private set; }
        public ReactiveProperty<string> Output { get; private set; }

        public ReactiveCommand ClearCommand { get; private set; }

        public MainPageViewModel()
        {
            this.Input = new ReactiveProperty<string>();

            this.Output = Observable.Merge(
                this.Input.Where(x => string.IsNullOrEmpty(x)).Select(_ => ""),
                this.Input.Where(x => !string.IsNullOrEmpty(x)).Select(x => x.ToUpper()))
                .ToReactiveProperty();

            this.ClearCommand = new ReactiveCommand();
            this.ClearCommand.Subscribe(_ => this.Input.Value = "");
        }
    }
}

こんな感じでActivityとつなぐことができます。

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Codeplex.Reactive.Extensions;
using ReactiveProperty.XamarinAndroid;
using ReactiveProperty.XamarinAndroid.Extensions;

namespace App15
{
    [Activity(Label = "App15", MainLauncher = true, Icon = "@drawable/icon")]
    publicclass MainActivity : Activity
    {
        privatereadonly MainPageViewModel viewModel = new MainPageViewModel();

        protectedoverridevoid OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // TwoWay bindingthis.SetBinding(
                // 対象のID
                Resource.Id.EditTextInput,
                // 対象のプロパティ
                (EditText x) => x.Text,
                // ソースのReactivePropertythis.viewModel.Input,
                // 更新タイミングのIO<Unit>
                (EditText x) => x.TextChangedAsObservable().ToUnit());

            // OneWay bindingthis.SetBinding(
                // 対象のID
                Resource.Id.TextViewOutput,
                // 対象のプロパティ
                (TextView x) => x.Text,
                // ソースのReactivePropertythis.viewModel.Output);

            // Command bindingthis.FindViewById<Button>(Resource.Id.ButtonClear)
                .ClickAsObservable()
                // IO<T>の発火でコマンドをキックする
                .SetCommand(this.viewModel.ClearCommand);
        }
    }
}

コレクション系は、前につくったライブラリのソースをごそっと持ってきたので、各種コレクションに対してToAdapterでIListAdapterがゲットできます。

Xamarin AndroidでIListやObservableCollectionをListViewに表示する - かずきのBlog@hatena

主な機能

  • コレクションをIListAdapterに変換するToAdapter拡張メソッド。
  • SetBindingメソッドによる単方向・双方向データバインド
  • SetCommandによるIObservableとReactiveCommandの接続
  • Viewを継承したクラスほぼ全てのイベントに対してEventNameAsObservableの拡張メソッド

です。さくっと作った割には少し気に入ってるので手を入れていってみようかな。

導入

NuGetに放流してあります。

NuGet Gallery | ReactiveProperty.XamarinAndroid 0.0.2


Xamarin.AndroidでReactivePropertyを楽に使うためのReactiveProperty.XamarinAndroidのサンプルをコードレシピに公開しました。

$
0
0

A simple master detail application with ReactiveProperty. in C# for Visual Studio 2013

これくらいでいけるなら、個人的には許容範囲かもってお思えるものができたので満足満足。

MVVMでめんどくさいと思ってる部分を、個人的にどうやって緩和してるか

$
0
0

MVVMのめんどくさいと感じてるところ

ModelとViewModelのクラスのマッピング

MVVMでアプリ組んでるとModelとViewModelで似た構造のクラスを作って、値の移し替えを行うことがあります。AutoMapperとか使ってもいいのですが、ReactivePropertyを使うことでも楽をすることができます。

以下のようなModelクラスがあるとします。(BindableBaseクラスはPrismのINotifyPropertyChangedを実装したクラスです)

publicclass Person : BindableBase
{
    privatestring name;

    publicstring Name
    {
        get { returnthis.name; }
        set { this.SetProperty(refthis.name, value); }
    }

    privateint age;

    publicint Age
    {
        get { returnthis.age; }
        set { this.SetProperty(refthis.age, value); }
    }

}

上記のクラスは2プロパティしかないですが、こんな感じでINotifyPropertyChangedの変更通知に対応したプロパティをたくさんもっているクラスがModelの中にはあります。

ModelからViewModelへの値の移し替えは、Codeplex.Reactive.Extensions名前空間に定義されているObserveProperty拡張メソッドを使うことで簡単に定義できます。

publicclass PersonViewModel
{
    public ReactiveProperty<string> Name { get; private set; }

    public ReactiveProperty<string> Age { get; private set; }

    public PersonViewModel(Person model)
    {
        this.Name = model
            .ObserveProperty(x => x.Name) // IObservable<string>に変換
            .ToReactiveProperty();        // ReactiveProperty<string>に変換this.Age = model
            .ObserveProperty(x => x.Age) // IObservable<int>に変換
            .Select(x => x.ToString())   // IObservable<string>に変換
            .ToReactiveProperty();       // ReactiveProperty<string>に変換
    }
}

これで、Personクラスのプロパティが書き換わると、ViewModelのプロパティが書き換わる処理が定義できます。

var p = new Person { Name = "tanaka" };
var vm = new PersonViewModel(p);

Console.WriteLine(vm.Name.Value); // tanaka

p.Name = "kimura";
Console.WriteLine(vm.Name.Value); // kimura

p.Age = 10;
Console.WriteLine(vm.Age.Value); // 10

Model → ViewModel間の一方向データバインディングと見ることもできます。

ModelとViewModelの双方向バインディング

単純なケースでは、MとVM間のプロパティを双方向でバインドするToReactivePropertyAsSynchronizedメソッドを提供しています。

publicclass PersonViewModel
{
    public ReactiveProperty<string> Name { get; private set; }

    public ReactiveProperty<string> Age { get; private set; }

    public PersonViewModel(Person model)
    {
        this.Name = model
            .ToReactivePropertyAsSynchronized(x => x.Name); // ReactiveProperty<string>に変換this.Age = model
            .ToReactivePropertyAsSynchronized(
                x => x.Age,
                convert: x => x.ToString(),   // M -> VMの変換処理                convertBack: x =>             // VM -> Mの変換処理
                {
                    try
                    {
                        returnint.Parse(x);
                    }
                    catch
                    {
                        return -1; // error
                    }
                });
    }
}
var p = new Person { Name = "tanaka", Age = 10 };
var vm = new PersonViewModel(p);

Console.WriteLine("{0} {1}", vm.Name.Value, vm.Age.Value); // tanaka 10

vm.Name.Value = "kimura";
vm.Age.Value = "30";
Console.WriteLine("{0} {1}", p.Name, p.Age); // kimura 30

vm.Age.Value = "xxx"; // error!!
Console.WriteLine("{0} {1}", p.Name, p.Age); // kimura -1

バリデーションとの連携

ToReactivePropertyAsSynchronized拡張メソッドは、シンプルな双方向のバインディングをサポートしていますが、エラーがあったらModelのデータを書き換えたくないという要望には対応していません。そこは、少しめんどくさいですが、自前で対応するしか無いです。

まず、順を追ってReactivePropertyの値の検証から説明します。ReactivePropertyのSetValidateNotifyErrorメソッドでReactivePropertyの値の検証が出来ます。(そのほかにも色々提供していますが一番単純な奴で)エラーがある場合はエラーメッセージを返して、エラーがない場合はnullを返すようにします。

publicclass PersonViewModel
{
    public ReactiveProperty<string> Name { get; private set; }

    public ReactiveProperty<string> Age { get; private set; }

    public PersonViewModel(Person model)
    {
        this.Name = model
            .ObserveProperty(x => x.Name)                                    // IObservable<string>に変換して
            .ToReactiveProperty()                                            // ReactiveProperty<string>に変換して
            .SetValidateNotifyError(x =>                                     // 空文字の時はエラーにするstring.IsNullOrWhiteSpace(x) ? "Name is required" : null);

        this.Age = model
            .ObserveProperty(x => x.Age)                                     // IObservable<int>に変換して
            .Select(x => x.ToString())                                       // IObservable<string>に変換して
            .ToReactiveProperty()                                            // ReactiveProperty<string>に変換して
            .SetValidateNotifyError(x =>                                     // int型に変換できない場合はエラーにする
            {
                int result; // no usereturnint.TryParse(x, out result) ? null : "Error";
            });
    }
}

プロパティのエラーの有無は、HasErrorsプロパティで確認できます。

var p = new Person { Name = "tanaka", Age = 10 };
var vm = new PersonViewModel(p);

Console.WriteLine(vm.Name.HasErrors); // False
vm.Name.Value = ""; // Error!
Console.WriteLine(vm.Name.HasErrors); // True

Console.WriteLine(vm.Age.HasErrors); // False
vm.Age.Value = "xxx"; // Error!
Console.WriteLine(vm.Age.HasErrors); // True

エラーメッセージは、WPFの場合はValidation.Errors添付プロパティのErrorContentで取得できるのでXAMLで完結できます。WPF以外のプラットフォームでは、特に検証エラーのサポートが無いので多少めんどくさい手順を踏むことにになります。手順は別記事に譲ります。

VMのプロパティにエラーが無いとき、つまりReactivePropertyのHasErrorsプロパティがFalseの時に、VM → Mに値を移せばいいということになります。

publicclass PersonViewModel
{
    public ReactiveProperty<string> Name { get; private set; }

    public ReactiveProperty<string> Age { get; private set; }

    public PersonViewModel(Person model)
    {
        this.Name = model                                                    // M -> VM
            .ObserveProperty(x => x.Name)                                    // IObservable<string>に変換して
            .ToReactiveProperty()                                            // ReactiveProperty<string>に変換して
            .SetValidateNotifyError(x =>                                     // 空文字の時はエラーにするstring.IsNullOrWhiteSpace(x) ? "Name is required" : null);
        this.Name                                                            // VM -> M
            .Where(_ => !this.Name.HasErrors)                                // エラーが無いときは
            .Subscribe(x => model.Name = x);                                 // 値を書き戻すthis.Age = model                                                     // M -> VM
            .ObserveProperty(x => x.Age)                                     // IObservable<int>に変換して
            .Select(x => x.ToString())                                       // IObservable<string>に変換して
            .ToReactiveProperty()                                            // ReactiveProperty<string>に変換して
            .SetValidateNotifyError(x =>                                     // int型に変換できない場合はエラーにする
            {
                int result; // no usereturnint.TryParse(x, out result) ? null : "";
            });
        this.Age                                                             // VM -> M
            .Where(_ => !this.Age.HasErrors)                                 // エラーが無いときは
            .Select(x => int.Parse(x))                                       // int型に変換して
            .Subscribe(x => model.Age = x);                                  // 書き戻す
    } 
}

これで、バリデーションエラーのないときだけVM → Mへ値を書き戻す処理のあるViewModelが出来ました。

var p = new Person { Name = "tanaka", Age = 10 };
var vm = new PersonViewModel(p);

Console.WriteLine(p.Name); // tanaka
vm.Name.Value = "kimura";
Console.WriteLine(p.Name); // kimura

vm.Name.Value = ""; // Error!
Console.WriteLine(p.Name); // kimura

コレクションのマッピング

MとVMの値の連携がRxで簡単に出来るようになりました。ただ、通常は、単一のオブジェクトだけではなく、コレクションに入ったModelをVMのコレクションに変換するといった処理がよくあります。

ReactivePropertyでは、これを単純化するためにReadOnlyReactiveCollectionクラスを提供しています。これは、ObservableCollectionから簡単に作ることができます。

var m = new ObservableCollection<Person>();

var vm = m.ToReadOnlyReactiveCollection(x => new PersonViewModel(x)); // M -> VMの変換処理を渡すと後はよろしくしてくれる

m.Add(new Person { Name = "tanaka", Age = 30 });
Console.WriteLine(vm[0].Name.Value); // tanaka

m[0].Name = "kimura";
Console.WriteLine(vm[0].Name.Value); // kimura

m.Add(new Person { Name = "nakata", Age = 40 });
Console.WriteLine(vm[1].Name.Value); // nakata

関連

ReactiveProperty オーバービュー - かずきのBlog@hatena

ReactiveProperty/README-ja.md at master · runceel/ReactiveProperty · GitHub

ReactiveProperty v1.2.0をリリースしました

$
0
0

先日書いた記事で、めんどくさいと思ってた部分を簡単にかけるようにしました。

MVVMでめんどくさいと思ってる部分を、個人的にどうやって緩和してるか - かずきのBlog@hatena

データのバリデーションンを伴うMとVMのプロパティの同期は以下のように書く必要がありました。

publicclass PersonViewModel
{
    public ReactiveProperty<string> Name { get; private set; }

    public ReactiveProperty<string> Age { get; private set; }

    public PersonViewModel(Person model)
    {
        this.Name = model                                                    // M -> VM
            .ObserveProperty(x => x.Name)                                    // IObservable<string>に変換して
            .ToReactiveProperty()                                            // ReactiveProperty<string>に変換して
            .SetValidateNotifyError(x =>                                     // 空文字の時はエラーにするstring.IsNullOrWhiteSpace(x) ? "Name is required" : null);
        this.Name                                                            // VM -> M
            .Where(_ => !this.Name.HasErrors)                                // エラーが無いときは
            .Subscribe(x => model.Name = x);                                 // 値を書き戻すthis.Age = model                                                     // M -> VM
            .ObserveProperty(x => x.Age)                                     // IObservable<int>に変換して
            .Select(x => x.ToString())                                       // IObservable<string>に変換して
            .ToReactiveProperty()                                            // ReactiveProperty<string>に変換して
            .SetValidateNotifyError(x =>                                     // int型に変換できない場合はエラーにする
            {
                int result; // no usereturnint.TryParse(x, out result) ? null : "";
            });
        this.Age                                                             // VM -> M
            .Where(_ => !this.Age.HasErrors)                                 // エラーが無いときは
            .Select(x => int.Parse(x))                                       // int型に変換して
            .Subscribe(x => model.Age = x);                                  // 書き戻す
    } 
}

これを、ToReactivePropertyAsSynchronizedメソッドにバリデーションエラーのときは値を無視するようにするオプションを追加して以下のようにかけるようにしました。

publicclass PersonViewModel
{
    public ReactiveProperty<string> Name { get; private set; }

    public ReactiveProperty<string> Age { get; private set; }

    public PersonViewModel(Person model)
    {
        this.Name = model
            .ToReactivePropertyAsSynchronized(
                x => x.Name,
                ignoreValidationErrorValue:true)
            .SetValidateNotifyError(x => string.IsNullOrEmpty(x) ? "error": null);
         
        this.Age = model                                                    
            .ToReactivePropertyAsSynchronized(
                x => x.Age,
                convert: x => x.ToString(),
                convertBack: x => int.Parse(x),
                ignoreValidationErrorValue:true)
            .SetValidateNotifyError(x =>
                {
                    int result; // no usereturnint.TryParse(x, out result) ? null : "error";
                });
    } 
}

今まではバリデーションエラーが、あろうが無かろうが、Mへ値が渡されてたのですが(しかも、convertBackに変な値がわたって例外がでると死ぬ)バリデーションエラーのときは値を流さないようにしました。個人的に気に入ってる。

動作は以下のようになります。

var model = new Person { Name = "tanaka", Age = 30 };
var vm = new PersonViewModel(model);

Console.WriteLine("{0} {1}", model.Name, model.Age); // tanaka 30

vm.Age.Value = "50"; // valid value
Console.WriteLine("{0} {1}", model.Name, model.Age); // tanaka 50

vm.Age.Value = "XX"; // invalid value
Console.WriteLine("{0} {1}", model.Name, model.Age); // tanaka 50

vm.Age.Value = "30"; // valid value
Console.WriteLine("{0} {1}", model.Name, model.Age); // tanaka 30

ReactiveProperty v2.0.0-pre1をリリースしました

$
0
0

NuGet Gallery | ReactiveProperty 2.0.0-pre1

変更箇所

  • 名前空間をCodeplex.ReactiveからReactive.Bindingsに変更しました。今後は、この名前空間を使っていきます。
  • Xamarin.Android向けのデータバインディング機能を追加

名前空間の変更

既存プロジェクトのアップグレードの際には以下の名前空間の置換を行ってください。

  • Codeplex.Reactive → Reactive.Bindings

Xamarin.Androidのデータバインディング機能を追加

Xamarin.AndroidのViewに対するSetBinding拡張メソッドと、Viewを継承したクラスのイベントをIObservableに変換する拡張メソッドと、IObservableにコマンドをセットするSetCommand拡張メソッドが主な機能です。

詳細は、以下のページを参照してください。

ReactiveProperty/README-Android.md at vNext · runceel/ReactiveProperty · GitHub

MVVM系フレームワークとReactivePropertyの組み合わせのサンプルプログラムを書きました

$
0
0

以下の3つのMVVMライブラリと

  • Livet
  • MVVM Light toolkit
  • Prism

ReactiveProperty(v2系使ってます)のサンプルをコードレシピに書きました。どれも、ほぼ同じ挙動をするサンプルなので比較用にでもどうぞ。

所感としては、WPFならLivetが一番機能が豊富で次点でPrismで一番機能が少ないのがMVVM Light toolkitです。対応プラットフォームが多いのは、MVVM Light toolkit、Prism、Livetの順番です。

機能特化か、汎用をとるかケースバイケースで自分にあったものを選ぶのがいいと思います。

Viewing all 1388 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>