はまったりひらめいたり…とか…

Angularや.NETやAzureやその他色々。

Angular2で和暦フォーマットDatePipeを作る。

業務で使っているだけあって、Angular2の知見が溜まってきたので放出放出。

こんな感じの表示ができるPipeを作りました。というお話です。

使用したのは「Angular2 R.C5」でございます。

f:id:TakasDev:20160812041422p:plain

Pipeってなんぞや

Angularでデータバインドする際、下記のように記述することで

Date型の値がFormatされて出力されるようになります。

{{ nowDate | date:"yyyy/MM/d" }}

↓こんな感じに表示されます。

f:id:TakasDev:20160812041012p:plain

Date型にかぎらず

例えば数値に対応する文字列

value=10000→画面上は「福沢諭吉」と表示

value=5000→画面上は「新渡戸稲造」と表示することもできるわけです。

和暦DateFormat

.Netは和暦もフォローしてくれていますが

Webは和暦をフォローしてくれません。

なので、和暦フォーマットファンクションを作成し

それを使用したDatePipeを作成してみます。

date-jp-format.ts(V1)

export class DateJpFormat {
    GetJpFormatDate(value: Date, format: string): string {
        if (!format) format = "yyyy年 MM月 DD日 aa曜日 hh:mm:ss";
        //和暦を取得
        var jpDate = value.toLocaleDateString("ja-JP-u-ca-japanese");
        var jpYear = jpDate.substring(0, jpDate.indexOf("/"));
        var jpYearArray = jpYear.match(/[0-9]+\.?[0-9]*/g);
        var jpYearNumber = "";
        //SafariなんかはArray=Nothingでくる!
        if (jpYearArray) {
            jpYearNumber = jpYearArray[0];
        }
        var regExpg = new RegExp(jpYearNumber, "g");

        var jpYearString = jpYear.replace(regExpg, "");
        var jpYearStringShort = jpYearString.slice(0, 1);

        //年変換(Full桁)
        format = format.replace("gg", jpYearString);
        format = format.replace("yyyy", ("" + (value.getFullYear())));

        //年変換(短縮)
        format = format.replace("g", jpYearStringShort);
        format = format.replace("yy", ("0" + (value.getFullYear())).slice(-2));
        format = format.replace("e", jpYearNumber);

        format = format.replace("MM", (("0" + (value.getMonth() + 1)).slice(-2)));
        format = format.replace("DD", (("0" + (value.getDay())).slice(-2)));
        format = format.replace("aa", weekDayList[value.getDay()]);
        format = format.replace("hh", (("0" + (value.getHours())).slice(-2)));
        format = format.replace("mm", (("0" + (value.getMinutes())).slice(-2)));
        format = format.replace("ss", (("0" + (value.getSeconds())).slice(-2)));
        return format;
    }
}
var weekDayList = ["日", "月", "火", "水", "木", "金", "土"];

とりあえず、DatePipeは抜きにして、どのように変換されるのかみてみます。

f:id:TakasDev:20160812041057p:plain

「ja-JP-u-ca-japanese」で元号「平成」等を表示してくれるのは

Chormeで、IEやEdgeなんかは、元号は取得してくれません(FireFoxは試してないです)。

Safariに至っては和暦の年数すら取得できません。

ベースはこれでいいかもしれませんが、元号が取れなかった場合のことを考え

自前で実装する他なさそうです。

Formatの実装

1. 和暦に関する情報を準備

和暦換算に必要な情報を作成します。 ・jp-calender-info.ts

export const jpCalendarInfo: JpCalenderType[] = [
    {
        Name: "明治",
        StartDate: new Date("1868-09-08T00:00:00"),
        EndDate: new Date("1912-07-30T00:00:00")
    },
    {
        Name: "大正",
        StartDate: new Date("1912-07-30T00:00:00"),
        EndDate: new Date("1926-12-25T00:00:00")
    },
    {
        Name: "昭和",
        StartDate: new Date("1926-12-25T00:00:00"),
        EndDate: new Date("1989-01-08T00:00:00")
    },
    {
        Name: "平成",
        StartDate: new Date("1989-01-08T00:00:00"),
        EndDate: new Date("2087-12-31T23:59:59")
    }
];

export class JpCalenderType{
    Name: string;
    StartDate: Date;
    EndDate: Date;
}

2. 和暦算出ファンクションの作成

1で作成した構造体を、算出した日付でフィルタを掛け

和暦の元号と年数の算出を行うファンクションを作成します。

    private FormatJpYearString(formatDate: Date): string {
        var jpInfo = jpCalendarInfo
            .filter((x:JpCalenderType) => x.StartDate <= formatDate && formatDate < x.EndDate);
        return jpInfo[0].Name;
    }
    private FormatJpYearNumber(formatDate: Date): string {
        var jpInfo = jpCalendarInfo
            .filter((x:JpCalenderType) => x.StartDate <= formatDate && formatDate < x.EndDate);
        var nowYear = formatDate.getFullYear();
        var baseYear = jpInfo[0].StartDate.getFullYear() - 1;
        return String(nowYear - baseYear);
    }

3.date-jp-format.tsの修正

上記のファンクションを合わせると、date-jp-format.tsは下記のようになります。

今回作成したファンクションは応急処置的な作りになっていますので

基本的にはブラウザ側で取得できたら、その値を使用する感じになっています。

ここで問題なのが「jpYearString 」にIEの場合ナニが入っているのか?ということです。

Emptyに見えますが実は「」という文字が入ってます。

ハイ。見えませんね。

ここにはUTF-16のU+200E文字コードの文字が入っているようです。

今回、かなり力技で出力された見えない文字をコピって貼り付けてますが

本来であれば文字コード変換等を行って、U+200E文字を潰すなりしなければならないはずです。

date-jp-format.ts

import { jpCalendarInfo, JpCalenderType } from "../function/jp-calender-info";

export class DateJpFormat {
    GetFormatDate(getValue: any, format: string): string {
        //取得した値を日付変換
        var value = new Date(getValue);

        //Format未指定の場合は西暦表示
        if (!format) format = "yyyy年 MM月 DD日 aa曜日 hh:mm:ss";
        //和暦を取得
        var jpYearNumber = this.FormatJpYearNumber(value);
        var jpYearString = this.FormatJpYearString(value);
        var jpYearStringShort = jpYearString.slice(0, 1);

        //年変換(Full桁)
        format = format.replace("gg", jpYearString);
        format = format.replace("yyyy", ("" + (value.getFullYear())));

        //年変換(短縮)
        format = format.replace("g", jpYearStringShort);
        format = format.replace("yy", ("0" + (value.getFullYear())).slice(-2));
        format = format.replace("e", jpYearNumber);

        format = format.replace("MM", (("0" + (value.getMonth() + 1)).slice(-2)));
        format = format.replace("DD", (("0" + (value.getDate())).slice(-2)));
        format = format.replace("aa", weekDayList[value.getDay()]);
        format = format.replace("hh", (("0" + (value.getHours())).slice(-2)));
        format = format.replace("mm", (("0" + (value.getMinutes())).slice(-2)));
        format = format.replace("ss", (("0" + (value.getSeconds())).slice(-2)));
        return format;
    }
    private FormatJpYearString(formatDate: Date): string {
        var jpInfo = jpCalendarInfo
            .filter((x: JpCalenderType) => x.StartDate <= formatDate && formatDate < x.EndDate);
        return jpInfo[0].Name;
    }
    private FormatJpYearNumber(formatDate: Date): string {
        var jpInfo = jpCalendarInfo
            .filter((x: JpCalenderType) => x.StartDate <= formatDate && formatDate < x.EndDate);
        var nowYear = formatDate.getFullYear();
        var baseYear = jpInfo[0].StartDate.getFullYear() - 1;
        return String(nowYear - baseYear);
    }
}

var weekDayList = ["日", "月", "火", "水", "木", "金", "土"];

結果、Edgeでも下図のように表示されるようになりました。

f:id:TakasDev:20160812041231p:plain

4.DatePipeを作る

「@angular/core」の「Pipe」「PipeTransform 」を使用します。

@Pipe以降の「name」でHTML上に記述する、Pipeの名称を設定します。

transform()内部は表示するデータのFormatを行っている部分です。

今回は日付のデータと文字列を使用したフォーマットを行います。

結果、下記のようなソースコードとなります。

jp-date-pipe.ts

import { Pipe, PipeTransform } from "@angular/core";
import { DateJpFormat } from "../function/date-jp-format"

@Pipe({ name: "JpDatePipe" })
export class JpDatePipe implements PipeTransform {
    dateJpFormat: DateJpFormat;
    transform(value: Date, args: any[]) {
        if (value) {
            this.dateJpFormat = new DateJpFormat();
            var formatString = String(args);
            return this.dateJpFormat.GetJpFormatDate(value, formatString);
        }
    }
}

5.DatePipeを使う

今回作成したPipeを使用したいコンポーネント

「独自設定したPipeを使いますよ」と設定しなければ当然使えません。

{pipes: [JpDatePipe]}で使用するPipeを宣言することで使えるようになります。

下記のような記述で使用します。 {{nowDate | JpDatePipe:[Formatする文字列]}}

最終的に下記のようなソースコードになりました。 date-format-page.component.ts

import { Component, Pipe } from "@angular/core";
import { DateJpFormat } from "../../function/date-jp-format";
import { JpDatePipe } from "../../custom-pipe/jp-date-pipe";
@Component({
  pipes: [JpDatePipe],
  template: `
    <h2>↓変換日付↓</h2> 
    {{nowDate}}
   <h2>↓DatePipe変換↓</h2>
    <div *ngFor="let outputFormat of formats">
      {{nowDate | JpDatePipe:[outputFormat]}}
    </div>
  `
})
export class DateFormatPageComponent {
    nowDate: Date;
    formats: string[];
    _dateJpFormat: DateJpFormat;
    constructor(){
        this.nowDate = new Date();
        this.formats= [
            "gg",
            "yy",
            "yyyy",
            "gge",
            "ge",
            "gge年MM月",
            "yyyy年MM月",
            "gge年MM月DD日",
            "yyyy年MM月DD日",
            "gge年MM月DD日aa曜日",
            "yyyy年MM月DD日aa曜日",
            "gge年MM月DD日aa曜日(hh)",
            "yyyy年MM月DD日aa曜日(hh)",
            "gge年MM月DD日aa曜日(hh)",
            "yyyy年MM月DD日aa曜日(hh)",
            "gge年MM月DD日aa曜日(hh:mm)",
            "yyyy年MM月DD日aa曜日(hh:mm)",
            "gge年MM月DD日aa曜日(hh:mm:ss)",
            "yyyy年MM月DD日aa曜日(hh:mm:ss)",
            "本日、gge年MM月DD日aa曜日です!!",
        ];
    }   
}

やたらと長いですが、結構な数のFormatを試しているので長くなっています。

肝の部分は下記のHTMLですね。

<h2>↓DatePipe変換↓</h2>
<div *ngFor="let outputFormat of formats">
    {{nowDate | JpDatePipe:[outputFormat]}}
</div>

Tsで宣言したFormatをすべて表示するHTMLとなります。

JpDatePipeという一つの変数から、ありとあらゆる表示の仕方が可能となっています。

結果、Webページ上には下図のように表示されました。

f:id:TakasDev:20160812041422p:plain

まとめ

上記のとおり、日付情報自体は同じ変数を使いまわしてるので

Pipeを使えばView用の変数と登録用の変数を分ける…

なんてことはしなくても良くなるということですね。

便利。

Xamarinでもくもくしたお話し

今日、昨日と両日もくもく会に参加してました。

JXUG東京もくもく会と、業務系システム開発勉強会でもくもくしていました。

どっちもXamarinでもくもくしました。一応それらの進展を。

ListViewにViewModelをバインドしようというお話。

まずは参考

ListView Data Sources - Xamarin

ytabuchi.hatenablog.com

ソース

まぁなんてことはない、普通のListViewバインディングです。

ViewModelとListViewのバインディングはかなり簡単に行えます。

//今回は初期ViewModelのデータをつっこんでおく
var searchInfo = new ObservableCollection<SearchInfo>
{
    new SearchInfo { Detail = "高橋 慶太郎", SearchType = ComicTokutenEnum.SearchType.AuthorSearch},
    new SearchInfo { Detail = "弐瓶 勉", SearchType = ComicTokutenEnum.SearchType.AuthorSearch},
    new SearchInfo { Detail = "Pumpkin Scissors", SearchType = ComicTokutenEnum.SearchType.TitleSearch},
    new SearchInfo { Detail = "メイドインアビス", SearchType = ComicTokutenEnum.SearchType.TitleSearch}
};

//データバインド
this.BindingContext = searchInfo;

実行結果

トグルの画面が崩れるのはエミュレータゆえなのかなぁ? f:id:TakasDev:20160612233854p:plain

ModernHttpClient + HtmlAgilityPack

XamarinでWebページを解析しよう!というお話。

こちらも比較的簡単に実現できました。

まずは参考

blog.ch3cooh.jp

Xamarin のプロジェクトで NuGet を使用する : XLsoft エクセルソフト

ModernHttpClient

Nativeに依存しないWeb通信が可能となる。

NugetでGet可能。 f:id:TakasDev:20160612233112p:plain

HtmlAgilityPack

.NetのHTMLパーサーの定番。

NuGetの取得だけでも使用できるようになっているようです。

f:id:TakasDev:20160612233217p:plain

処理。

"@CH3COOHさんの処理内容にModernHttpClientの使用を追加した感じです。

ほぼ同内容の処理は下コードな感じになりました。 (パース部分はちょっと手抜きしてますけど)

・ソース

//画面EntryからCode値を取得
var code = CompanyId.Text;
var urlstring = string.Format("http://stocks.finance.yahoo.co.jp/stocks/detail/?code={0}", code);

var httpClient = new HttpClient(new NativeMessageHandler());
var response = await httpClient.GetAsync(urlstring);

//html取得
var html = await response.Content.ReadAsStringAsync();

//AgilityPackにぶっこみ
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(html);

//解析解析
var priceNode = doc.DocumentNode.Descendants("td")
    .Single(node => node.GetAttributeValue("class", "") == "stoksPrice");

var companyNode = doc.DocumentNode.Descendants("th")
    .Single(node => node.GetAttributeValue("class", "") == "symbol");

//表示
this.Outputlabel.Text = string.Format("{0}({1})の株価: {2}円", companyNode.InnerText, CompanyId.Text, priceNode.InnerHtml);

実行結果

f:id:TakasDev:20160612233803p:plain

後回しにしたこと

本当はAmazonの商品検索APIを使用して

XMLの解析をしてみたかったのだけど、Amazonが提供しているサンプル

SignedRequestHelperがSystem.Webをバリバリ使っているんで

結構書き直さなきゃいけない印章。

参考: Product Advertising API Signed Requests Sample Code - C# REST/QUERY : Sample Code & Libraries : Amazon Web Services

blog.nakajix.jp

もくもく会中にViewに表示できるなにかを作りたかったので

後に回しました(時間が余って作業したけど結局Amazonのは終わらなかった…)

最後に

もくもく会楽しかったのでまた参加したいです。

Angular2Tutorial + VisualStudio2015

Angular2のページで提供されているチュートリアル

MVC4でやっちゃおうというものです(準備編)

基本的に前回作ったソリューションで

新しいものに入れ替えていくようなイメージとなります。

TutorialSite

angular.io

package.jsonを改造

前回作成したAngularの情報はちと古めだったみたいです。

チュートリアルのは、最新の動向にあわせている(はず)なので

そちらの流行によせておきます。

Tutorialで増えているファイルを追加していく。

上記それぞれが増えているので、ソリューションに追加します。

BundleConfigを変更

    bundles.Add(New ScriptBundle("~/bundles/angular2").Include(
        "~/node_modules/core-js/client/shim.min.js",
        "~/node_modules/zone.js/dist/zone.js",
        "~/node_modules/reflect-metadata/Reflect.js",
        "~/node_modules/systemjs/dist/system.src.js",
        "~/systemjs.config.js"))

app.tsを変更

Tutorialと同一ソースに変更します。

はまりやすいポイント

基本的には、チュートリアルのソースと見比べ

変更部分を反映させる方法でうまくいきました。

package.jsonを入替えて、パッケージのインストールをすると

下記のサイトで報告されているようなエラーが複数発生します。

stackoverflow.com

ただ、GitHubで報告された結果、解決方法が反映されているようですので

typings.json等の新しいJsonファイルを追加することで、解消されます。

エラーが出てもそのまま立ち止まらず、進めていくのがいいかもしれません。

github.com

結果Tutorialをすすめることが出来ました

f:id:TakasDev:20160611002933p:plain

構成は下図のような感じです。

f:id:TakasDev:20160611005803p:plain

Angular2 + VisualStudio(ASP.NET MVC"4") + VisualBasic.net

風邪ひいてたりなんだりで全然ブログ更新してませんでしたー。っと。

Angular2とASP.NET MVC"4"をお仕事でいじることになりました。

Angular2 + VisualStudio(ASP.NET MVC"4") + VisualBasic.net

ニッチすぎて参考文献が少なく、VS上で動かすまでに色々と面倒くさかったのでメモ残します。

使用環境

  • VisualStudio2015(Update2)
  • TypeScript(1.8.29.0)

環境構築

1.ひとまずプロジェクトを作成

f:id:TakasDev:20160609221724p:plain
業務の都合上VB.netでプロジェクト作成していますが

今回記述ソースは.net側は、ほとんど何もありません。

VB.netなのでMVC5はテンプレート上存在しないので、MVC4を選択しました。

2.npmのpackage.jsonファイルを追加

rootにpackage.jsonを追加します。

記述するソースは下記のとおり。

{
  "name": "angular2-quickstart",
  "version": "1.0.0",
  "scripts": {
    "tsc": "tsc",
    "tsc:w": "tsc -w",
    "lite": "lite-server",
    "start": "concurrent \"npm run tsc:w\" \"npm run lite\" "
  },
   "license": "ISC",
  "dependencies": {
    "angular2": "2.0.0-beta.1",
    "systemjs": "0.19.6",
    "es6-promise": "^3.0.2",
    "es6-shim": "^0.33.3",
    "reflect-metadata": "0.1.2",
    "rxjs": "5.0.0-beta.0",
    "zone.js": "0.5.10"
  },
  "devDependencies": {
    "concurrently": "^1.0.0",
    "lite-server": "^1.3.2",
    "typescript": "^1.7.5" 
  }
} 

4.パッケージインストール

f:id:TakasDev:20160609222757p:plain

5.Rootディレクトリにtsconfig.jsonを作成

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false,
    "outDir": "app/"
  },
  "exclude": [
    "node_modules"
  ]
}

outDirに.tsファイルから生成した.jsファイルが出力されます。

6.BundleConfigにAngular2読み込みを追加

        bundles.Add(New ScriptBundle("~/bundles/angular2").Include(
            "~/node_modules/es6-shim/es6-shim.js",
            "~/node_modules/systemjs/dist/system-polyfills.js",
            "~/node_modules/angular2/bundles/angular2-polyfills.js",
            "~/node_modules/systemjs/dist/system.src.js",
            "~/node_modules/rxjs/bundles/rx.js",
            "~/node_modules/angular2/bundles/angular2.dev.js",
            "~/node_modules/angular2/bundles/router.dev.js",
            "~/node_modules/angular2/bundles/http.dev.js"
            ))

7.app.tsを作成

scriptsディレクトリにappディレクトリを作成。
appディレクトリにapp.tsファイルを作成。

import {Component} from 'angular2/core';
@Component({
    selector: 'my-app',
    template: '<h1>Angular 2 Sample Application</h1>'
})
export class AppComponent { }

8.エラー発生

app.tsに下記のエラーが発生
f:id:TakasDev:20160609224013p:plain

まずひとつ。TypeScript古いですよー。ってメッセージが出る。

これ、Resharperが入っている場合にでるエラーだそう。

ReSharperの設定をいじることで解決。

参考:
stackoverflow.com


残りのエラー
f:id:TakasDev:20160609224205p:plain

これは、promise.d.tsに

declare var Promise: PromiseConstructor;

を加えることで解決できるらしい。

参考:
github.com

最後 _Layout.vbhtmlを改造

HEADに下記

    @Scripts.Render("~/bundles/angular2")
    <script>
      System.config({
        transpiler: 'typescript',
        typescriptOptions: { emitDecoratorMetadata: true },
        packages: {'app': {defaultExtension: 'js'}}
      });
      System.import('app/boot')
            .then(null, console.error.bind(console));
    </script>

BODYに下記を記述

<my-app>Loading...</my-app>

結果

f:id:TakasDev:20160609224654p:plain
表示できました。

f:id:TakasDev:20160609225634p:plain
最終出力時点のディレクトリ構成は上図のとおりです。

というわけで、Angular2の開発実験場が整ったわけです。

長かった…。

Xamarinやろーっと。

Xamarin.FormsのGridについて

下図の画面をGridで作成するための前準備のお話し。

Gridの仕様(?)が結構特殊な感じだったためφ(..)
f:id:TakasDev:20160518000634p:plain


Xamarin.FormsのAngerの内容を流用しているため、Xamlの話ではありません。

www.syntaxismyui.com


AngerのCardsの画面の作り方、結構好きなんですが

これ、Xamlで作るにはどうしたらいいんですかね?さっぱり想像つきません…

Grid

Grid grid = new Grid
{
    RowSpacing = 1,
    ColumnSpacing = 1,
    BackgroundColor = UserPageStyleKit.CardBorderColor,
    VerticalOptions = LayoutOptions.FillAndExpand,
    RowDefinitions =
    {
        new RowDefinition { Height = new GridLength(80, GridUnitType.Absolute)},
        new RowDefinition { Height = new GridLength(50, GridUnitType.Absolute)}
    },
    ColumnDefinitions =
    {
        new ColumnDefinition { Width = new GridLength(100, GridUnitType.Absolute)},
        new ColumnDefinition { Width = new GridLength ( 1,GridUnitType.Star)},
        new ColumnDefinition { Width = new GridLength ( 10,GridUnitType.Absolute)},
        new ColumnDefinition { Width = new GridLength ( 140,GridUnitType.Absolute)}
    }
};

上記で2行4列のGridとなります。

下図の様なGridの配置となります。

f:id:TakasDev:20160518001048p:plain

それぞれGridLengthが指定されていますが、紫色背景の列は

GridUnitType.starが指定されているため、表示領域により動的に幅が変わるようです。

例えば、下記のようにソースを変更すると、1:2で描画されるようです。

ColumnDefinitions =
{
    new ColumnDefinition { Width = new GridLength(100, GridUnitType.Absolute)},
    new ColumnDefinition { Width = new GridLength ( 1,GridUnitType.Star)},
    new ColumnDefinition { Width = new GridLength ( 2,GridUnitType.Star)},
    new ColumnDefinition { Width = new GridLength ( 140,GridUnitType.Absolute)}
}

f:id:TakasDev:20160518001228p:plain

Grid要素の配置

Gridに対する要素の配置は、下記のようなソースで行っています。

grid.Children.Add(new Label { Text = "(0,1,0,1)", BackgroundColor = Color.Black }, 0, 1, 0, 1);
grid.Children.Add(new Label { Text = "(1,2,0,1)", BackgroundColor = Color.White }, 1, 2, 0, 1);
grid.Children.Add(new Label { Text = "(2,3,0,1)", BackgroundColor = Color.Blue }, 2, 3, 0, 1);
grid.Children.Add(new Label { Text = "(3,4,0,1)", BackgroundColor = Color.Pink }, 3, 4, 0, 1);
grid.Children.Add(new Label { Text = "(0,1,1,2)", BackgroundColor = Color.Silver }, 0, 1, 1, 2);
grid.Children.Add(new Label { Text = "(1,2,1,2)", BackgroundColor = Color.Purple }, 1, 2, 1, 2);

Text要素が、上図の(x,x,x,x)で表記されている内容です。

下図のような感じの考えたでしょうか?(手書き汚くてすみません)

f:id:TakasDev:20160518001432p:plain

なので、(0,1,0,2)と指定してやることで

下図のようにRowをまたがった描画も可能となります。

f:id:TakasDev:20160518001456p:plain

ただし、下記のように描画箇所がバッティングを起こすと

後に宣言されたものが優先して描画されます。

grid.Children.Add(new Label { Text = "(0,1,0,2)", BackgroundColor = Color.Black }, 0, 1, 0, 2);
grid.Children.Add(new Label { Text = "(0,1,1,2)", BackgroundColor = Color.Silver }, 0, 1, 1, 2);

f:id:TakasDev:20160518001551p:plain

Column数以上の数値を指定したとき

ここ、少し戸惑いました。

基本形を下図とします。

f:id:TakasDev:20160518001712p:plain

grid.Children.Add(new Label { Text = "(3,5,0,2)", BackgroundColor = Color.Blue }, 3, 5, 0, 2);

上記コードを突っ込んだときエラーになるかというとなりませんでした。

下図のようになります。

見にくいですが、2/3列目が縮小されています。
f:id:TakasDev:20160518001805p:plain

2/3列目をAbsolute属性に変更し、同様のソースで実行したところ

下図のようになりました。
f:id:TakasDev:20160518002002p:plain

領域をフルに活用して描画するようになっているのでしょうか?

Column数が拡張されているように見受けられます。

ちなみに、下のようなソースで実行した場合

grid.Children.Add(new Label { Text = "(4,100,0,1)", BackgroundColor = Color.Maroon }, 4, 100, 0, 1);
grid.Children.Add(new Label { Text = "(4,20,1,2)", BackgroundColor = Color.Maroon }, 4, 20, 1, 2);

下図のような描画となります。
f:id:TakasDev:20160518002250p:plain

残領域で100カラム作っているんですかね?

Gridのここらへんの動作分かってないと

ほぼエラーが発生しないんでドツボにはまることになるかもしれません。

まとめ

気を付けなければいけない部分はありますが

Gridを活用すれば、イケている画面を作成する余地は十分にありそうです。

Congnitive Serviceを使ってみる - App実行編(Adult検証) -

続きです。

完全に釣りタイトルかもしれませんが

VisionがXamarinでも使用できるのであれば

顔認識系のアプリもさくっとできるかもしれない。

というかそれがしたいがために、このサービスの検証をしています。

「Adult」はAPIの中にあったんで、気になって検証しました。

だって男の子ですし

コンソールアプリケーションで試す(Code)

以下のサイトを参考にコンソールからCongnitive Serviceを使用してみます。

Microsoft Cognitive Services - Documentation

NuGetでVisionを取得
f:id:TakasDev:20160508032259p:plain

ソース

VisionServiceClient VisionServiceClient = new VisionServiceClient({SUBSCRIPTION_KEY});
AnalysisResult analysisResult;

using (Stream imageFileStream = File.OpenRead({FILE_PATH}))
{
    VisualFeature[] visualFeatures = new VisualFeature[] { VisualFeature.Adult, VisualFeature.Categories, VisualFeature.Color, VisualFeature.Description, VisualFeature.Faces, VisualFeature.ImageType, VisualFeature.Tags };
    analysisResult = await VisionServiceClient.AnalyzeImageAsync(imageFileStream, visualFeatures);
}

これだけです。

これだけでComputerVisionの恩恵をあずかれるのは素晴らしいですね。

動かしてみる

自分の写真を使ってみました(実際に目線はないしサイズももうちょっとでかいです)

・こんなん
f:id:TakasDev:20160508032504p:plain

実行結果
f:id:TakasDev:20160508032714p:plain

きちんと返ってきました。中身も色々と興味深いです。

現在年齢は27、1歳など誤差の範囲なので、いい感じの認識能力です。

GenderはFemale??

MSでは、Female=男なの?と訝しみ、Bingで翻訳してみましたが「女性」と出ましたので

やはり、自分はMS的には女性として認識されているようです。
(日本人の顔認識は苦手なのかな?)

「Adult」が気になる
f:id:TakasDev:20160508032545p:plain

IsAdultContent = False?

もしかして、これ画像が成人コンテンツか判断しているのか?

気になるので検証してみました。

いざAdult検証

というわけで、検証のため、検証のため

肌色多めのあだるてぃな画像を用意して検証してみました。
(さすがに画像は載せれません)

結果
f:id:TakasDev:20160508033210p:plain
Adult認証!

やはり画像が成人コンテンツかどうか判定するものなんですね。

ちなみに、↓みたいな肌色一色の画像はFalseになりました。結構有能っぽいです。
f:id:TakasDev:20160508033350p:plain

ちょっと真面目にAdult考察

まぁ画像1・2個試しただけなので、その精度はいかがなものか?というのもありますが

お子さん向けのアプリケーションで、フィルタをかけるのにいいかもしれません。

また、MobileApp系の審査って、成人コンテンツに敏感なイメージがあります

Upload&閲覧系のアプリケーションの成人コンテンツのブロックにもいいかもしれませんね。

しかし、2次元とか水着とか肌の色がアバターとか着衣とかどうなるんだろう…?

明日にでも試そうと思います。んでXamarinの人柱erに戻ります。

今日はもう限界なのでここまで。

Congnitive Serviceを使ってみる - 導入編 -

JXUGとても楽しかったです。

色々と昂ってきたので、冷めやらないうちに色々やろうと思います。

とりあえずCongnitiveServieを!(ぇ

準備

CongnitiveServieのトライアル版を下記サイトで試すことができます。

www.microsoft.com


f:id:TakasDev:20160508025925p:plain
「GetStartedFree」からトライアル版の申し込みが可能です。

Microsoftアカウントが必要となりますので、ない人は事前に取得が必要です。

登録が完了すると「MyAccount」から登録したプロダクトの確認が行えます。

プロダクトの確認画面
f:id:TakasDev:20160508030057p:plain

Preview版は下記の通りの制限があるようです。

Face:30,000トランザクション/月 20回/分

ComputerVision:5,000トランザクション/月 20回/分

APIの動作の確認

使用の仕方はAPIのリファレンスがあるので↓を参考に。
Microsoft Cognitive Services

上記ページの「OPEN API TESTING CONSOLE」から

コード記述なしで実際の動きを確認することができます。

TESTING CONSOLE画面
f:id:TakasDev:20160508030446p:plain

  • VisualFeatures:顔認識の「Face」を選択。
  • Subscription-Key:上図の「MyAccount」画面で表示されるKey。
  • RequestBody:顔認識を行いたい画像のURLを指定。


画面下部の「Send」押下で処理が実行されます。

実行結果
f:id:TakasDev:20160508030754p:plain

結果は、上記に様に返却されます。

今回は1万円の諭吉さんの画像を使用したので

年齢・性別ともに、いい感じに認識できていると思います。

あとはJson.Netを使用して

ほしい情報を引っこ抜けばいいわけですね。


長くなるので「App実行編」に続きます。