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)
先に参考
Angular2 with TypeScript using Visual Studio 2015 - CodeProject
Angular2 Typescript in ASP.NET MVC Visual Studio 2015 – Russell Canfield – Code Blog
環境構築
1.ひとまずプロジェクトを作成
業務の都合上VB.netでプロジェクト作成していますが
今回記述ソースは.net側は、ほとんど何もありません。
VB.netなのでMVC5はテンプレート上存在しないので、MVC4を選択しました。
2.npmのpackage.jsonファイルを追加
rootにpackage.jsonを追加します。
記述するソースは下記のとおり。
- 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.パッケージインストール
5.Rootディレクトリにtsconfig.jsonを作成
- 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に下記のエラーが発生
まずひとつ。TypeScript古いですよー。ってメッセージが出る。
これ、Resharperが入っている場合にでるエラーだそう。
ReSharperの設定をいじることで解決。
残りのエラー
これは、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>
Xamarin.FormsのGridについて
下図の画面をGridで作成するための前準備のお話し。
Gridの仕様(?)が結構特殊な感じだったためφ(..)
Xamarin.FormsのAngerの内容を流用しているため、Xamlの話ではありません。
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の配置となります。
それぞれ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)} }
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)で表記されている内容です。
下図のような感じの考えたでしょうか?(手書き汚くてすみません)
なので、(0,1,0,2)と指定してやることで
下図のようにRowをまたがった描画も可能となります。
ただし、下記のように描画箇所がバッティングを起こすと
後に宣言されたものが優先して描画されます。
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);
Column数以上の数値を指定したとき
ここ、少し戸惑いました。
基本形を下図とします。
grid.Children.Add(new Label { Text = "(3,5,0,2)", BackgroundColor = Color.Blue }, 3, 5, 0, 2);
上記コードを突っ込んだときエラーになるかというとなりませんでした。
下図のようになります。
見にくいですが、2/3列目が縮小されています。
2/3列目をAbsolute属性に変更し、同様のソースで実行したところ
下図のようになりました。
領域をフルに活用して描画するようになっているのでしょうか?
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);
下図のような描画となります。
残領域で100カラム作っているんですかね?
Gridのここらへんの動作分かってないと
ほぼエラーが発生しないんでドツボにはまることになるかもしれません。
まとめ
気を付けなければいけない部分はありますが
Gridを活用すれば、イケている画面を作成する余地は十分にありそうです。
Congnitive Serviceを使ってみる - App実行編(Adult検証) -
続きです。
完全に釣りタイトルかもしれませんが
VisionがXamarinでも使用できるのであれば
顔認識系のアプリもさくっとできるかもしれない。
というかそれがしたいがために、このサービスの検証をしています。
「Adult」はAPIの中にあったんで、気になって検証しました。
だって男の子ですし
コンソールアプリケーションで試す(Code)
以下のサイトを参考にコンソールからCongnitive Serviceを使用してみます。
Microsoft Cognitive Services - Documentation
NuGetでVisionを取得
ソース
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の恩恵をあずかれるのは素晴らしいですね。
動かしてみる
自分の写真を使ってみました(実際に目線はないしサイズももうちょっとでかいです)
・こんなん
実行結果
きちんと返ってきました。中身も色々と興味深いです。
現在年齢は27、1歳など誤差の範囲なので、いい感じの認識能力です。
GenderはFemale??
MSでは、Female=男なの?と訝しみ、Bingで翻訳してみましたが「女性」と出ましたので
やはり、自分はMS的には女性として認識されているようです。
(日本人の顔認識は苦手なのかな?)
「Adult」が気になる
IsAdultContent = False?
もしかして、これ画像が成人コンテンツか判断しているのか?
気になるので検証してみました。
いざAdult検証
というわけで、検証のため、検証のため
肌色多めのあだるてぃな画像を用意して検証してみました。
(さすがに画像は載せれません)
結果
Adult認証!
やはり画像が成人コンテンツかどうか判定するものなんですね。
ちなみに、↓みたいな肌色一色の画像はFalseになりました。結構有能っぽいです。
ちょっと真面目にAdult考察
まぁ画像1・2個試しただけなので、その精度はいかがなものか?というのもありますが
お子さん向けのアプリケーションで、フィルタをかけるのにいいかもしれません。
また、MobileApp系の審査って、成人コンテンツに敏感なイメージがあります
Upload&閲覧系のアプリケーションの成人コンテンツのブロックにもいいかもしれませんね。
しかし、2次元とか水着とか肌の色がアバターとか着衣とかどうなるんだろう…?
明日にでも試そうと思います。んでXamarinの人柱erに戻ります。
今日はもう限界なのでここまで。
Congnitive Serviceを使ってみる - 導入編 -
JXUGとても楽しかったです。
色々と昂ってきたので、冷めやらないうちに色々やろうと思います。
とりあえずCongnitiveServieを!(ぇ
準備
CongnitiveServieのトライアル版を下記サイトで試すことができます。
「GetStartedFree」からトライアル版の申し込みが可能です。
Microsoftアカウントが必要となりますので、ない人は事前に取得が必要です。
登録が完了すると「MyAccount」から登録したプロダクトの確認が行えます。
プロダクトの確認画面
Preview版は下記の通りの制限があるようです。
Face:30,000トランザクション/月 20回/分
ComputerVision:5,000トランザクション/月 20回/分
APIの動作の確認
使用の仕方はAPIのリファレンスがあるので↓を参考に。
Microsoft Cognitive Services
上記ページの「OPEN API TESTING CONSOLE」から
コード記述なしで実際の動きを確認することができます。
TESTING CONSOLE画面
- VisualFeatures:顔認識の「Face」を選択。
- Subscription-Key:上図の「MyAccount」画面で表示されるKey。
- RequestBody:顔認識を行いたい画像のURLを指定。
画面下部の「Send」押下で処理が実行されます。
実行結果
結果は、上記に様に返却されます。
今回は1万円の諭吉さんの画像を使用したので
年齢・性別ともに、いい感じに認識できていると思います。
あとはJson.Netを使用して
ほしい情報を引っこ抜けばいいわけですね。
長くなるので「App実行編」に続きます。
EmployeeDirectoryを弄ってみた
以前に書いた通り、EmployeeDirectoryのマージをしました。
ただ、思った以上にサンプルとしては内容が盛りだくさんな感じなので
ポイント部分と動作イメージだけさらっと
結果として、EmployeeDirectoryは入門編にはちょうどいい感じがしました。
構成
サンプルのソリューションの構成が今までのと違います。
UIとビジネスロジックをソリューションから分離している感じですね。
というかこのサンプル、ログイン画面込みの構成なんで
最初からこれを使えばよかった…と思ったのは別の話。
一応自分が作ったマージするほうも同様に、下図の様にUIとロジックで分離しました
これ関係ぜんぶ書き出すとめちゃくちゃ長くなりそうなので
一部だけ
ログイン画面の遷移
App.cs内の初期ページの指定として、ログイン画面ではなくMain画面を指定します
App.cs
public App() { var task = Task.Run(async () => { //CSVをメモリに格納 Service = await MemoryDirectoryService.FromCsv("XamarinDirectoryTemp.csv"); }); task.Wait(); //ログインページではなくMainPageを指定 MainPage = new NavigationPage(new MainPage()); }
また、上記で指定したMainPage.csのOnAppearingイベント内で
LoginPageをPush表示します。
これで、App起動時にLogin画面が開かれることになります。
MainPage.cs
protected async override void OnAppearing() { base.OnAppearing(); if (LoginViewModel.ShouldShowLogin(App.LastUseTime)) { if (!LoginPageAdd) { LoginPageAdd = true; //ログインページを表示 await Navigation.PushModalAsync(new LoginPage()); } } favoritesRepository = await XmlFavoritesRepository.OpenIsolatedStorage("XamarinFavorites.xml"); if (favoritesRepository.GetAll().Count() == 0) { favoritesRepository = await XmlFavoritesRepository.OpenFile("XamarinFavorites.xml"); } viewModel = new FavoritesViewModel(favoritesRepository, true); }
ログイン成功時はPopModaiAsyncでMain画面に戻ります。
※単純にこの実装だけだと、Androidの「戻る」ボタン押下で
Main画面を見れてしまうのですが(^^;)
まぁその制御はおいおい。
PCLStorage
このサンプルでは、端末のローカルストレージにCSVファイルを落とし
CSVの内容からLINQでUserViewModelの配列を生成しています。
端末のローカルストレージに落とす場合
PCLStorageの機能を使用して
同一のロジックでファイル操作を行っているようです。
MemoryDirectoryService.cs
public async static Task<MemoryDirectoryService> FromCsv(string path) { //PCLStorage機能を使用しファイル操作 IFolder store = FileSystem.Current.LocalStorage; var file = await store.GetFileAsync(path); using (var reader = new StreamReader(await file.OpenAsync(FileAccess.Read))) { return FromCsv(reader); } }
これは↓に格納されるようです。
動作させた
ログイン後はMasterDetailページとなります。
MasterDetailのサンプル画面から
EmployeeListの画面に遷移します。
(サンプルから中身弄ってないんで頭だけ出しときます)
まとめ
ページ遷移からファイル操作、ViewModelを使用したMVVMな作り等
EmployeeDirectoryのサンプルは盛りだくさんといった感じです。
なので、とっかかりとしてやる分には非常にいいのではないかな?
と思います。
備忘録:Xamarin系
Link貼り間違えてました…ので修正。
とりあえず、Xamarin.Formsで色々やりたいこと調べたので備忘録的にφ(..)
Xamarin.FormsでPopUpViewを出したいときに使えそう
SearchBarの使い方
Xamarin.Formsのイケてるデザイン指南?
Xamarin.Formsコントロールのチートシート
Xamarin.Formsのサンプルで遊んでみたよ!
Evoleve2016始まりましたね!
どんな発表があるかドキドキですね!
サンプルで遊ぶ
Hello, iOSですとかHello Androidもいいですけど
実際にゴリゴリ動いているのを見るとソースをいじるのが楽しくなりますよね。
今回、Xamarin.FormsのGitHubで提供されている、2つのSampleを組み合わせて遊んでみました。
今回組み合わせたサンプル2つ
xamarin-forms-samples/Navigation/LoginFlow/
xamarin-forms-samples/Navigation/MasterDetailPage/
使用しているサンプル名称を見ていただければわかる通りですが
ログイン画面でログイン→MasterDetailページに遷移
といった単純な構成です。とっかりりとしてはこれぐらいがちょうどいいですね。
※MasterDetailPageの構成なんかは、上記のGitHub内にScreenShotがありますのでご覧くださいませ。
まずはサンプルソースをざっと眺めてみる
LoginFlow
開いて早速エラーが出まくったり、MVVMチックな構成ではなかったりと
気になるところは多々ありますが、サンプルを下敷きに新しく作り直せば問題ありません。
LoginPageCS.cs
LoginPage.xaml
まず上の2つのファイルがその名の通りLoginPageのView部分ですね
C#のコードのみでも構成できますし、XAMLを使用することも可能です。
Sampleを組み合わせるうえでLogInPage側の肝のLogicは下記部分ですね。
(実際には色々基本的な部分で重要な部分はいっぱいありますよ!)
LoginPage.xaml,cs
async void OnLoginButtonClicked (object sender, EventArgs e) { var user = new User { Username = usernameEntry.Text, Password = passwordEntry.Text }; var isValid = AreCredentialsCorrect (user); if (isValid) { App.IsUserLoggedIn = true; Navigation.InsertPageBefore (new MainPage (), this); await Navigation.PopAsync (); } else { messageLabel.Text = "Login failed"; passwordEntry.Text = string.Empty; } }
入力されたユーザー名称とパスワードが一致しますかー?というものを確認。
Navigation.InsertPageBeforeで遷移先のページを生成し、Navigation.PopAsyncでページ遷移を行っています。
今回やりたいのは、このMainPageをMasterPageDetailのSampleで使用されているMain画面にするだけです。
MasterDetailPage
こちらはLogic部分で変更する箇所はありません。
ただ、押さえておきたいのは、下記の内容です。
MainPage.xaml
<?xml version="1.0" encoding="UTF-8"?> <MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:MasterDetailPageNavigation;assembly=MasterDetailPageNavigation" x:Class="MasterDetailPageNavigation.MainPage"> <MasterDetailPage.Master> <local:MasterPage x:Name="masterPage" /> </MasterDetailPage.Master> <MasterDetailPage.Detail> <NavigationPage> <x:Arguments> <local:ContactsPage /> </x:Arguments> </NavigationPage> </MasterDetailPage.Detail> </MasterDetailPage>
CSのコードハイライトはちと役不足感があるけどまぁいいか
MainPage.xaml.cs
public MainPage () { InitializeComponent (); masterPage.ListView.ItemSelected += OnItemSelected; if (Device.OS == TargetPlatform.Windows) { Master.Icon = "swap.png"; } } void OnItemSelected (object sender, SelectedItemChangedEventArgs e) { var item = e.SelectedItem as MasterPageItem; if (item != null) { Detail = new NavigationPage ((Page)Activator.CreateInstance (item.TargetType)); masterPage.ListView.SelectedItem = null; IsPresented = false; } }
MainPageがMasterとDetailに分かれており、それぞれページが指定されています。
Maseterがわが「MasterPage」Detail側が「ContactsPage」です。
リストアイテムの選択処理はMasterPage(子)のCSではなく、MainPage(親)で行われています。
XAML側7行目で配置された、MasterPageに配置されたListViewのEventをキャッチし、選択処理が行われています。
NavigationPageでページ遷移を行っています。
item.TargetTypeはMainPageやContentPage等のXAMLで作成したページ名称ですね。
では組み合わせてみよう
前述の通り、Sampleをそのままですとエラーが多すぎるため
新しいソリューションを立ち上げて、ソースを組み合わせていきます。
途中過程はすっとばしまして、ソリューションの構成は下記の通りです。
MVVMっぽい構成になりました。
Mock内にはSampleUserをつっこんでます。
ViewModel内には、UserViewModelとMenuのViewModelですね。
Xamarin.Droid,/iOsにはResourceとして、アイコン情報を登録しただけです。
Droidは \Resources\drawableに
iOSは \Resourcesに格納しました。
デバイス側にはコードを1行も記述していないです。スバラシイ
実行
ログインページでた!
が、ログイン処理時にエラー!!
やはりすんなりはいかないか…
エラーの内容を確認すると
「Navigationページは1ページ1個しか認めませんよ!」という内容のようです。
そりゃそうだ。
では、ここでXAMLを見ていきます。
たしかに、MasterDetailを使用しているMainPage.xamlは
しかし、LoginPageには
では、どこにあるのでしょうか。
App.cs
public App() { if (!IsUserLoggedIn) { MainPage = new NavigationPage(new LoginPage()); } else { MainPage = new NavigationPage(new MainPage()); ; } }
App.csでアプリケーション立ち上げ時のページを指定していますが
ここの部分で、LoginPageがNavigationPageとしてNewされています。
だから、ログインアクション時の、「Navigation.InsertPageBefore (new MainPage (), this)」が使用できるわけですね。
対処療法的に対処してみる
変更後のMainPage.xaml
<?xml version="1.0" encoding="UTF-8"?> <MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamarinSample.Views;assembly=XamarinSample" x:Class="XamarinSample.Views.MainPage"> <MasterDetailPage.Master> <local:MasterPage x:Name="MasterPage" /> </MasterDetailPage.Master> <MasterDetailPage.Detail> <!--<NavigationPage>--> <!--<x:Arguments>--> <local:ContactsPage /> <!--</x:Arguments>--> <!--</NavigationPage>--> </MasterDetailPage.Detail> </MasterDetailPage>
完全に対処療法ですが
すでにLoginPageからNavigationを使用したページ遷移が行われているわけですし
改めてNavigationPageタグの宣言する必要ないよね?ってことでNavigationタグを消してみました。
まとめ
- Sampleソースは見るのもいじるのも勉強になります。
- いきなり作りたいものを作るより、Sampleを組み合わせるほうがイメージしやすいかも?
次
EmployeeDirectoryのSampleを流用して、ユーザー管理画面を作ってみようかなと考えています。
あぁ、でもBotFrameworkも気になってるんですよね
まぁ、明日から月曜なんで、遊ぶのはしばらくお預けです…。
ものすごい長くなったんで次回は抑えれるよう頑張ります。