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

ユニバーサルアプリのプロジェクトでページやクラスを追加しようとしたときにシステムエラーが出た場合の対処法

$
0
0

システムエラーというダイアログと、詳細なエラーログは、ここに出したよっていうダイアログが出て既存のソースはいじれるけど、新規追加ができないという悲しい状態になりました。

なんか一時フォルダ的なのを消してやろうと思って以下のフォルダを消したらなおりました。 勘が仕事した。

ユーザーフォルダ\AppData\Local\Microsoft\VisualStudio\12.0\ComponentModelCache

JSON.NETで変なJSONを読み込む方法

$
0
0

Pocketのクライアントを作ろうとJSONを読み込んでたんですよ。JSON.NETで。 んで、配列で結果返すというやつがあったので正直に配列で受け取ろうとしたら…

{
  values: 
  {
    "1": {"name": "taro"},
    "2": {"name": "jiro"},
    "3": {"name": "saburo"}
  }
}

こんなJSON返してきやがった。Arrayじゃない!Objectです。ということで、以下のようにデータを受け取るようにしました。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        staticvoid Main(string[] args)
        {
            var json = @"{  values:   {""1"": {""name"": ""taro""},""2"": {""name"": ""jiro""},""3"": {""name"": ""saburo""}  }}";
            var item = JsonConvert.DeserializeObject<Item>(json);
            foreach (var v in item.Values)
            {
                Console.WriteLine("{0}: {1}", v.Key, v.Value.Name);
            }
        }
    }

    class Item
    {
        // 何がくるかわからなならDictionaryしか…
        [JsonProperty("values")]
        public Dictionary<string, Person> Values { get; set; }
    }
    class Person
    {
        [JsonProperty("name")]
        publicstring Name { get; set; }
    }
}

リストじゃないと思いつつ、これで動いたので進めようと思ったら、データが0件のときに以下のようなJSONが返ってきました。

{
  values:[]
}

あ…うん知ってる。それ配列…。

これをさっきのプログラムに食わせると…

ハンドルされていない例外: Newtonsoft.Json.JsonSerializationException: Cannot des
erialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Gen
eric.Dictionary`2[System.String,ConsoleApplication1.Person]' because the type re
quires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}
) or change the deserialized type to an array or a type that implements a collec
tion interface (e.g. ICollection, IList) like List<T> that can be deserialized f
rom a JSON array. JsonArrayAttribute can also be added to the type to force it t
o deserialize from a JSON array.

はい。おっしゃる通りです。

なので、いったんdynamicで受けてTypeプロパティで型を見てからつどシリアライズしてでシリアライズする(超非効率)

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        staticvoid Main(string[] args)
        {
//            var json = @"//{//  values: //  {//    ""1"": {""name"": ""taro""},//    ""2"": {""name"": ""jiro""},//    ""3"": {""name"": ""saburo""}//  }//}//";
            var json = @"{  values:[]}";
            var item = JsonConvert.DeserializeObject<Item>(json);
            foreach (var v in item.GetValues())
            {
                Console.WriteLine("{0}: {1}", v.Key, v.Value.Name);
            }
        }
    }

    class Item
    {
        // いったんdynamicで受けておいて
        [JsonProperty("values")]
        public dynamic Values { get; set; }

        public Dictionary<string, Person> GetValues()
        {
            // 中身の型を見て再度必要な型になおすif (this.Values.Type == JTokenType.Object)
            {
                var json = JsonConvert.SerializeObject(this.Values);
                return JsonConvert.DeserializeObject<Dictionary<string, Person>>(json);
            }

            // オブジェクトじゃなかったら空だよねreturnnew Dictionary<string, Person>();
        }
    }
    class Person
    {
        [JsonProperty("name")]
        publicstring Name { get; set; }
    }
}

とりあえず、これで逃げてるけど、もっといい方法ないのかなぁ。実際にはGetValuesにあたるメソッドは、実際には二度目からはキャッシュした値を返すようにしてます。

ユニバーサルWindowsアプリでコード共有の方法などあれこれ

$
0
0

花粉がつらい今日この頃です。

Sharedプロジェクト

まずは基本です。Universal Windows appでは、SharedのプロジェクトにおいたコードはWindowsストアアプリとWindows Phone アプリで共有されます。

f:id:okazuki:20140420105417j:plain

このときXAMLもC#も共有されます

プラットフォーム固有のXAML

でも、プラットフォーム固有の処理や見た目を定義したいことがありますよね。ページはそのまま個別に作ればいいですが、リソースてきなXAMLは両方のプロジェクトに同じ名前で作成してApp.xamlで読み込みましょう。

こんな風にPlatformDictionary.xamlを両方のプロジェクトに作って…

f:id:okazuki:20140420110056j:plain

App.xamlで読み込む。これでWindows ストア アプリのときはストアのプロジェクトのやつを読み込んで、電話のときは電話のプロジェクトのやつを読み込みます。

<Applicationx:Class="App33.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App33"><Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="PlatformDictionary.xaml" /></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources></Application>

コントロールのスタイルとか、DataTemplateに効果を発揮しますね。

partialクラス

ビルドしたアセンブリが共有されるのではなく、コードが共有されてビルド時によきにはからってくれるという仕組みなので、パーシャルクラスが使えます。以下のようなクラス配置で。

f:id:okazuki:20140420110608j:plain

Person.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace App33
{
    publicpartialclass Person
    {
        publicstring Name { get; set; }
    }
}

WPA側Person.partial.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace App33
{
    publicpartialclass Person
    {
        publicvoid Greet()
        {
            Debug.WriteLine("Hello Windows phone!!");
        }
    }
}

Windows store app側Person.partial.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace App33
{
    publicpartialclass Person
    {
        publicvoid Greet()
        {
            Debug.WriteLine("Hello Windows store app!!");
        }
    }
}

こんな風にメソッド全体が違う場合はパーシャルクラスを使うことができます。まぁでもこの後紹介する#ifディレクティブのほうがIDE支援は強いので、そっちが使うのがいいかも?

#ifディレクティブ

WINDOWS_APP, WINDOWS_PHONE_APPという定数があるので、それで#if ~ #endifでくくることでプラットフォーム固有の処理を書けます。

partialの例を#ifディレクティブになおすとこんな感じのコードになります。

f:id:okazuki:20140420111249j:plain

黄色いマーカーで示してるところでプロジェクトを切り替えると、そっちのプロジェクトでは無効なコードがグレーアウトされます。

f:id:okazuki:20140420111436j:plain

Windows Phone 8.1アプリでAsyncOAuthを使う

$
0
0

使い方は通常でよくてNuGetから仕入れることが出来ないくらい。ただ、HttpClientのPCL版の参照の追加と、以下のつぶやきにあるように、Microsoft.Bcl.AsyncのDLLも追加しておこう。 英語情報しかないかなぁと思ったら、まさかのあの人のつぶやきで解決しました。

東北行く人は東北さくらトリップ入れてもいいかも?あと興味深い実装も?

$
0
0

MVPの初音さんが作った東北さくらトリップというアプリがあります。

このアプリ名前の通り東北の桜の名所+アルファの情報を表示してくれるアプリです。

f:id:okazuki:20140423185508j:plain

このアプリの説明を簡単にすると、桜の見どころからSNSの情報、ほかの近所の観光地や宿泊情報など、東北へ行く際の旅行を完全にサポート!!してくれるアプリです。

f:id:okazuki:20140423185628j:plain

トップ画面で待機してると桜がひらひら舞い落ちてきたり見てるだけでも楽しいアプリになってます。

この実装が気になって担当してたみつばたんにお願いして作成風景とってもらった動画がこちら。

さくらの実装もさることながら、色々なSNSと連携したりと詰まってる技術要素の数には頭が下がります。

というこで、初音さんに確認したところGWくらいが東北は桜が見ごろということなので、Windowsタブレット片手に行く予定の方は、このアプリを入れてみるといいのではないかと思いました。

よりMVVMに、よりライトにPrism 5がリリースされました

$
0
0

10日ほど前の2014年4月19日にPrism 5 for .NET4.5がリリースされてました。

Prismは、MSの中の人たちが作ってるOSSのWPF用(SL用やストアアプリ用などもある)のフレームワークで、複合型アプリケーション(モジュールを組み合わせて1つのアプリケーションに仕立て上げるもの)のサポートと、MVVMパターンのサポート機能が含まれています。

ライブラリの入手

サンプルプログラムなどを見るならMSのサイトからDLしてもいいですが、さくっと始めるだけならNuGetで入手するのが楽です。

Install-Package Prism

もちろんGUIからもOKです。ダウンロードする際に、結構細分化された状態でパッケージが入ってくるのがわかります。

  • Prism.Composition
    • 複合型アプリケーションを作成するための機能が含まれてます。一番複雑怪奇。
  • Prism.Interactivity
    • MVVMパターンでVMから、ユーザと対話するための機能を提供するクラス群が入ってます。簡単にいうと確認ダイアログとかが出せます。
  • Prism.PubSubEvents
    • モジュール間で疎結合にメッセージのやり取りを行うためのクラス群が入ってます。Medatorパターンですね。
  • Prism.Mvvm
    • MVVMをするための基本的なICommandの実装やINotifyPropertyChangedの実装の他にViewとViewModelを自動的に結び付ける機能を提供します。
  • Prism
    • 上記のNuGetパッケージをまとめるためのプレースホルダーです。

複合型アプリケーション

昔とかわりません。ムズイです。

MVVMサポート

結構シンプルに実装してます。そのぶんめんどいです。大体以下のクラスです。

  • INotifyPropertyChangedの実装のBindableBaseクラス。
  • ViewからViewModelの自動引き当てを行うViewModelLocatorProviderクラス。
  • INotifyErrorInfoを実装するときに楽できるErrorsContainer
  • ICommandの実装のDelegateCommand

遊んでみた

DataAnnotationでのエラーチェックをサポートしたViewModel作って遊んでみた。

using Microsoft.Practices.Prism.Mvvm;
using Microsoft.Practices.Prism.ViewModel;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.CompilerServices;

namespace WpfApplication7.ViewModels
{
    /// <summary>/// PrismのBindableBaseを拡張してデータの検証を追加/// </summary>publicclass ViewModelBase : BindableBase, INotifyDataErrorInfo
    {
        // INotifyDataErrorInfo実装時に楽できるクラスprivatereadonly ErrorsContainer<string> Errors;

        publicevent EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        public ViewModelBase()
        {
            // ErrorsChangedを発行するメソッドをコンストラクタに渡してインスタンス化すると// SetErrorsなどのときに、いい感じにイベント出してくれるthis.Errors = new ErrorsContainer<string>(this.OnErrorsChanged);
        }

        // INotifyDataErrorInfoの実装。基本委譲するだけ。public IEnumerable GetErrors(string propertyName)
        {
            returnthis.Errors.GetErrors(propertyName);
        }

        publicbool HasErrors
        {
            get { returnthis.Errors.HasErrors; }
        }

        protectedvirtualvoid OnErrorsChanged([CallerMemberName]string propertyName = null)
        {
            var h = this.ErrorsChanged;
            if (h != null)
            {
                h(this, new DataErrorsChangedEventArgs(propertyName));
            }
        }

        /// <summary>/// 値の設定と値の検証を行う/// </summary>protectedoverridebool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
        {
            var changed = base.SetProperty<T>(ref storage, value, propertyName);
            if (changed)
            {
                // DataAnnotationsでプロパティの検証
                var context = new ValidationContext(this)
                {
                    MemberName = propertyName
                };
                List<ValidationResult> validationErrors = new List<ValidationResult>();
                if (!Validator.TryValidateProperty(value, context, validationErrors))
                {
                    // エラーがあったらエラーを設定this.Errors.SetErrors(
                        propertyName,
                        validationErrors.Select(error => error.ErrorMessage).ToArray());
                }
                else
                {
                    // なければクリアthis.Errors.ClearErrors(propertyName);
                }
            }
            return changed;
        }

        publicbool ValidateObject()
        {
            // オブジェクト全体の検証を行う
            var context = new ValidationContext(this);
            List<ValidationResult> validationErrors = new List<ValidationResult>();
            if (Validator.TryValidateObject(this, context, validationErrors))
            {
                returntrue;
            }

            var errors = validationErrors
                .Where(error => error.MemberNames.Any())
                .GroupBy(error => error.MemberNames.First());
            foreach (var error in errors)
            {
                this.Errors.SetErrors(
                    error.Key,
                    error.Select(e => e.ErrorMessage).ToArray());
            }

            returnfalse;
        }
    }
}

そして、これを継承したViewModel

using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Interactivity.InteractionRequest;
using System.ComponentModel.DataAnnotations;

namespace WpfApplication7.ViewModels
{
    publicclass MainWindowViewModel : ViewModelBase
    {

        privatestring input;

        /// <summary>/// 必須入力の項目/// </summary>
        [Required(ErrorMessage = "必須!")]
        publicstring Input
        {
            get { returnthis.input; }
            // 値が設定されたタイミングでコマンドの実行可否の確認set { this.SetProperty(refthis.input, value); this.AlertCommand.RaiseCanExecuteChanged(); }
        }

        private InteractionRequest<Notification> alertRequest = new InteractionRequest<Notification>();

        /// <summary>/// Viewへ通知を行うためのInteractionRequest/// </summary>public InteractionRequest<Notification> AlertRequest
        {
            get { returnthis.alertRequest; }
            set { this.SetProperty(refthis.alertRequest, value); }
        }

        private DelegateCommand alertCommand;

        /// <summary>/// 適当にコマンド/// </summary>public DelegateCommand AlertCommand
        {
            get
            {
                returnthis.alertCommand ?? 
                    (this.alertCommand = new DelegateCommand(this.AlertExecute, this.CanAlertExecute));
            }
        }

        publicvoid AlertExecute()
        {
            // エラーがなければif (!this.ValidateObject())
            {
                return;
            }

            // Viewに投げるthis.AlertRequest.Raise(new Notification
            { 
                Title = "お知らせ", 
                Content = this.HasErrors ? "エラー" : "オッケー" 
            });
        }

        publicbool CanAlertExecute()
        {
            return !this.HasErrors;
        }

    }
}

んでViewのコードビハインドです。

using Microsoft.Practices.Prism.Mvvm;
using System.Windows;

namespace WpfApplication7.Views
{
    /// <summary>/// IViewを実装してViewModelLocatorProviderでViewModelを自動で設定/// </summary>publicpartialclass MainWindow : Window, IView
    {
        public MainWindow()
        {
            InitializeComponent();
            ViewModelLocationProvider.AutoWireViewModelChanged(this);
        }
    }
}

ViewからViewModelを設定する方法はXAMLでViewModelLocatorを使う方法があるけど、それだとXAMLエディタがエラーをはくので個人的にはコードビハインドでViewModelLocationProviderを使うほうが好み。

そしてXAML。

<Windowxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mvvm="clr-namespace:Microsoft.Practices.Prism.Mvvm;assembly=Microsoft.Practices.Prism.Mvvm.Desktop"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:ViewModels="clr-namespace:WpfApplication7.ViewModels"xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"xmlns:Custom="http://www.codeplex.com/prism"xmlns:DefaultPopupWindows="clr-namespace:Microsoft.Practices.Prism.Interactivity.DefaultPopupWindows;assembly=Microsoft.Practices.Prism.Interactivity"mc:Ignorable="d"x:Class="WpfApplication7.Views.MainWindow"Title="MainView"Height="300"Width="300"d:DataContext="{d:DesignInstance {x:Type ViewModels:MainWindowViewModel}, IsDesignTimeCreatable=True}"><i:Interaction.Triggers><!-- AlertRequestからの要求を受けるTrigger --><Custom:InteractionRequestTrigger SourceObject="{Binding AlertRequest}"><!-- 地味にPrism 5からWindow表示アクションがある --><Custom:PopupWindowAction /></Custom:InteractionRequestTrigger></i:Interaction.Triggers><Grid><!-- VMの入力項目にバインド --><TextBox x:Name="textBox"HorizontalAlignment="Left"Height="23"Margin="10,10,0,0"TextWrapping="Wrap"Text="{Binding Input, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"VerticalAlignment="Top"Width="120"/><!-- TextBoxのエラーの内容を表示する --><TextBlock HorizontalAlignment="Left"Margin="135,18,0,0"TextWrapping="Wrap"Text="{Binding (Validation.Errors)[0].ErrorContent, ElementName=textBox, Mode=OneWay}"VerticalAlignment="Top"/><!-- コマンドと接続するためのボタン --><Button Content="Button"HorizontalAlignment="Left"Margin="10,38,0,0"VerticalAlignment="Top"Width="75"Command="{Binding AlertCommand, Mode=OneWay}"/></Grid></Window>

実行するとこんな感じ。

f:id:okazuki:20140427024728j:plain

何もせずボタンお押すとブロックするよ。これはCommandでValidateObjectを呼んでるおかげ。

f:id:okazuki:20140427024830j:plain

何か入力するとボタンが押せるようになる。押したらデフォルトのウィンドウが出てくる。

f:id:okazuki:20140427024933j:plain

所感

軽いMVVMフレームワークとして、PrismのMVVM部分だけ参照して使うのはありかなと思いました。(NuGetでもパッケージが細かくわかれてるので) 複合アプリ作りたい人はフルパッケージでどうぞ!

Prism 5で足し算アプリ

Leap + Reactive Extensionsでフリックしよう

$
0
0

Leap Motionの組み込みジェスチャーではなくて、自分でLeapからデータを拾ってきてジェスチャーとして認識する方法が、細かいチューニングができていい! ということでフリックを認識するサンプルをコードレシピにあげました。元ネタは初音さんのコード。

コードレシピにあげたコードはこちら。


Universal Windows app入門 表という名前でKindle本出してみました

ReactiveProperty オーバービュー

$
0
0

ReactivePropertyとは

Reactive ExtensionsをベースとしたMVVMパターンのアプリケーションの作成をサポートするためのライブラリです。特徴としては、Reactive Extensionsをベースとしているため、全てをIObservableをベースとして捉えて、V, VM, Mすべてのレイヤをシームレスにつなぐことが可能な点です。

サポートしているプラットフォーム

現時点の最新版(0.4.5-beta3)で以下のプラットフォームをサポートしてます。

  • WPF(.NET4, .NET4.5)
  • Windows store app 8.1
  • Windows Phone 8, 8.1
  • Universal Windows app
  • MonoAndroid(alphaチャネルで動作確認)
  • MonoTouch(環境が無いから試せてないけど恐らく…)

基本機能

ReactivePropertyは、名前の通りReactiveProperty<T>クラスをViewModelクラスのプロパティとして定義します。そのかわりMVVMフレームワークと異なり、ViewModelの基本型を強制することはありません。

ReactivePropertyクラス

ReactivePropertyクラスは、以下の機能を持つクラスです。

  • Valueプロパティで現在の値を設定・取得
  • Valueプロパティの値が変かする度に以下の動作をする
    • IObservableのOnNextが発生する
    • PropertyChangedイベントが発生する
    • 値の検証を設定してる場合は、ObserveErrorChangedにエラーがOnNextで通知される

プロパティ自体がIObservableで、プロパティのエラーもIObservableとして表現されているのが特徴です。

ReactivePropertyの作成方法

ReactivePropertyクラスの特徴がわかったので、次は作り方です。int型の値を持つReactivePropertyの作り方。

new ReactiveProperty<int>();

シンプルにnewで作れます。こういうシンプルさ大事だと思います。では、初期値100の場合は?

new ReactiveProperty<int>(100);

コンストラクタで指定できます。そして、特徴的なのがIObservable<T>からReactivePropertyへの変換が出来る点です。

Subject<int> s = new Subject<int>();
ReactiveProperty<int> p = s.ToReactiveProperty(); // IO<T> -> RxProp<T>へ変換

基本的に、この3つのインスタンス化の方法を使ってReactivePropertyをつくります。

使用例

ReactivePropertyを使用したシンプルなViewModelクラスの定義は、以下のようになります。

publicclass MainViewModel
{
    public ReactiveProperty<string> Input { get; private set; }

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

    public MainViewModel()
    {
        // 通常のインスタンス化this.Input = new ReactiveProperty<string>();

        // ReactiveProperty<T>はIObservable<T>なので、LINQで変換してToReactivePropertyで// ReactiveProperty化。this.Output = this.Input
            .Select(s => s != null ? s.ToUpper() : null)
            .ToReactiveProperty();
    }
}

これをDataContextに設定して画面にバインドするとこうなります。

<Windowxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApplication9"x:Class="WpfApplication9.MainWindow"Title="MainWindow"Height="350"Width="525"><Window.DataContext><local:MainViewModel/></Window.DataContext><StackPanel><TextBox Text="{Binding Input.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /><TextBlock Text="{Binding Output.Value}" /></StackPanel></Window>

f:id:okazuki:20140506232546j:plain

Inputプロパティの値が変換されてOutputに流れていることがわかると思います。この例では単純に入力→変換→出力をVとVM内で完結させていますが、MVVMすべての層で一連の流れとして記述するのがReactivePropertyの特徴になります。

IObservableじゃないものとの接続

世の中、全てがIO<T>だったらいいのですが、一般的なPOCOなどはそうじゃありません。そういうもののためにINotifyPropertyChangedのPropertyChangedイベントをIOに変換するヘルパーメソッドがあります。

以下のようなカウンタークラスがあるとします。

publicclass Counter : INotifyPropertyChanged
{

    publicevent PropertyChangedEventHandler PropertyChanged;

    privateint count;

    publicint Count
    {
        get { returnthis.count; }
        privateset
        {
            this.count = value;
            var h = this.PropertyChanged;
            if (h != null)
            {
                h(this, new PropertyChangedEventArgs("Count"));
            }
        }
    }

    publicvoid Increment()
    {
        this.Count++;
    }

    publicvoid Decriment()
    {
        this.Count--;
    }
}

このクラスのCountプロパティの連続的な変化をIOだと見してReactivePropertyにするには以下のようなObservePropertyメソッドを使います。

publicclass MainViewModel
{
    private Counter counter = new Counter();

    public ReactiveProperty<int> Count { get; private set; }

    public MainViewModel()
    {
        this.Count = this.counter
            // IO<T>に変換するプロパティを指定
            .ObserveProperty(c => c.Count)
            // IO<T>になったのでReactivePropertyに変換可能
            .ToReactiveProperty();

    }
}

このように、POCOのModelとの接続も行えます。

コマンドはIObservable<bool>とIObservable<object>

ReactivePropertyが提供するICommandの実装は、ReactiveCommandといいます。ReactivePropertyでは、ICommandのCanExecuteChangedイベントをIObservable<bool>とみなして、IObservable<bool>からReactiveCommandを生成する機能を提供しています。

ReactiveCommandのExecuteメソッドは、IObservable<T>として動作します。

var s = new Subject<bool>(); // 何かのIO<bool>
ReactiveCommand command = s.ToReactiveCommand(); // コマンドに変換
command.Subscrive(_ =>
{
  // コマンドのExecute時の処理
});

例えば、先ほどのCounterクラスが10になるまでインクリメントできて、0になるまでデクリメントできる2つのコマンドを持ったViewModelクラスは以下のようになります。

publicclass MainViewModel
{
    private Counter counter = new Counter();

    public ReactiveProperty<int> Count { get; private set; }

    public ReactiveCommand IncrementCommand { get; private set; }

    public ReactiveCommand DecrementCommand { get; private set; }

    public MainViewModel()
    {
        this.Count = this.counter
            // IO<T>に変換するプロパティを指定
            .ObserveProperty(c => c.Count)
            // IO<T>になったのでReactivePropertyに変換可能
            .ToReactiveProperty();

        // Countの値が10以下の場合インクリメント出来るthis.IncrementCommand = this.Count
            // IO<bool>へ変換
            .Select(i => i < 10)
            // コマンドへ変換
            .ToReactiveCommand();
        // Executeが呼ばれたらインクリメントthis.IncrementCommand
            .Subscribe(_ => this.counter.Increment());

        // Countの値が0より大きい場合デクリメントできるthis.DecrementCommand = this.Count
            // IO<bool>へ変換
            .Select(i => i > 0)
            // コマンドへ変換
            .ToReactiveCommand();
        // Executeが呼ばれたらデクリメントthis.DecrementCommand
            .Subscribe(_ => this.counter.Decriment());

    }
}

このViewModelを以下のようなViewと接続します。

<Windowxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApplication9"x:Class="WpfApplication9.MainWindow"Title="MainWindow"Height="350"Width="525"><Window.DataContext><local:MainViewModel/></Window.DataContext><StackPanel><TextBlock Text="{Binding Count.Value}" /><Button Content="Incr"Command="{Binding IncrementCommand, Mode=OneWay}"/><Button Content="Decr"Command="{Binding DecrementCommand, Mode=OneWay}"/></StackPanel></Window>

Countの値の変化に応じて自動的に実行可否が変わることが確認できます。

f:id:okazuki:20140507000635j:plain

f:id:okazuki:20140507000659j:plain

f:id:okazuki:20140507000729j:plain

値の検証

ReactivePropertyには、入力値の検証機能も組み込まれています。一番簡単な検証は、アトリビュートで検証ルールを指定することです。検証ルールをReactiveProperty>T<のプロパティに設定して、インスタンスを作成するタイミングでSetValidateAttributeで自分自身が、なんのプロパティであるか指定する必要があります。

publicclass MainViewModel
{
    // System.ComponentModel.DataAnnotationsの属性で検証ルールを指定
    [Required(ErrorMessage = "必須です")]
    public ReactiveProperty<string> Input { get; private set; }

    public MainViewModel()
    {
        this.Input = new ReactiveProperty<string>()
            // 検証ルールをReactivePropertyに設定する
            .SetValidateAttribute(() => this.Input);
    }
}

以下のようにViewと接続すると、値の検証が働いていることがわかります。

<Windowxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApplication9"x:Class="WpfApplication9.MainWindow"Title="MainWindow"Height="350"Width="525"><Window.DataContext><local:MainViewModel/></Window.DataContext><StackPanel><TextBox Text="{Binding Input.Value, UpdateSourceTrigger=PropertyChanged}" /></StackPanel></Window>

f:id:okazuki:20140507002328j:plain

f:id:okazuki:20140507002350j:plain

エラーメッセージを出すには、以下のようにObserveErrorChangedから目的のエラーメッセージだけを取り出すクエリを書けばOKです。

publicclass MainViewModel
{
    // System.ComponentModel.DataAnnotationsの属性で検証ルールを指定
    [Required(ErrorMessage = "必須です")]
    public ReactiveProperty<string> Input { get; private set; }

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

    public MainViewModel()
    {
        this.Input = new ReactiveProperty<string>()
            // 検証ルールをReactivePropertyに設定する
            .SetValidateAttribute(() => this.Input);
        // エラーメッセージを出力するプロパティを作成するthis.InputErrorMessage = this.Input
            // エラーが発行されるIO<IE>を変換する
            .ObserveErrorChanged
            // エラーがない場合nullになるので空のIEにする
            .Select(e => e ?? Enumerable.Empty<object>())
            // 最初のエラーメッセージを取得する
            .Select(e => e.OfType<string>().FirstOrDefault())
            // ReactiveProperty化
            .ToReactiveProperty();
    }
}

このInputErrorMessageをXAMLでTextBlockにバインドすればエラーメッセージの表示が出来ます。

初期状態ではエラーメッセージは表示されません(仕様)

f:id:okazuki:20140507004511j:plain

値を変更して、エラーの条件に合致するようになるとエラーが表示されます。

f:id:okazuki:20140507004536j:plain

また、SetValidateNotifyErrorメソッドを使うことでカスタム検証ロジックを含めることができます。こちらはnullを返すことでエラーなし。それ以外の値を返すことで、エラーがあるという結果になります。 このメソッドで返した値はObserveErrorChangedに流れていきます。

コレクション

ReadOnlyReactiveCollection<T>を提供しています。これは読み取り専用のコレクションで、IObservable<CollectionChanged<T>>か、シンプルにIObservable<T>から生成する方法があります。前者は登録・更新・削除・リセットに対応していて、後者は、登録とリセットに対応しています。

ToReadOnlyReactoveCollectionは、値が発行されるたびにコレクションに値を追加します。オプションとして、何かOnNextが発生するとコレクションをリセットするIObservable<Unit>が渡せます。

例として、入力値のログをコレクションとして保持しているが、resetという入力がされるとクリアされるものを作ります。

publicclass MainViewModel
{
    public ReactiveProperty<string> Input { get; private set; }

    public ReadOnlyReactiveCollection<string> InputLog { get; private set; }

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

        this.InputLog = this.Input
            // Inputの値が発行されるたびに追加されるコレクションを作成
            .ToReadOnlyReactiveCollection(
                // リセットのきっかけはInputがresetになったときthis.Input
                    .Where(s => s == "reset")
                    .Select(_ => Unit.Default));
    }
}

以下のような画面と紐づけて実行します。

<Windowxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApplication9"x:Class="WpfApplication9.MainWindow"Title="MainWindow"Height="350"Width="525"><Window.DataContext><local:MainViewModel/></Window.DataContext><Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition /></Grid.RowDefinitions><TextBox Text="{Binding Input.Value, UpdateSourceTrigger=PropertyChanged}" /><ListBox Grid.Row="1"ItemsSource="{Binding InputLog}" /></Grid></Window>

入力値のログをとっているが

f:id:okazuki:20140507010203j:plain

入力値がresetになるとクリアされる

f:id:okazuki:20140507010217j:plain

細かな制御を行うコレクション

先ほど紹介した方法では、追加かリセットしかできないですが、これから紹介する方法では登録・更新・削除を制御することができます。

追加・更新・削除・リセットを制御するにはReactivePropertyのCollectionChanged>T<型のIObservableからToReadOnlyReactiveCollectionメソッドで作成します。

CollectionChanged<T&gt型には、staticメソッドでAdd, Remove, Replaceが定義されていて、static readonlyなフィールドでResetという値が定義されています。これらの値を流すIObservableを作成することで、柔軟にコレクションの変更が出来るようになります。例えば、ボタンが押された時間を表す文字列を記録するアプリを考えます。以下のように4つのコマンドと選択項目を表すプロパティ、そして、記録を残すコレクションをプロパティに持ち、これらを組み合わせて登録更新削除などを行います。

publicclass MainViewModel
{
    public ReactiveProperty<string> SelectedItem { get; private set; }
    public ReactiveCommand AddCommand { get; private set; }
    public ReactiveCommand ResetCommand { get; private set; }
    public ReactiveCommand UpdateCommand { get; private set; }
    public ReactiveCommand DeleteCommand { get; private set; }

    public ReadOnlyReactiveCollection<string> TimestampLog { get; private set; }

    public MainViewModel()
    {
        this.SelectedItem = new ReactiveProperty<string>();

        this.AddCommand = new ReactiveCommand();
        this.ResetCommand = new ReactiveCommand();
        this.UpdateCommand = this.SelectedItem.Select(v => v != null).ToReactiveCommand();
        this.DeleteCommand = this.SelectedItem.Select(v => v != null).ToReactiveCommand();

        this.TimestampLog = Observable.Merge(
            this.AddCommand
                .Select(_ => CollectionChanged<string>.Add(0, DateTime.Now.ToString())),
            this.ResetCommand.Select(_ => CollectionChanged<string>.Reset),
            this.UpdateCommand
                .Select(_ => this.SelectedItem.Value)
                .Select(v => CollectionChanged<string>.Replace(this.TimestampLog.IndexOf(v), DateTime.Now.ToString())),
            this.DeleteCommand
                .Select(_ => this.SelectedItem.Value)
                .Select(v => CollectionChanged<string>.Remove(this.TimestampLog.IndexOf(v))))
            .ToReadOnlyReactiveCollection();
    }
}

MergeメソッドでAddCommand、ResetCommand、UpdateCommand、DeleteCommandを1本のIObservable<CollectionChanged<T>>にまとめてからコレクションに変換しています。このような合成もReactivePropertyがIObservableである故の強みです。

他のMVVMライブラリとの連携

ReactivePropertyは、シンプルにプロパティとコマンドとコレクションと、いくつかのReactive Extensionsを拡張するユーテリティメソッドから構成されています。そのためMVVMのフル機能はカバーしていません(例としてメッセンジャーとか)。 これらの機能が必要な場合は、お好みのMVVMライブラリを使うことが出来ます。プロパティ定義とコマンドをRxPropertyにして、メッセンジャーを既存ライブラリのものにすればOKです。メッセンジャーをIObservableにする拡張メソッドを用意したり、逆の変換を行うメソッドを用意すると、よりシームレスに使えるようになるかもしれません。

ReactivePropertyを.NET 4対応しました

$
0
0

今まで.NET 4.5からだったんですよね地味に。

.NET 4専用のプロジェクトをおこして、そちらにポーティング可能なものだけ移動させて作りました。まだ正式版じゃなくてプレリリースの状態です。なので、NuGetで入れるには以下のようにしてリリース前のパッケージをインストールするようにしてください。

Install-Package ReactiveProperty -Pre

該当バージョンのページ

XamarinのAndroidアプリでReactivePropertyを使う

$
0
0

最近書いてるReactivePropertyですが、Xamarinにも対応しています。なので、簡単にカウントアップするサンプル(ひな形で生成されるやつ)を作ってみようと思います。

Xamarinのバージョン

使用しているXamarinのバージョンは5.0(build784)です。XamarinのVisual Studioプラグインを利用してVisual Studio上で開発しています。

プロジェクトの作成とReactivePropertyのインストール

新規作成から「Android Ice Cream Sandwich Application」を選びます。プロジェクト名は、CounterAppにしました。

NuGetパッケージの管理からReactivePropertyで検索して「ReactiveProperty Portable」をインストールします。最新のXamarinに現時点で対応しているのは、0.4.5-beta3になるので、リリース前のパッケージを含めるを選択してください。

f:id:okazuki:20140507170822j:plain

Reactive ExtensionsとReactivePropertyがインストールされます。

f:id:okazuki:20140507170944j:plain

カウンタークラスの作成

次にModelとなるCounterクラスを作成します。これはINotifyPropertyChangedを実装した普通のC#のクラスです。現在のカウント値を表すプロパティと、カウントアップするメソッドを持っています。

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace CounterApp
{
    publicclass Counter : INotifyPropertyChanged
    {
        publicevent PropertyChangedEventHandler PropertyChanged;
        privatebool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null)
        {
            if (Equals(field, value))
            {
                returnfalse;
            }

            field = value;
            var h = this.PropertyChanged;
            if (h != null)
            {
                h(this, new PropertyChangedEventArgs(propertyName));
            }

            returntrue;
        }

        privateint count;

        publicint Count
        {
            get { returnthis.count; }
            privateset { this.SetProperty(refthis.count, value); }
        }

        publicvoid Incr()
        {
            this.Count++;
        }
    }
}

ViewModelの作成

次に、Activityに紐づけるViewModelクラスを作成します。名前はMainViewModelにしました。先ほど作成したCounterクラスを内部にもってActivityに公開するプロパティや操作などをReactivePropertyやReactiveCommandで定義しています。

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

namespace CounterApp
{
    publicclass MainViewModel
    {
        privatereadonly Counter Model = new Counter();

        // Activityに公開するカウント値public ReactiveProperty<string> CountMessage { get; private set; }

        // インクリメントを行うためのコマンドpublic ReactiveCommand IncrCommand { get; private set; }

        public MainViewModel()
        {
            // model -> rxpropへのOneWayバインディングthis.CountMessage = this.Model
                .ObserveProperty(c => c.Count)
                .Select(i => string.Format("{0} click!", i))
                .ToReactiveProperty();

            // コマンドが実行されたらModelのIncrメソッドを実行するthis.IncrCommand = new ReactiveCommand();
            this.IncrCommand.Subscribe(_ => this.Model.Incr());
        }
    }
}

Activityの作成

MainActivityのコードをひな形から修正します。

// バインディング用メソッドが定義されてる名前空間using Codeplex.Reactive.Binding;

using System;

using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace CounterApp
{
    [Activity(Label = "CounterApp", MainLauncher = true, Icon = "@drawable/icon")]
    publicclass MainActivity : Activity
    {
        // ViewModelprivatereadonly MainViewModel DataContext = new MainViewModel();

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

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

            // Get our button from the layout resource,// and attach an event to it
            var button = FindViewById<Button>(Resource.Id.MyButton);

            // CountMessageをbuttonのTextプロパティへバインドthis.DataContext.CountMessage.BindTo(button, b => b.Text);

            // コマンドをイベントハンドラに変換してボタンと紐づける
            button.Click += this.DataContext.IncrCommand.ToEventHandler();
        }
    }
}

ReactivePropertyを通常のプロパティにバインドするためのBindToという拡張メソッドが、Codeplex.Reactive.Binding名前空間に用意されているので、それを使ってButtonのTextと紐付けを行います。

コマンドはToEventHandlerという拡張メソッドでイベントハンドラへ変換する拡張メソッドが、同じくCodeplex.Reactive.Binding名前空間に用意されてるので、それを使ってButtonのClickイベントと紐づけます。

実行して動作確認

MonoForAndroid_API_15のエミュレータを起動してじっくりと配備を行います。ここらへん実機がほしくなりますね…。

実行すると以下のような画面が表示されます。

f:id:okazuki:20140507173346j:plain

ボタンを押すとカウントアップされていきます。

f:id:okazuki:20140507173428j:plain

まとめ

簡単なサンプルレベルでしか試していませんが、とりあえず動く…!ということでReactivePropertyをよろしくお願いいたします。

ReactiveProperty 0.4.5.0リリースしました

ReactivePropertyとPrism for WinRTとの連携

$
0
0

ちょっとしたTipsですが…。

Prism for WinRTとReactivePropertyを同時に使う場合以下の点に気を付けるといいです。

なるべくコンストラクタインジェクションを使う

ReactivePropertyは、コンストラクタで組み立てるのが一番スムーズなので出来るだけ外部からDIするものはプロパティでインジェクションするのではなくて、コンストラクタでインジェクションしましょう。そうすると、コンストラクタで準備万端状態になるので、ReactivePropertyの組み立てがスムーズに行えます。

どうしてもプロパティインジェクションしたいときなど

どうしてもプロパティでDIしたいときや、ナビゲーションのタイミングで初期化したいものなどがあるときも、以下の方法で、やるとコンストラクタでReactivePropertyの組み立てができます。

DIで使うプロパティもPropertyChangedイベントを発行する

こうしておくとObservePropertyを使って監視できるのでコンストラクタでさくっといけるようになります。

public Hoge()
{
  // DI対象のプロパティが全部設定が終わったら初期化する
  var initCompleted = Observable.Merge(
    this.ObserveProperty(o => o.DIProp1).Where(v => v != null),
    this.ObserveProperty(o => o.DIProp2).Where(v => v != null))
    .Skip(1) // もっといいやり方ありそう…
    .Subscribe(_ =>
    {
        // ここで初期化
    });
}

サスペンド対応

サスペンド時にReactivePropertyの値を保存したいときは、リストアしたくないReactivePropertyにIgnoreDataMember属性を付けたうえで、以下のようなプロパティをViewModelに定義しておくといい感じにしてくれます。

publicclass MainPageViewModel : ViewModel
{
    // リストア対象のReactivePropertypublic ReactiveProperty<string> Input { get; private set; }

    // リストア対象外のReactiveProperty
    [IgnoreDataMember]
    public ReactiveProperty<string> Output { get; private set; }

    // Prism for WinRTのリストアの仕組みに乗せるためのプロパティ
    [RestorableState]
    publicstring PackData
    {
        get { return SerializeHelper.PackReactivePropertyValue(this); }
        set { SerializeHelper.UnpackReactivePropertyValue(this, value); }
    }
}

Prism 5とReactiveProperty

$
0
0

Prism for WinRTとReactivePropertyの連携書いてみたので、WPFのPrism5での使用方法について書いてみようと思います。

参照についかするもの

以下のライブラリをNuGetから追加します。

  • ReactiveProperty
  • Prism.Mvvm
  • Prism.Interactivity

続いて、以下のアセンブリを参照設定から追加します。

  • System.Windows.Interactivity
  • Microsoft.Expression.Interactions

IInteractionRequestインターフェースの独自実装

ReactivePropertyになじむように、PrismのデフォルトのIInteractionRequestの実相ではなく、自前のTaskベースの実装を行います。といってもコールバックで受けてた続きの処理をTaskにするだけなので、そんなに難しくないです。

using Microsoft.Practices.Prism.Interactivity.InteractionRequest;
using System;
using System.Threading.Tasks;

namespace PrismRxApp.Mvvm
{
    // コールバックではなくTaskで続きの処理を受け取るようにするpublicsealedclass AsyncInteractionRequest<T> : IInteractionRequest
        where T : INotification
    {
        publicevent EventHandler<InteractionRequestedEventArgs> Raised;
        privatevoid OnRaised(InteractionRequestedEventArgs e)
        {
            var h = this.Raised;
            if (h != null)
            {
                h(this, e);
            }
        }

        public Task<T> RaiseAsync(T context)
        {
            var source = new TaskCompletionSource<T>();
            this.OnRaised(new InteractionRequestedEventArgs(context, () => source.SetResult(context)));
            return source.Task;
        }
    }
}

PrismのMessengerであるInteractionRequestは、IInteractionRequestを実装していれば何でもよくて、守るべき約束事はRaisedイベントを適時発行することだけです。なので、こういう風にTaskにくるんでやるのも楽でいいです。ここらへんの抽象化のさじ加減は、いい感じだなと思います。

アプリの作成

では、カウンターアプリでも作ってみようと思います。今回のカウンターは0より小さい値にはカウントダウンできない。10より大きい値は危険という感じのカウンターにしようと思います。危険な値でカウントアップする場合は、確認ダイアログを出そうと思います。

Modelの作成

ということでカウンタークラスを作成します。ちょっと条件が複雑になってきたのでコード長くなりますね。サンプルにしては。

using Microsoft.Practices.Prism.Mvvm;
using System;

namespace PrismRxApp.Models
{
    // 0以下にならない、10以上になると危険フラグがONになるカウンタクラスpublicclass Counter : BindableBase
    {
        privateint count;

        publicint Count
        {
            get { returnthis.count; }
            privateset { this.SetProperty(refthis.count, value); }
        }

        privatebool isDanger;

        publicbool IsDanger
        {
            get { returnthis.isDanger; }
            privateset { this.SetProperty(refthis.isDanger, value); }
        }

        privatebool canDecrement;

        publicbool CanDecrement
        {
            get { returnthis.canDecrement; }
            set { this.SetProperty(refthis.canDecrement, value); }
        }


        publicvoid Increment()
        {
            this.Count++;
            this.UpdateState();
        }

        publicvoid Decrement()
        {
            if (!this.CanDecrement)
            {
                thrownew InvalidOperationException();
            }

            this.Count--;
            this.UpdateState();
        }

        privatevoid UpdateState()
        {
            this.IsDanger = this.Count >= 10;
            this.CanDecrement = this.Count > 0;
        }
    }
}

ViewModelの作成

次に、ViewModelの作成です。こいつは、Incrementコマンド実行の処理がIsDangerがtrueのときはInteractionRequestを通じてViewに確認要求を出して、IsDangerがfalseのときは素通りして、CounterクラスのIncrementを呼び出すという点以外は特筆すべき点はないと思います。

using Codeplex.Reactive;
using Codeplex.Reactive.Extensions;
using Microsoft.Practices.Prism.Interactivity.InteractionRequest;
using PrismRxApp.Models;
using PrismRxApp.Mvvm;
using System;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;

namespace PrismRxApp.ViewModels
{
    publicclass MainWindowViewModel
    {
        privatereadonly Counter Model = new Counter();

        // 画面に出力するメッセージpublic ReactiveProperty<string> Message { get; private set; }

        // インクリメントに確認が必要な時にVに確認要求を投げるInteractionRequestpublic AsyncInteractionRequest<Confirmation> IncrementConfirmationRequest { get; private set; }

        // カウンターをインクリメントするpublic ReactiveCommand IncrementCommand { get; private set; }

        // カウンターをデクリメントするpublic ReactiveCommand DecrementCommand { get; private set; }

        public MainWindowViewModel()
        {
            // メッセージはカウンターの値を適当にフォーマッティングしたものthis.Message = this.Model
                .ObserveProperty(o => o.Count)
                .Select(i => string.Format("Counter value: {0}", i))
                .ToReactiveProperty();

            this.IncrementConfirmationRequest = new AsyncInteractionRequest<Confirmation>();

            this.IncrementCommand = new ReactiveCommand();
            this.IncrementCommand
                .SelectMany(
                    _ => Observable.If(
                        // インクリメントするのが危険なら
                        () => this.Model.IsDanger,
                        // 確認した結果のIO<bool>を返す
                        Observable.Defer(() => 
                            this.IncrementConfirmationRequest
                                .RaiseAsync(new Confirmation { Title = "confirmation", Content = "Increment?" })
                                .ToObservable()
                                .Select(c => c.Confirmed)
                        ),
                        // 普段は、trueを流す
                        Observable.Return(true)
                    )
                )
                // 結果がtrueの場合だけインクリメントする
                .Where(b => b)
                .Subscribe(_ =>
                {
                    this.Model.Increment();
                });
                
            // デクリメント可能なときだけデクリメントするthis.DecrementCommand = this.Model.ObserveProperty(o => o.CanDecrement).ToReactiveCommand();
            this.DecrementCommand.Subscribe(_ => this.Model.Decrement());

        }
    }
}

Viewの作成

Viewは、Prismの命名規約にしたがって自動でViewModelをDataContextに紐づけるためにIViewを実装して、ViewModelLocationProviderを使ってDataContextの設定を行うようにしています。

using Microsoft.Practices.Prism.Mvvm;
using System.Windows;

namespace PrismRxApp.Views
{
    /// <summary>/// MainWindow.xaml の相互作用ロジック/// </summary>publicpartialclass MainWindow : Window, IView
    {
        public MainWindow()
        {
            InitializeComponent();
            ViewModelLocationProvider.AutoWireViewModelChanged(this);
        }
    }
}

あとは、ちゃちゃっとXAML。ボタン2個とメッセージ表示用のテキストボックス。あとは、VMからの要求にこたえて確認ダイアログを出すBehaviorを仕込んでおきます。

<Windowxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:ViewModels="clr-namespace:PrismRxApp.ViewModels"xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"xmlns:Custom="http://www.codeplex.com/prism"mc:Ignorable="d"x:Class="PrismRxApp.Views.MainWindow"Title="MainWindow"Height="300"Width="300"d:DataContext="{d:DesignInstance {x:Type ViewModels:MainWindowViewModel}, IsDesignTimeCreatable=True}"><i:Interaction.Triggers><Custom:InteractionRequestTrigger SourceObject="{Binding IncrementConfirmationRequest, Mode=OneWay}"><Custom:PopupWindowAction IsModal="True"CenterOverAssociatedObject="True" /></Custom:InteractionRequestTrigger></i:Interaction.Triggers><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="145*"/><ColumnDefinition Width="147*"/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition/></Grid.RowDefinitions><Button Content="Increment"Grid.Row="1"Margin="5"Command="{Binding IncrementCommand, Mode=OneWay}"/><Button Content="Decrement"Grid.Column="1"Grid.Row="1"Margin="5"Command="{Binding DecrementCommand, Mode=OneWay}"/><TextBlock Grid.ColumnSpan="2"TextWrapping="Wrap"Text="{Binding Message.Value}"Margin="5"/></Grid></Window>

Prism 5から、PopupWindowActionというActionが追加されて、こいつがNotificationとConfirmationに対するデフォルトのWindowを表示する機能を持ってるので積極的に使っていきます。状況に応じてカスタムのWindowを出す機能もあります(使ったことないけど)

実行

実行してみます。カウンタ値が最初0なのでインクリメントだけできる画面が出てきます。

f:id:okazuki:20140509082433p:plain

インクリメントボタンを押していくとデクリメントもできるようになります。

f:id:okazuki:20140509082533p:plain

10以上でIncrementをすると確認ダイアログが出てきます。

f:id:okazuki:20140509082638p:plain

OKを押すとカウントアップされて、Cancelを押すとカウントアップされません。

プロジェクトファイル

一応プロジェクトは以下からダウンロードできます。


Windows Phone ArchでUniversal Windows app入門というタイトルで話してきました #wparch

$
0
0

Visual Studio Onlineにつないでたせいでオフライン環境でVisual Studioが拗ねてしまってデモがスムーズにいかなかったorz

発表資料は以下になります。

Visual Studio 2013 Update2 RTM出ました

Mvvm LightとPrismとで同じプログラムのサンプル書いてみました

Windows ストアアプリのボタンのマウスオーバー時の色をカスタマイズする方法

TypeScriptとAngularJS「Visual Studio用プロジェクトテンプレート」

$
0
0

毎回NuGetからAngularJSと型定義入れたりするのがめんどくさくなったので、プロジェクトテンプレート化しました。zipのまま、TemplatesのProjectTemplatesフォルダにつっこめば使えると思います。

Viewing all 1388 articles
Browse latest View live


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