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

めとべや東京 #4の資料公開。「ユニバーサルWindowsアプリ入門」 #めとべや東京


AngularJSとTypeScript「Directiveのハローワールド」

$
0
0

AngularJSのかなり強力なディレクティブという機能のハローワールドしてみました。こいつAngularJSでDOM操作する処理をカプセル化する素敵なやつです。とりあえず、JavaScriptではなくてTypeScriptでは、どういう風にかくのかというのをつかむために、Hello worldを書いてみようと思います。

JSでのディレクティブとかについては、以下のサイトがとても参考になります。

ディレクティブの作り方

ディレクティブが持つべきプロパティを定義したng.IDirectiveインターフェースを実装します。linkとかが型指定で定義されているので、まぁ割と安心ですね。たとえば、こていの テンプレートを流し込むだけのデイxれくてぃぶなら以下のように定義できます。

/// <reference path="scripts/typings/angularjs/angular.d.ts" />

module TypeScriptAngularJSApp1 {
    // Hello worldと挿入するだけのディレクティブclass HelloWorldDirective implements ng.IDirective {
        template = "<p>Hello world</p>";
        replace = true;
    }

    // okazuki-HelloWorldで指定できるように登録する。// 第二引数は、ラムダ式で、先ほど作ったクラスをnewして返す。
    angular.module("myApp", [])
        .directive("okazukiHelloWorld", () => new HelloWorldDirective());
}

あとは、HTMLで使うだけです。何もないdivタグにokazuki-hello-worldを追加しています。

<!DOCTYPE html><htmllang="ja" ng-app="myApp"><head><metacharset="utf-8" /><title>TypeScript HTML App</title><linkrel="stylesheet"href="app.css"type="text/css" /><scriptsrc="Scripts/angular.min.js"></script><scriptsrc="app.js"></script></head><body><div okazuki-hello-world></div></body></html>

実行すると、ちゃんとディレクティブが動いていることがわかります。

f:id:okazuki:20140601143056p:plain

TypeScriptとAngularJS「コントローラ起点の画面遷移」

$
0
0

ng.ILocationServiceを使います。

ルートの定義

$locationでng.ILocationServiceをコントローラにインジェクションできるので、そいつのpathメソッドにURLを指定することで画面遷移が出来ます。

/// <reference path="scripts/typings/angularjs/angular.d.ts" />/// <reference path="scripts/typings/angularjs/angular-route.d.ts" />

module TypeScriptAngularJSApp2 {
    // Page1用のスコープinterface Page1Scope extends ng.IScope {
        title:string;
        navigate(): void;
    }

    // Page1用のコントローラclass Page1Ctrl {
        constructor($scope: Page1Scope, $location: ng.ILocationService) {
            // タイトルと画面遷移を行う処理を定義する
            $scope.title = "Page1";
            $scope.navigate = () => {
                $location.path("/Page2");
            };
        }
    }

    // Page1と基本同じなのでコメントは省略interface Page2Scope extends ng.IScope {
        title:string;
        navigate(): void;
    }

    class Page2Ctrl {
        constructor($scope: Page2Scope, $location: ng.ILocationService) {
            $scope.title = "Page2";
            $scope.navigate = () => {
                $location.path("/Page1");
            }
        }
    }

    // モジュールを定義。ルート定義をするのでngRouteを忘れずに
    angular.module("MyApp", ["ngRoute"])
        // 上で作ったコントローラの登録// $locationで画面遷移を行うILocationServiceをインジェクションする
        .controller("Page1Ctrl", ["$scope", "$location", Page1Ctrl])
        .controller("Page2Ctrl", ["$scope", "$location", Page2Ctrl])
        // ルートの定義
        .config(["$routeProvider", ($routeProvider: ng.route.IRouteProvider) => {
            $routeProvider
                .when("/Page1", {
                    controller:"Page1Ctrl",
                    templateUrl:"views/page1.html"
                })
                .when("/Page2", {
                    controller:"Page2Ctrl",
                    templateUrl:"views/page2.html"
                })
                .when("/", {
                    controller:"Page1Ctrl",
                    templateUrl:"views/page1.html"
                });
        }]);
}

ルート定義については、前に書いたのでそちらを見てください。

ng.ILocationServiceを使った画面遷移での画面間データ渡し

$
0
0

とりあえずパラメータを使うのが一番楽そうです。パラメータはng.ILocationServiceのsearchメソッドにオブジェクトを渡してやるといい感じに面倒を見てくれます。

前回の処理に、ページ間のデータ渡しをコントローラに追加したコードは以下のような感じになります。

// Page1用のスコープinterface Page1Scope extends ng.IScope {
    title:string;
    navigate(): void;
}

// Page1用のコントローラclass Page1Ctrl {
    constructor($scope: Page1Scope, $location: ng.ILocationService) {
        // タイトルと画面遷移を行う処理を定義する
        $scope.title = "Page1";
        $scope.navigate = () => {
            $location.path("/Page2").search({key: Date()});;
        };
    }
}

// Page1と基本同じなのでコメントは省略interface Page2Scope extends ng.IScope {
    title:string;
    navigate(): void;
}

class Page2Ctrl {
    constructor($scope: Page2Scope, $location: ng.ILocationService) {
        $scope.title = "Page2 - " + $location.search().key;
        $scope.navigate = () => {
            $location.path("/Page1");
        }
    }
}

AngularJSとTypeScript「サービスを共有してページ間のデータ共有」

$
0
0

サービスのインスタンスは1つ。ということは、同じサービスをページ間で共有すればデータを渡すことが出来るということです。ということでやってみました。

/// <reference path="scripts/typings/angularjs/angular.d.ts" />/// <reference path="scripts/typings/angularjs/angular-route.d.ts" />

module TypeScriptAngularJSApp2 {
    // データ共有用のサービスclass SampleService {
        key:string;
    }

    // Page1用のスコープinterface Page1Scope extends ng.IScope {
        title:string;
        navigate(): void;
    }

    // Page1用のコントローラclass Page1Ctrl {
        constructor($scope: Page1Scope, $location: ng.ILocationService, sampleService: SampleService) {
            // タイトルと画面遷移を行う処理を定義する
            $scope.title = "Page1";
            $scope.navigate = () => {
                sampleService.key = Date();
                $location.path("/Page2");
            };
        }
    }

    // Page1と基本同じなのでコメントは省略interface Page2Scope extends ng.IScope {
        title:string;
        navigate(): void;
    }

    class Page2Ctrl {
        constructor($scope: Page2Scope, $location: ng.ILocationService, sampleService: SampleService) {
            $scope.title = "Page2 - " + sampleService.key;
            $scope.navigate = () => {
                $location.path("/Page1");
            }
        }
    }

    // モジュールを定義。ルート定義をするのでngRouteを忘れずに
    angular.module("MyApp", ["ngRoute"])
        // サービスの登録
        .factory("sampleService", () => new SampleService())
        // 上で作ったコントローラの登録// $locationで画面遷移を行うILocationServiceをインジェクションする
        .controller("Page1Ctrl", Page1Ctrl)
        .controller("Page2Ctrl", Page2Ctrl)
        // ルートの定義
        .config(($routeProvider: ng.route.IRouteProvider) => {
            $routeProvider
                .when("/Page1", {
                    controller:"Page1Ctrl",
                    templateUrl:"views/page1.html"
                })
                .when("/Page2", {
                    controller:"Page2Ctrl",
                    templateUrl:"views/page2.html"
                })
                .when("/", {
                    controller:"Page1Ctrl",
                    templateUrl:"views/page1.html"
                });
        });
}

ソースコードをminifyして、パラメータ名が変わらなかったら、パラメータ名で勝手にDIしてくれるみたいなので、今回はそれを使ってコントローラにスコープやらなんやらをインジェクションしてもらうようにしています。

ルート定義のresolve

$
0
0

ルート定義のwhenメソッドの第二引数のresolveでは、コントローラに注入したいデータを取得する処理を定義することが出来ます。

/// <reference path="scripts/typings/angularjs/angular.d.ts" />/// <reference path="scripts/typings/angularjs/angular-route.d.ts" />

module TypeScriptAngularJSApp2 {
    angular.module("MyApp", ["ngRoute"])
        // コントローラの定義。value1とvalue2をresolveから注入してもらう// 今回はコントローラの定義は手抜き。
        .controller("Page1Ctrl", ($scope, value1, value2) => {
            $scope.value = value1 + value2;
        })
        .config(($routeProvider: ng.route.IRouteProvider) => {
            $routeProvider
                .when("/", {
                    // Page1Ctrlにresolveでvalue1とvalue2をresolveで取得してインジェクションする// 見た目はviews/page1.html                    templateUrl:"views/page1.html",
                    // コントローラはPage1Ctrol                    controller:"Page1Ctrl",
                    // コントローラに設定する値を決める処理を書く                    resolve: {
                        value1: () => "Hello",
                        value2: () => "World"
                    }
                });
        });
}

この例だとresolveに指定しているvalue1とvalue2の関数の結果がコントローラのvalue1とvalue2にわたってきます。コントローラ内では、単純に足してるだけなので、最終的な結果はHelloWorldという文字列になるという寸法です。

views/page1.htmlに{{value}}と書いておけば表示されますが、ここでは結果とHTMLは省略します。

不可解なエラーのとき確認するポイント

$
0
0

ただのメモです。 AngularJS使っててエラーがでたら以下の点を確認すること。

  • 必要なモジュールは読み込まれているか
    • angujar.module("hoge", [ここを確認])
  • 必要なjsファイルは、ちゃんとページに読み込まれているか
  • typoしてないか
    • $routeProviderを$rootProviderって書いて数十分悩んだ

なんというか、エラーメッセージをもうちょいわかりやすくしてほしいという点と、文字列ベースでのDIはつらいな…。

TypeScript + AngularJS + ASP.NET WebAPI + ASP.NET Identity + Form認証でサインイン処理してみよう

$
0
0

カオスな感じですが、AngularJSを使ってForm認証を行うサンプルを作成しました。ちょっと自信がないので、突っ込み歓迎です…。

基本的にはASP.NET Identityでユーザー引き当てて、Form認証のチケットを作成するだけのアプリになります。画面側は、AngularJSとTypeScriptでSPAとして実装しました。OAuthとかで認証したほうがかっこいいんでしょうが、今の実力ではForm認証でCookieに認証チケット発行するのが精いっぱいでしたorz


TypeScript + AngularJS「リソースを使う」

$
0
0

結構めんどくさい。とりあえず動いたのでメモ。

  • ng.resource.IResourceを継承したものを作る
  • プロパティを生やす
  • IResourceServiceからIResourceClassを作る
  • IResourceClassのgetの戻り値を自作のIResourceを継承したものにキャスト
  • あとは好きにする
/// <reference path="scripts/typings/angularjs/angular.d.ts" />/// <reference path="scripts/typings/angularjs/angular-resource.d.ts" />

var m = angular.module("sampleApp", ["ngResource"]);

interface AppScope extends ng.IScope {
    message:string;
}

class Person {
    constructor(
        public id: number = null,
        public name: string = null) {
    }
}

interface PersonResource extends ng.resource.IResource<Person> {
    name:string;
}

m.controller("AppCtrl", ($scope: AppScope, $resource: ng.resource.IResourceService) => {
    var p = $resource("/api/Person/:id", { id: "@id" });

    var user = <PersonResource>p.get({ id: 10 }, () => {
        $scope.message = user.name;
    });
});

CLIMutable属性つけると便利らしい?

$
0
0

F#にCLIMutableなる属性が追加さられたみたいです。こいつをつけるとデフォルトコンストラクタとか、プロパティに自動的にgetter/setterつけてくれてDTO作ったりEFで便利かもね!みたいな説明が書いてあるような気がしました。

namespace Hoge
    open System;

    // この属性をつけるとレコードにデフォルトコンストラクタとgetter/setterが出来るらしい
    [<CLIMutableAttribute>]
    type Person = {
        Name: string;
        Age: int;
    }

C#のプロジェクトから参照するとデフォルトコンストラクタとプロパティへのセッターが使えました

var p = new Hoge.Person(); // OK
p.Name = "tanaka"; // OK!

ちなみに、属性を外して実行すると上記コードはエラーになります。

var p = new Hoge.Person("tanaka", 10); // コンストラクタで値を設定したら変更できない
p.Name = "kimura"; // NG

F#版のBindableBaseクラス

$
0
0

F#でINotifyPropertyChangedを実装するとどうなるかな~というので実装してみた。

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns

type BindableBase() =
    let propertyChnged = Event<_, _>()
    interface System.ComponentModel.INotifyPropertyChanged with
        [<CLIEventAttribute>]
        member x.PropertyChanged = propertyChnged.Publish

    member x.Set<'T>((field: 'T byref), value, propertyExpression) =
        if (System.Object.Equals(field, value)) then
            false
        else
            field <- value

            match propertyExpression with
            | PropertyGet(_, info, _) -> propertyChnged.Trigger(x, System.ComponentModel.PropertyChangedEventArgs(info.Name))
            | _ -> ()

            true

SetメソッドのpropertyExpressionは、F#のコード クォート (F#)を使ってタイプセーフにプロパティ名を指定できるようにしてみました。使い方はこんな感じ。

type Person() =
    inherit BindableBase()

    let mutable name =""
    member this.Name 
        with get() = name
        and set(v) = this.Set(&name, v, <@ this.Name @>) |> ignore
let p = Person()
(p :> System.ComponentModel.INotifyPropertyChanged).PropertyChanged.Add(fun e -> printfn "%s" e.PropertyName)
p.Name <- "aaa"
printfn "%s" p.Name

実行すると

Name
aaa

と表示される。

F#でTaskを待つ

$
0
0

非同期ワークフローの中でTaskってどうやって使うんだろう?と思ってたらAsyncクラスにAwaitaTaskっていうずばりっぽいメソッドがありました。こいつを使えば非同期ワークフローの中でlet!で結果を受け取ることができる。HttpClientを使ってGoogleトップページの情報をとってくるコードはこんな感じになりました。

割といい感じかも。

open System.Net.Http

async {
    use c = new HttpClient()
    let! r = c.GetAsync("http://www.google.co.jp") |> Async.AwaitTask
    r.EnsureSuccessStatusCode() |> ignore
    let! body = r.Content.ReadAsStringAsync() |> Async.AwaitTask

    printfn "%A" body
} |> Async.RunSynchronously

ReactiveProperty + F#で簡単なViewModel書いてみたよ

$
0
0

超簡単なやつ。入力があったら1秒後に別プロパティに全部大文字にして出すやつです。

open Codeplex.Reactive
open System
open System.Reactive.Linq

type MainWindowViewModel() =
    // 入力用
    let input = new ReactiveProperty<string>()
    // 出力用
    let output = 
        // 入力されたものを1秒まつ
        input.Delay(TimeSpan.FromSeconds(1.))
        // 大文字に変換する
        |> Observable.map (fun s -> s.ToUpper())
        // IO<T>をReactivePropertyに変換する
        |> ReactiveProperty.ToReactiveProperty

    // 外部にプロパティとして公開する
    member x.Input = input
    member x.Output = output

悪くないくらいすっきりかけそうな気がしますね。

System.Xamlを使ったシリアライズ・デシリアライズ

$
0
0

上から順に、単純なデシリアライズ。ローカルアセンブリを指定したデシリアライズ。文字列へのシリアライズ。文字列以外のストリームやテキストライターへのシリアライズです。

using System;
using System.IO;
using System.Reflection;
using System.Xaml;

namespace ConsoleApplication5
{
    class Program
    {
        staticvoid Main(string[] args)
        {
            // 読み込み
            {
                // XAML
                var xaml = @"<Person xmlns='clr-namespace:ConsoleApplication5;assembly=ConsoleApplication5' Name='Tanaka' />";
                // 
                var r = new XamlXmlReader(new StringReader(xaml));
                // 読み込み
                var p = XamlServices.Load(r) as Person;
                // 確認
                Console.WriteLine(p.Name);
            }

            // 読み込み2
            {
                // XAML(LocalAssemblyを指定してるのでxmlnsにassembly指定がいらない)
                var xaml = @"<Person xmlns='clr-namespace:ConsoleApplication5' Name='Tanaka' />";
                // 
                var r = new XamlXmlReader(new StringReader(xaml), new XamlXmlReaderSettings
                {
                    LocalAssembly = Assembly.GetExecutingAssembly()
                });
                // 読み込み
                var p = XamlServices.Load(r) as Person;
                // 確認
                Console.WriteLine(p.Name);
            }

            // 書き込み
            {
                var xaml = XamlServices.Save(new Person { Name = "kimura" });
                Console.WriteLine(xaml);
            }
            // 書き込み2
            {
                var w = new StringWriter();
                XamlServices.Save(w, new Person { Name = "kimura" });
                Console.WriteLine(w.ToString());
            }

        }
    }

    // XAMLで保存するオブジェクトpublicclass Person
    {
        publicstring Name { get; set; }
    }
}

WPFのXAMLのTypeConverterお試し

$
0
0

WPFのXAMLではTypeConverterを自作して文字列から、オブジェクトを作るようなことが簡単にできるようになっています。ということで簡単に試してみました。XAMLからオブジェクトへの変換にはTypeConverterクラスのConvertFromメソッドとCanConvertFromメソッドを実装していれば問題なさそう。

今回は以下のようなPersonクラスで

publicclass Person
{
    publicstring Name { get; set; }
    public Person Child { get; set; }
}

Tanaka->Kimura->Hoge->Fooみたいに入力するとNameがTnakaのPersonを作って、そのChildにNameがKimuraのPersonを作って、そのChildに…(略)となるようなTypeConverterを作りました。->で区切って組み立てていくだけですので簡単です。

publicclass PersonTypeConverter : TypeConverter
{
    publicoverridebool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    publicoverrideobject ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, objectvalue)
    {
        var text = (string)value;
        var people = text
            .Split(new[] { "->" }, StringSplitOptions.None)
            .Select(s => s.Trim())
            .Select(s => new Person { Name = s })
            .ToArray();
        people
            .Aggregate((parent, child) =>
            {
                parent.Child = child;
                return child;
            });
        return people.First();
    }
}

このコンバータをPersonクラスにつけます。

[TypeConverter(typeof(PersonTypeConverter))]
publicclass Person
{
    publicstring Name { get; set; }
    public Person Child { get; set; }
}

System.Xamlを参照してレッツパース。

class Program
{
    staticvoid Main(string[] args)
    {
        var xaml = @"<Person xmlns='clr-namespace:ConsoleApplication3;assembly=ConsoleApplication3'                Name='Tanaka taro'                Child='Tanaka goro->Tanaka jiro->Tanaka hoge->Tnaka foo' />";

        var p = XamlServices.Parse(xaml) as Person;
        while (p != null)
        {
            Console.WriteLine("Name: {0}", p.Name);
            p = p.Child;
        }
    }
}

実行するといい感じに表示されます。

Name: Tanaka taro
Name: Tanaka goro
Name: Tanaka jiro
Name: Tanaka hoge
Name: Tnaka foo

コード全体

最後にコード全体をのせておきます。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xaml;

namespace ConsoleApplication3
{
    class Program
    {
        staticvoid Main(string[] args)
        {
            var xaml = @"<Person xmlns='clr-namespace:ConsoleApplication3;assembly=ConsoleApplication3'                    Name='Tanaka taro'                    Child='Tanaka goro->Tanaka jiro->Tanaka hoge->Tnaka foo' />";

            var p = XamlServices.Parse(xaml) as Person;
            while (p != null)
            {
                Console.WriteLine("Name: {0}", p.Name);
                p = p.Child;
            }
        }
    }

    [TypeConverter(typeof(PersonTypeConverter))]
    publicclass Person
    {
        publicstring Name { get; set; }
        public Person Child { get; set; }
    }

    publicclass PersonTypeConverter : TypeConverter
    {
        publicoverridebool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string);
        }

        publicoverrideobject ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, objectvalue)
        {
            var text = (string)value;
            var people = text
                .Split(new[] { "->" }, StringSplitOptions.None)
                .Select(s => s.Trim())
                .Select(s => new Person { Name = s })
                .ToArray();
            people
                .Aggregate((parent, child) =>
                {
                    parent.Child = child;
                    return child;
                });
            return people.First();
        }
    }
}

ItemsControlの劣化版を自作してみる(DataTemplate対応コントロールの作成)

$
0
0

DataTemplateに対応したコントロールの作り方ということで、こちらのサイトを写経させていただきました。

カスタムコントロールの作成

カスタムコントロールを新規作成して、Generic.xamlに適当にStackPanelを追加します。ここにアイテムを追加していくっていう予定です。

<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApplication4"><Style TargetType="{x:Type local:MyItemControl}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:MyItemControl}"><Border Background="{TemplateBinding Background}"BorderBrush="{TemplateBinding BorderBrush}"BorderThickness="{TemplateBinding BorderThickness}"><StackPanel x:Name="ItemsPanel" /></Border></ControlTemplate></Setter.Value></Setter></Style></ResourceDictionary>

依存プロパティの作成

今回は、データを格納するためのItemsSourceプロパティと、1要素1要素に適用するDataTemplateを設定するItemTemplateプロパティを追加します。

public IEnumerable ItemsSource
{
    get { return (IEnumerable)GetValue(ItemsSourceProperty); }
    set { SetValue(ItemsSourceProperty, value); }
}

// Using a DependencyProperty as the backing store for ItemsSource.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty ItemsSourceProperty =
    DependencyProperty.Register("ItemsSource", 
        typeof(IEnumerable), 
        typeof(MyItemControl), 
        new PropertyMetadata(null, ItemsSourceChanged));

public DataTemplate ItemTemplate
{
    get { return (DataTemplate)GetValue(ItemTemplateProperty); }
    set { SetValue(ItemTemplateProperty, value); }
}

// Using a DependencyProperty as the backing store for ItemTemplate.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty ItemTemplateProperty =
    DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(MyItemControl), new PropertyMetadata(null));

ItemsSourceは変更のタイミングで再描画したいので、変更時のコールバックを設定してます。

OnApplyTemplateとその他のメソッド

ということで、残りの部分です。ItemsSourceChangedは、再描画するためのメソッドを呼ぶだけのシンプル実装です。

privatestaticvoid ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ((MyItemControl)d).RenderItems();
}

ControlTemplateが適用されるタイミングで呼ばれるOnApplyTemplateメソッドも、Panelをテンプレートから取得するしたら再描画するだけです。

private Panel itemsPanel;

publicoverridevoid OnApplyTemplate()
{
    base.OnApplyTemplate();
    this.itemsPanel = this.GetTemplateChild("ItemsPanel") as Panel;
    this.RenderItems();
}

そして大事な再描画メソッドのRenderItemsメソッドです。こいつは、DataTemplateのLoadContentメソッドを使ってテンプレートからコントロールの実体を作成して、DataContextに要素をつっこんでからPanelに追加しています。

privatevoid RenderItems()
{
    if (this.itemsPanel == null)
    {
        return;
    }

    this.itemsPanel.Children.Clear();

    if (this.ItemsSource == null)
    {
        return;
    }

    foreach (var item inthis.ItemsSource)
    {
        var elm = this.ItemTemplate.LoadContent() as FrameworkElement;
        elm.DataContext = item;
        this.itemsPanel.Children.Add(elm);
    }
}

このメソッドが今回の核ですね。LoadContentメソッドメモメモ。

実行してみる

画面に適当においてみます。

<local:MyItemControl ItemsSource="{Binding}"><local:MyItemControl.ItemTemplate><DataTemplate><TextBlock Text="{Binding Name}" /></DataTemplate></local:MyItemControl.ItemTemplate></local:MyItemControl>

DataContextにはNameプロパティをもったオブジェクトの配列を入れてます。実行すると、こんな感じに表示されます。

f:id:okazuki:20140623211034j:plain

WinRTのAPIをデスクトップアプリから使う 8.1版

$
0
0

Windows 8.0のころとそんなに変わりありませんでした。

8.0の頃はプロジェクトファイルに8.0と書いてたけど8.1にします。プロジェクトをアンロードして下記の内容を最初のPropertyGroupタグの中に追記します。

<TargetPlatformVersion>8.1</TargetPlatformVersion>

プロジェクトの再読込をしたら、参照でWindows ->コア -> Windowsを追加します。これがwinmdファイルになります。

次に以下のフォルダにあるSystem.Runtime.WindowsRuntime.dllを参照に追加します。

  • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5.1

あとは、Windows Runtimeでデスクトップに対応しているAPIを叩きまくりましょう!

Windows ストアアプリでマークアップ拡張作りたい・・・!(作れません)

$
0
0

オワタ。

というのでは何なので、1つだけ残された拡張の道を歩んでみようと思います。最近まで存在を知らなかったCustomResourceというマークアップ拡張があります。こいつは、デフォルトでは動作しないかわりに、自分で独自の実装を差し込むことが出来るようになっています。やり方は以下の通り。

  • CustomXamlResourceLoaderクラスを継承したクラスを作る
  • GetResourceメソッドをオーバーライドして、状況に応じて返したい型を返すロジックを書く
  • App.xaml.csのコンストラクタで以下の処理をする
    • CustomXamlResourceLoader.Currentに自前クラスのインスタンスをつっこむ

実用的ではないですが、以下のようなCustomXamlResourceLoaderを継承したクラスを作ったとします。

using System;
using System.Collections.Generic;
using System.Text;
using Windows.UI.Xaml.Resources;

namespace App2
{
    publicclass MyCustomLoader : CustomXamlResourceLoader
    {
        protectedoverrideobject GetResource(
            string resourceId, string objectType, string propertyName, string propertyType)
        {
            returnstring.Join(", ", resourceId, objectType, propertyName, propertyType);
        }
    }
}

そして、Appクラスのコンストラクタで、このMyCustomLoaderを使うように設定します。

public App()
{
    CustomXamlResourceLoader.Current = new MyCustomLoader();

    this.InitializeComponent();
    this.Suspending += this.OnSuspending;
}

あとはCustomResourceマークアップ拡張をかくだけです。適当に画面にTextBlockを置いて、Textプロパティに設定してみました。

<TextBlock Text="{CustomResource SampleCustomResource}" />

そうすると、TextBlockが以下のように表示されます。

f:id:okazuki:20140627081948p:plain

まとめ

ドキュメントにも、普通つかわんだろみたいなことが書いてあったのですが、普通使わない感じですね・・・!誰得情報でした。

Leap Motionで指先を画面に表示するサンプルをコードレシピにおきました

C#からWin32API叩く

$
0
0

P/Invokeもいいですが、C++/CLI経由も個人的に好きです。例えばアクティブなWindowのタイトルを取りたいときとか…。

C++/CLIでこんなクラスを用意しておく。

// CPPCLR.h#pragma once#include "Stdafx.h"#include <Windows.h>#include <tchar.h>usingnamespace System;

namespace CPPCLR {

    public ref class ActiveWindow
    {
    public:
        static String^ GetActiveWindowText()
        {
            GUITHREADINFO info;
            info.cbSize = sizeof(GUITHREADINFO);
            ::GetGUIThreadInfo(NULL, &info);
            WCHAR str[1024];
            ::GetWindowText(info.hwndActive, str, 1024);
            return gcnew String(str);
        }
    };
}

このままだとリンカエラーになるので、プロパティのリンカーの入力にある、追加の依存ファイルを編集して、user32.libとかを追加するようにする。編集画面開いてOK押すだけでよさそう。

C#側は、C++/CLIのプロジェクトを参照して普通に呼び出すだけですね。

privatevoid Window_Loaded(object sender, RoutedEventArgs e)
{
    timer = new DispatcherTimer();
    timer.Tick += (s, args) =>
    {
        info.Text = CPPCLR.ActiveWindow.GetActiveWindowText();
    };
    timer.Start();
}

込み入った値を返すとか、コールバックが必要とかそういうのじゃなければ割とサクッと作れて便利です。

Viewing all 1388 articles
Browse latest View live


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