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

UWP版Prismを使うとリリースビルド時にアプリが落ちる #windev_jp

$
0
0

という状況です。原因は端的にいうとこれです。

blog.okazuki.jp

https://connect.microsoft.com/VisualStudio/feedback/details/1925435/-windows-10-pcl-uwp-nullreferenceexception

別にPrismが悪いというわけではなくUWPからPCLを使うと起きる可能性があります。 Prismの処理を追っていくと、Prismが内部で起動シーケンスの進捗報告をしてるログ出力で、上記の原因に該当してました。

回避方法はLoggerを差し替えることです。 とりあえずログがいらないならAppクラスで以下の対応をすればOKです。

protectedoverride ILogger CreateLogger()
{
    returnnew EmptyLogger();
}

参ったものですね。個人的には.NET Nativeのバグだと思ってます。


UWPのGridViewのItemsSourceに設定可能なコレクションの条件

$
0
0

てっきりINotifyCollectionChangedとIEnumerableあたり実装してればいいのかと思ってたら違ってました…。

https://msdn.microsoft.com/library/windows/apps/windows.ui.xaml.controls.gridview.aspx

C# or Visual Basic apps should implement INotifyCollectionChanged and System.Collections.IList (not IList<T>). Virtualization requires both of these interfaces.

ということでINotifyCollectionChangedとIListの実装が必要でした。 独自コレクション作る人は要注意。

ReactiveProperty v2.2.7をリリースしました

$
0
0

www.nuget.org

リアルタイムフィルタリングコレクションのIFilteredReadOnlyObservableCollectionがUWPのGridViewのItemsSourceに設定しても反応しなかった問題に対処しました。 その関係で内部実装がちょろっと変わってます。

Windows 10のUWPでInkCanvasを使って手書き文字認識をする

$
0
0

思ったより簡単だったのでメモメモ。因みにWindows 8のころはInkManager使ったりしてちょっとめんどかったです。

InkCanvasを置く

描画や消したりとかはInkCanvasにお任せです。

<InkCanvas x:Name="myInkCanvas" />

認識処理を書く

認識にはInkRecognizerContainerを使います。RecognizeAsyncでInkCanvasのInkPresenterのStrokeContainerと、認識対象を示す引数を渡してやれば結果が返ってきます。結果は、InkRecognitionResultのListなのでループで回してGetTextCandidatesから認識候補を取り出してあれこれします。 とりあえず、以下のような雰囲気のコードになります。(とりあえず第一候補を連結してみた)

var irc = new InkRecognizerContainer();
var results = await irc.RecognizeAsync(this.myInkCanvas.InkPresenter.StrokeContainer, InkRecognitionTarget.All);
var dlg = new MessageDialog(results.Select(x => x.GetTextCandidates().First()).Aggregate((x, y) => x + y));
await dlg.ShowAsync();

結果はこんな感じです。認識できてますね。

f:id:okazuki:20151103104110p:plain

Surface Book買いました

$
0
0

Global Summit 2015に参加中なのですが、最後の1台でほしいモデルのSurface Bookがあるという情報を聞きつけて購入してしまいました。

その後も、最後の1台が出てきていろんな人が買ってみんなで開封の儀を執り行いました。(総額180万)

英語配列のキーボードなので、慣れないけれど、これから自分を慣らしていくつもり。

そのためのお試しBlog記事もかねてる

ReactivePropertyで、ViewModelのプロパティに変更があったかどうか判別できるようにしたい

$
0
0

プロパティの値が1つでも書き換わってたら判別したい。そういうことはありますよね? ReactivePropertyを使うと、こんな感じで実現できます。(変更があったかというフラグをリセットする機能つき)

まず、ReactivePropertyに変更があったときにtrueを発行するIObservableに変換するメソッドを作っておきます。

staticclass MvvmHelpersExtensions
{
    publicstatic IObservable<bool> ChangedAsObservable<T>(this ReactiveProperty<T> self, bool skipFirst = true)
    {
        var result = self.AsObservable();
        if (skipFirst)
        {
            result = result.Skip(1);
        }
        return result.Select(_ => true);
    }
}

このメソッドを使うと、ReactivePropertyに変更があったときにtrueがくるので、必要なプロパティの数だけMergeしてReadOnlyReactiveProperty(初期値false)あたりにしてしまえばOKです。そのとき、Subjectを渡して任意の値にセットできるようにしておくと、リセットにも対応できます。

class MountainViewModel
{
    private Subject<bool> ResetIsDirtySubject { get; } = new Subject<bool>();
    public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>();
    public ReactiveProperty<double> HeightInMeters { get; } = new ReactiveProperty<double>();
    public ReadOnlyReactiveProperty<bool> IsDirty { get; }

    public MountainViewModel()
    {
        this.IsDirty = Observable.Merge(
            this.Name.ChangedAsObservable(),
            this.HeightInMeters.ChangedAsObservable(),
            this.ResetIsDirtySubject)
            .ToReadOnlyReactiveProperty(false);
    }

    publicvoid ResetDirty()
    {
        this.ResetIsDirtySubject.OnNext(false);
    }
}

UWPのAPIをDesktopアプリから呼び出す

$
0
0

いろいろ手順があります。詳しくは以下のサイトを。

MSDN Blogs

で、細かい手順は置いといて上記のサイトにもある通りNuGetパッケージをインストールするだけでOKなようにしてくれてます。これは有難い。

NuGetから

Install-Package UwpDesktop

でパッケージをインストールしたらUWPのAPIが使えるようになってます。こんなコードを書いたらドキュメントライブラリのフォルダのファイル一覧がさくっととれます。

using System;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        staticvoid Main(string[] args)
        {
            NewMethod().Wait();
        }

        privatestatic async Task NewMethod()
        {
            var files = await Windows.Storage.KnownFolders.DocumentsLibrary.GetFilesAsync();
            foreach (var file in files)
            {
                Console.WriteLine(file.Name);
            }
        }
    }
}

とてもいいですね。

Windows 10 for IoTでHello worldまでの道のり

$
0
0

ラズパイ2をゲットしてきたので、ちょっとHello worldしてみたいと思います。UWPアプリの開発ができるVisual Studio 2015とWindows 10を用意しておくのが前提条件っぽいです。

Visual Studioの拡張機能と更新プログラムから、Windows 10 IoT Core Project Templatesをインストールします。

f:id:okazuki:20151108133053p:plain

デバイスの設定

ここから、Downlaod RTM Release for Raspberry Pi 2でISOイメージをゲットします。

ms-iot.github.io

DLすると中にISOイメージの中にmsiファイルが入ってるので、そいつでもろもろをインストールします。

C:\Program Files (x86)\Microsoft IoT\FFU\RaspberryPi2にflash.ffuというのがあって、これがどうもイメージっぽいです。

micro sd cardをPCにさしてWindows IoT Image Helperを立ち上げます。認識してるmicro sd cardを選択して、先ほど確認したffuファイルを選択してFlashボタンを押します。

こんな感じの画面が表示されるのでしばし待ちます。

f:id:okazuki:20151108134659p:plain

焼き終わったらRaspberry Pi 2の裏面にあるMicro SD Card入れにさくっとカードを入れます。

f:id:okazuki:20151108135137j:plain

LANケーブルを差し込みHDMIでディスプレイにつないで電源供給のためのmicroUSBを差し込んで待ちます。

ディスプレイにWindowsのロゴマークが出てきて…待つこと数分…。言語を選ぶ画面になるので慌ててマウスをつないで言語を選択します。

UWPアプリ開発

UWPアプリを作ってMainPage.xamlを以下のようにしました。

<Page x:Class="App1.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:local="using:App1"      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"      mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="Hello world"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   Style="{StaticResource HeaderTextBlockStyle}"/>
    </Grid>
</Page>

そして、プロジェクトのプロパティを選んでデバッグを選択します。プラットフォームをARMにして、ターゲットデバイスをリモート コンピュータにしてラズパイ2のIPアドレスを打ち込んで、認証を使用のチェックを外して実行します。

f:id:okazuki:20151108140929p:plain

暫くすると、ラズパイ2を繋いだディスプレイに以下のような感じの画面が表示されます。お手軽!

f:id:okazuki:20151108141247j:plain


ReactiveProperty v2.2.8をリリースしました

$
0
0

IReadOnlyReactivePropertyインターフェースを導入して、ReactivePropertyもこのインターフェースを実装するようにしました。まぁBCLのList系コントロールと同じようなパターンに従った感じです。

詳細は以下のIssueから。

github.com

nugetのページは以下になります。

www.nuget.org

Knockout.jsを入門してみた

$
0
0

ちょっとJavaScriptのフレームワークを使おうかなと思ったのでどれを使おうか選んでたのですが、最近のreact.jsや、AngularJSや、Cycle.jsとかもいいですが以下の理由でKnockout.jsにしてみようと思いました。

  • 枯れてる
  • メンテナンスされ続けてる
  • 学習コストが低い
  • TypeScriptと相性がよさそうに見えた

特に半年前のフレームワークって何処行ったの??っていう状況になってるような気がするJavaScript界においてこれだけ長期間安定して提供され続けてるという点が個人的に評価ポイントとして高かったです。

Visual Studioでの使い方

ということでVSから使ってみようと思います。ASP.NETのプロジェクトを作ってMVCにだけチェック入れてEmptyのプロジェクトを作ります。

ライブラリの導入

NuGetからknockoutjsをインストールします。続けてknockout.typescriptで検索して型定義を入れます。

Viewの作成

HomeControllerを作ってIndexのViewをさくっと作ります。HomeControllerのIndexメソッドで右クリックしてViewの追加からやるとスムーズです。

Views/Shared/_Layout.cshtmlのJavaScriptに以下のようにknockoutを読み込むようにします。

<!DOCTYPE html><html><head><metacharset="utf-8" /><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>@ViewBag.Title - My ASP.NET Application</title><linkhref="~/Content/Site.css"rel="stylesheet"type="text/css" /><linkhref="~/Content/bootstrap.min.css"rel="stylesheet"type="text/css" /><scriptsrc="~/Scripts/modernizr-2.6.2.js"></script><scriptsrc="~/Scripts/knockout-3.3.0.js"></script>></head><body><divclass="navbar navbar-inverse navbar-fixed-top"><divclass="container"><divclass="navbar-header"><buttontype="button"class="navbar-toggle"data-toggle="collapse"data-target=".navbar-collapse"><spanclass="icon-bar"></span><spanclass="icon-bar"></span><spanclass="icon-bar"></span></button>
                @Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div><divclass="navbar-collapse collapse"><ulclass="nav navbar-nav"></ul></div></div></div><divclass="container body-content">
        @RenderBody()
        <hr /><footer><p>&copy; @DateTime.Now.Year - My ASP.NET Application</p></footer></div><scriptsrc="~/Scripts/jquery-1.10.2.min.js"></script><scriptsrc="~/Scripts/bootstrap.min.js"></script></body></html>

Scriptの作成

Scriptsフォルダにindex.tsという名前でTypeScriptのファイルを作ります。ここにいろいろ書いていきます。

とりあえず、以下のページのサンプルをさくっと書いてみようと思います。

kojs.sukobuto.com

Hello world

TypeScriptはこんな感じで。

/// <reference path="typings/knockout/knockout.d.ts" />

module ViewModels {
    export class HelloWorldViewModel {
        public firstName: KnockoutObservable<string>;
        public lastName: KnockoutObservable<string>;
        public fullName: KnockoutComputed<string>;

        constructor(firstName: string, lastName: string) {
            this.firstName = ko.observable(firstName);
            this.lastName = ko.observable(lastName);
            this.fullName = ko.computed(() => this.firstName() + this.lastName())
        }
    }
}

window.onload = () => ko.applyBindings(new ViewModels.HelloWorldViewModel("Planet", "Earth"));

Index.cshtmlはこんな感じで。

@{
    ViewBag.Title = "Index";
}

<p>ファーストネーム:<inputdata-bind="value: firstName" /></p><p>ラストネーム:<inputdata-bind="value: lastName" /></p><h2>Hello, <spandata-bind="text: fullName"></span>!</h2><scriptsrc="~/Scripts/index.js"></script>

クリックカウンター

TypeScriptはこんな感じ。

/// <reference path="typings/knockout/knockout.d.ts" />

module ViewModels {exportclass ClickCounterViewModel {public numberOfClicks: KnockoutObservable<number> = ko.observable(0);

        public registerClick(): void {this.numberOfClicks(this.numberOfClicks() + 1);
        }public resetClicks(): void {this.numberOfClicks(0);
        }public hasClickedTooManyTimes: KnockoutComputed<boolean> = ko.computed(() => {returnthis.numberOfClicks() >= 5;
        }, this);
    }}window.onload = () => ko.applyBindings(new ViewModels.ClickCounterViewModel());

HTMLはこんな感じ。

@{
    ViewBag.Title = "Index";
}

<div>クリック回数 <spandata-bind="text: numberOfClicks"></span></div><buttondata-bind="click: registerClick, disable: hasClickedTooManyTimes">クリック</button><divdata-bind="visible: hasClickedTooManyTimes">クリックしすぎ。
    <buttondata-bind="click: resetClicks">リセット</button></div><scriptsrc="~/Scripts/index.js"></script>

シンプルなリスト

TypeScriptはこんな感じ。

/// <reference path="typings/knockout/knockout.d.ts" />

module ViewModels {exportclass SimpleListViewModel {public items: KnockoutObservableArray<string>;
        public itemToAdd: KnockoutObservable<string> = ko.observable("");
        constructor(items: string[]) {this.items = ko.observableArray(items);
        }public addItem(): void {this.items.push(this.itemToAdd());
            this.itemToAdd("");
        }}}window.onload = () => ko.applyBindings(new ViewModels.SimpleListViewModel(["Alpha", "Beta", "Gamma"]));

HTMLはこんな感じ。

@{
    ViewBag.Title = "Index";
}

<formdata-bind="submit: addItem">新しいアイテム
    <inputdata-bind="value: itemToAdd" /><buttondata-bind="click: addItem">追加</button><p>アイテム一覧</p><selectmultiple="multiple"data-bind="options: items"></select></form><scriptsrc="~/Scripts/index.js"></script>

リストを改良する

ちょっとめんどくさくなってきたので手抜きでこんな感じに。

/// <reference path="typings/knockout/knockout.d.ts" />

module ViewModels {exportclass SimpleListViewModel {public items: KnockoutObservableArray<string>;
        public selectedItems: KnockoutObservableArray<string>;
        public itemToAdd: KnockoutObservable<string> = ko.observable("");
        constructor(items: string[]) {this.items = ko.observableArray(items);
            this.selectedItems = ko.observableArray(items.slice(0, 1));
        }public addItem(): void {this.items.push(this.itemToAdd());
            this.itemToAdd("");
        }public removeSelected(): void {this.items.removeAll(this.selectedItems());
            this.selectedItems([]);
        }public sort(): void {this.items.sort();
        }}}window.onload = () => ko.applyBindings(new ViewModels.SimpleListViewModel(["Alpha", "Beta", "Gamma"]));

HTMLのほうはこんな感じに。

@{
    ViewBag.Title = "Index";
}

<formdata-bind="submit: addItem">新しいアイテム
    <inputdata-bind="value: itemToAdd" /><buttondata-bind="click: addItem">追加</button><p>アイテム一覧</p><selectmultiple="multiple"data-bind="options: items, selectedOptions: selectedItems"></select><buttondata-bind="click: removeSelected, enable: selectedItems().length > 0">削除</button><buttondata-bind="click: sort, enable: items().length > 0">ソート</button></form><scriptsrc="~/Scripts/index.js"></script>

コレクションを操る

段々めんどくさくなってきたので$rootとかは省略

/// <reference path="typings/knockout/knockout.d.ts" />

module ViewModels {exportclass Person {public children: KnockoutObservableArray<string>;

        constructor(public name: string, children: string[]) {this.children = ko.observableArray(children);
        }public addChild(): void {this.children.push("新しいお子様");
        }}exportclass ViewModel {public people: Person[];

        constructor() {this.people = [new Person("Annabella", ["Arnie", "Anders", "Apple"]),
                new Person("Bertie", ["Arnie", "Anders", "Apple"]),
                new Person("Charles", ["Arnie", "Anders", "Apple"])
            ];
        }}}window.onload = () => ko.applyBindings(new ViewModels.ViewModel());

HTML

@{
    ViewBag.Title = "Index";
}

<uldata-bind="foreach: people"><li><div><spandata-bind="text: name"></span>さんの<spandata-bind="text: children().length"></span>人のお子様
            <ahref="#"data-bind="click: addChild">また産まれた!</a><uldata-bind="foreach: children"><li><spandata-bind="text: $data"></span></li></ul></div></li></ul><scriptsrc="~/Scripts/index.js"></script>

力尽きた

とりあえず、眠たくなってきたので今日はここまで。

かっこいいネットワーク図?を描くためのvis.js

$
0
0

グラフとかっていうんですかね? vis.jsというライブラリを使うと簡単に描けるっぽいです。

vis.js - A dynamic, browser based visualization library.

使い方

ASP.NETアプリケーションにサイトからDL出来るvis.min.jsとvis.min.cssを追加します。

<linkhref="~/Content/vis.min.css"rel="stylesheet" /><scriptsrc="~/Scripts/vis.min.js"></script>

こんな感じで_Layout.cshtmlのheadの中に追加しておきます。

あとは、vis.DataSetというのでデータとデータの関連を用意して、vis.Networkを作るだけです。


@{
    ViewBag.Title = "Index";
}

<h2>Index</h2><divid="networkmap"style="width: 100%; height: 500px;"></div><scripttype="text/javascript">var nodes = new vis.DataSet([{ id: 1, label: "Name 1"},{ id: 2, label: "Name 2"},{ id: 3, label: "Name 3"},{ id: 4, label: "Name 4"},{ id: 5, label: "Name 5"},{ id: 6, label: "Name 6"}]);var edges = new vis.DataSet([{ from: 1, to: 2},{ from: 1, to: 3},{ from: 1, to: 4},{ from: 4, to: 2},{ from: 5, to: 3},{ from: 6, to: 4}]);var data = { nodes: nodes, edges: edges };var options = {};var network = new vis.Network(document.getElementById("networkmap"), data, options);</script>

これだけで、以下のような図が描かれます。

f:id:okazuki:20151113214458p:plain

こういうネットワーク図以外にもいろいろな図に対応してるみたいなのでちょと見てみるといいかもしれません。

Surface Bookのタッチパッドの操作方法いろいろ

$
0
0

Surface Bookのタッチパッド色々な操作が割り当てられてるので気づいたものをメモしておきます。

1本指

1本指は、タップすることでクリックになります。 あとは、スライドさせることでマウスカーソルの移動です。普通ですね。

2本指

2本指はタップすることで、右クリックになります。 スライドさせることで、スクロールさせることができます。上下と左右に対応してます。

3本指

3本指はタップすることで、コルタナさんが起動します。 上から下にスライドすることで、デスクトップを表示、下から上にスライドすることでアプリと仮想デスクトップを切り替える画面になります。 左右にスライドすることで、アプリの切り替えができます。

4本指

4本指はタップすることで、アクションセンターが開きます。 それだけ。

5本指

何もない。

まとめ

Surface Bookのタッチパッドは、手のひらで触ると反応しなくて本当に指先を認識して動作してたりします。なのでタイピング中に誤動作するということに今のところ遭遇したことがありません。とてもかしこい!

ReactivePropertyのリポジトリにひっそりTODOアプリのサンプル追加しました

$
0
0

ひっそりと…

github.com

TodoMVCというサイトにあるサンプルに似た雰囲気にしてみました。

f:id:okazuki:20151117082817p:plain

Serviceクラスの存在意義が無い気がしてるので消すかも。

Windows 10 ver1511でスナップが便利になってる件について

$
0
0

先日きたWindows 10のアップデートで個人的に一番気に入ってる機能です。

画面を左と右にスナップします。

f:id:okazuki:20151117203520p:plain

その状態で、ウィンドウの横幅を変更しようとすると、うっすらと黒いバーみたいなのが出てきます。

f:id:okazuki:20151117203635p:plain

この状態でリサイズすると、左右にスナップされてるウィンドウが両方リサイズされます!

f:id:okazuki:20151117203818p:plain

(Aristeaのリサイズをしただけで、Edgeもリサイズされてる)

これは便利になったというかWindows 8.1の頃の使い勝手に戻ったというか、まぁ個人的に好みの挙動です!

Windows store for Businessが出ましたね

$
0
0

日本語では「ビジネス向け Windows ストア」というみたいです。

ビジネス向け Microsoft Windows ストア

企業向けアカウント(私の場合Office 365のアカウント使ってみました)でサインインすると以下のような感じの画面が出ます。

f:id:okazuki:20151117205100p:plain

設定のLOBパブリッシャーを選ぶことで、LOBアプリの開発者を招待することができます。(ここは組織外の開発者でもOK)

f:id:okazuki:20151117205253p:plain

招待された人には「LOBアプリを公開するように依頼されています」というタイトルのメールが届きます。Windows デベロッパーセンターのアカウント設定のところに企業団体という謎のメニューが増えてるので、そこでOKをすると、こんな感じになります。

f:id:okazuki:20151117210534p:plain

ここまでくるとアプリの提出のときに、価格と使用可能状況で分布と認知度の項目で基幹業務(LOB)配布という項目が選べるようになります。これを選ぶと、どの企業に対して配るのか設定ができるようになります。

f:id:okazuki:20151117211216p:plain

これで提出して審査をパスすると企業向けのアプリとして公開できるみたい(まだそこまでは出来てない)みたいです。

多分、管理者の在庫あたり(この翻訳どうなの)から色々できそうな気がします。

f:id:okazuki:20151117212057p:plain

とりあえず、わかってることだけ書きました。


Windows store for Businessからアプリをインストールしてみた

$
0
0

前回は、アプリの提出までやってみました。

blog.okazuki.jp

WACKは通らないといけないみたいですが、無事真っ白なアプリケーションが審査を通過してWindows store for Businessの新しいLOBアプリの所にアプリが追加されました。(スクリーンショットとるの忘れた)

f:id:okazuki:20151118192756p:plain

...をクリックするとインベントリに追加というメニューがあるので、これをクリックすると在庫(この翻訳どうよ)にアプリが追加されます。

f:id:okazuki:20151118192949p:plain

ここでも、...をクリックすることでプライベートストアに追加することができます。

f:id:okazuki:20151118193055p:plain

ステータスが追加中になります。

f:id:okazuki:20151118193223p:plain

1日ほどまつとステータスがプライベートストアにあるに変わります。

f:id:okazuki:20151119215139p:plain

そうするとプライベートストアから落とせるようになります。プライベートストアにアクセスするには、ストアのアプリを立ち上げてアカウント(検索ボックスの左のユーザーのアイコン)をクリックしてアカウントの名前が表示されてるところをクリックします。

f:id:okazuki:20151119215327p:plain

そうするとアカウントの追加画面になるので、企業のアカウントを追加します。アカウントを追加すると、アカウントが2つ表示されるようになるのと、ストアのタブの並びに企業名が表示されるようになります。

f:id:okazuki:20151119215534p:plain

企業名をクリックすると、LOBアプリが表示されます。

f:id:okazuki:20151119215621p:plain

クリックするとインストールができる画面に遷移します。

f:id:okazuki:20151119215705p:plain

あとはインストールボタンを押すと普通にインストールされてアプリが使えるようになります。

f:id:okazuki:20151119215800p:plain

無事ストアに追加した真っ白アプリが使えるようになりました。

ストアに審査を出してから、プライベートストアに追加できるまで最短で2.5日くらいかかりそうなので、それくらいの余裕をもってやらないといけなさそうです。

コルタナさんでアプリを起動する

$
0
0

「コルタナさん、サンプルアプリ起動」といったらアプリが起動するそんな感じです。

コルタナさんは、Voice Command Fileというので音声に対して何をするか定義します。アプリを作ってVoiceCommands.xmlとかいう名前でファイルを作って以下のようなXMLを定義します。

<?xml version="1.0" encoding="utf-8"?><VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2"><CommandSet xml:lang="ja-jp"Name="HolCommandSet_ja-jp"><CommandPrefix>サンプルアプリ</CommandPrefix><Example>起動</Example><Command Name="LaunchApp"><Example>起動</Example><ListenFor>起動</ListenFor><Feedback>サンプルアプリを起動しています</Feedback><Navigate /></Command></CommandSet></VoiceCommands>

xmlnsを定義したら、インテリセンスがきくのでいい感じで書けます。CommandSetにlangでja-jpを定義すると日本語のものになります。

CommandにListenForで指定した文字列が来たら何をするという感じで定義します。Navigateタグはアプリを起動するという定義になります。

このVoice Command Fileは、なんとプログラムで登録します。ということで、アプリが最低でも1度は起動して登録処理を走らせないといけないってことになります。OnLaunchedの先頭に以下のコードを書いて登録します。

var storageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///VoiceCommands.xml"));
await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(storageFile);

因みに名前空間書いてませんのでCtrl + .あたりで名前空間はusingしてください。

こうしてVoice Command Fileが登録されると、ついにコルタナさん対応ができます。コルタナさんからアプリが起動されるとOnActivatedが呼ばれます。こんな感じのコードを書くとコルタナさんから起動したときの処理が書けます。

protectedoverridevoid OnActivated(IActivatedEventArgs args)
{
    var rootFrame = Window.Current.Content as Frame;
    if (rootFrame == null)
    {
        rootFrame = new Frame();
        rootFrame.NavigationFailed += this.OnNavigationFailed;

        Window.Current.Content = rootFrame;
    }

    if (args.Kind == ActivationKind.VoiceCommand)
    {
        this.HandleVoiceCommand(args as VoiceCommandActivatedEventArgs, rootFrame);
    }

    Window.Current.Activate();
    base.OnActivated(args);
}

privatevoid HandleVoiceCommand(VoiceCommandActivatedEventArgs args, Frame rootFrame)
{
    var result = args.Result;
    var command = result.Text;
    var voiceCommandName = result.RulePath[0];
    Debug.WriteLine($"command {command}, voiceCommandName {voiceCommandName}");
    if (voiceCommandName == "LaunchApp")
    {
        rootFrame.Navigate(typeof(VoiceCommandPage));
    }
}

これでコルタナさんに「サンプルアプリ起動」と話しかけるとアプリが起動します。以外とお手軽ではありますね。

ReactiveProperty + Prism.Wpfでリストに表示されてる項目を編集するサンプルを書いてみた

$
0
0

github.com

ListBoxに表示されてる項目を選択してボタンを押すとポップアップが出てきて編集するっていう簡単なサンプルです。

f:id:okazuki:20151121221516p:plain

ReactivePropertyでModelに紐づくViewModelの作り方のパターン

$
0
0

データの入れ物のModelに対するViewModelは大体こんな感じになるよねっていう感じのものを作ってみました。

とりあえず以下のようなイメージです。

  • ModelはINotifyPropertyChangedを実装した値の入れ物のクラス
  • ViewModelはそれをラップしてVからの入力値を保持するクラス
  • ViewModelでは入力値の検証を行う
  • ViewModelではModelのプロパティが変わったらそれを直ちに反映する
  • ViewModelではVからの入力値にエラーがない状態でCommitメソッドを呼ばれたらModelに値を書き戻す

まず、値の入れ物のクラスを作ります。

class Person : BindableBase
{
    privateint age;

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

    privatestring name;

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

ViewModelはこんな感じ。

class PersonViewModel : IDisposable
{
    private CompositeDisposable Disposable { get; } = new CompositeDisposable();

    public Person Model { get; }

    [Required]
    public ReactiveProperty<string> Name { get; }

    [Required]
    [RegularExpression("[0-9]+")]
    [Range(0, 100)]
    public ReactiveProperty<string> Age { get; }

    private ReactiveProperty<bool> hasErrors;
    public ReactiveProperty<bool> HasErrors =>
        hasErrors ?? (hasErrors = Observable.CombineLatest(
            this.Name.ObserveHasErrors,
            this.Age.ObserveHasErrors)
            .Select(x => x.Any(y => y))
            .ToReactiveProperty());

    private Subject<Unit> CommitTrigger { get; } = new Subject<Unit>();

    private IObservable<Unit> CommitAsObservable =>
        this.CommitTrigger
            .Where(_ => !this.HasErrors.Value);

    public PersonViewModel(Person model)
    {
        this.Model = model;

        // Name propertythis.Name = this.Model
            .ObserveProperty(x => x.Name)
            .ToReactiveProperty()
            .SetValidateAttribute(() => this.Name)
            .AddTo(this.Disposable);
        this.CommitAsObservable
            .Select(_ => this.Name.Value)
            .Subscribe(x => this.Model.Name = x)
            .AddTo(this.Disposable);

        // Age propertythis.Age = this.Model
            .ObserveProperty(x => x.Age)
            .Select(x => x.ToString())
            .ToReactiveProperty()
            .SetValidateAttribute(() => this.Age)
            .AddTo(this.Disposable);
        this.CommitAsObservable
            .Select(_ => this.Age.Value)
            .Select(x => int.Parse(x))
            .Subscribe(x => this.Model.Age = x)
            .AddTo(this.Disposable);
    }

    publicvoid Commit()
    {
        this.CommitTrigger.OnNext(Unit.Default);
    }

    publicvoid Dispose()
    {
        this.Disposable.Dispose();
    }
}

M -> VMのプロパティの書き換えはObserveProperty -> ToReactivePropertyでやっています。基本ですね。SetValudateAttributeを使ってDataAnnnotationsのデータ検証を有効化しています。

次にVMのReactivePropertyにエラーがあるかどうかを返すHasErrorsというReactivePropertyを定義しています。これは、各プロパティのObserveHasErrorsをCombineLatestで結合して1つでもTrueがあったらTrueになるようにしています。

private ReactiveProperty<bool> hasErrors;
public ReactiveProperty<bool> HasErrors =>
    hasErrors ?? (hasErrors = Observable.CombineLatest(
        this.Name.ObserveHasErrors,
        this.Age.ObserveHasErrors)
        .Select(x => x.Any(y => y))
        .ToReactiveProperty());

次に、値をVMからMに書き戻すためのきっかけとなるタイミングを制御するIObservableを作ります。これは以下のようにSubjectで発火したものをエラーが無いときだけ素通りさせるという感じでいきます。

private Subject<Unit> CommitTrigger { get; } = new Subject<Unit>();

private IObservable<Unit> CommitAsObservable =>
    this.CommitTrigger
        .Where(_ => !this.HasErrors.Value);

このCommitAsObservableに値が流れてきたときにVM -> Mへの値の書き戻し処理を書きます。例としてAgeプロパティを。

// Age propertythis.Age = this.Model
    .ObserveProperty(x => x.Age)
    .Select(x => x.ToString())
    .ToReactiveProperty()
    .SetValidateAttribute(() => this.Age)
    .AddTo(this.Disposable);
this.CommitAsObservable
    .Select(_ => this.Age.Value)
    .Select(x => int.Parse(x))
    .Subscribe(x => this.Model.Age = x)
    .AddTo(this.Disposable);

こんな感じでM -> VMとVM -> Mの値の移し替え処理を近いところに定義できるので個人的には書き忘れ起きにくいと思ってるので気に入ってます。

最後に、CommitでCommitTriggerに値を流します。

publicvoid Commit()
{
    this.CommitTrigger.OnNext(Unit.Default);
}

これで、Commitを呼ぶとエラーのないときだけ、VM -> Mへの値の書き戻し処理が走るようになります。

まだちょっとめんどいかな。

UWP版Prismで、ルートがFrameじゃない構成のアプリを作りたい

$
0
0

UWPアプリとかストアアプリって普通はWindow.Current.ContentはFrameになるのが一般的です。 ただ、SplitViewを使ったアプリとかは、SplitViewの右側にFrameを置いて、Window.Current.ContentにはMainPageを置くということをやったりします。自分で作ってるアプリだとApp.xaml.csで色々出来るのですが、Prismを使った場合はPrismApplicationクラスが起動シーケンスを制御するので、好きにはできません。

一応PrismApplicationにはCreateShellというWindow.Current.Contentに設定するものを作るためのメソッドが用意されてるので、それを使えば出来ます。

<Page x:Class="App15.Views.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:local="using:App15.Views"      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"      mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <SplitView x:Name="FrameContainer"                   x:FieldModifier="public">

        </SplitView>
    </Grid>
</Page>

こんな感じでMainPageを用意しておいてAppクラスで以下のようにします。

using App15.Views;
using Prism.Windows;
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace App15
{
    sealedpartialclass App : PrismApplication
    {
        public App()
        {
            this.InitializeComponent();
        }

        protectedoverride UIElement CreateShell(Frame rootFrame)
        {
            var mainPage = new MainPage();
            mainPage.FrameContainer.Content = rootFrame;
            return mainPage;
        }

        protectedoverride Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
        {
            this.NavigationService.Navigate("Init", null);
            return Task.CompletedTask;
        }
    }
}

これでいい感じにできます。

Viewing all 1388 articles
Browse latest View live


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