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

Azure App Service で Azure Key Vault がめちゃ簡単に使えるようになりました(プレビュー)

$
0
0

プレビュー機能ですが、Azure Functions とかで Key Vault が凄く簡単に使えるようになってます。 今までは、少しだけとはいえプログラムに手を入れないといけなかった部分を Azure ポータルのアプリケーション設定に指定するだけでよしなにしてくれるようになっています。凄い便利。

例えば以下のような Key Vault なんて全く意識してない、単純に secret という設定を読んで返すだけの関数があったとします。

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Extensions.Configuration;

namespace HelloWorld
{
    publicstaticclass HttpTrigger
    {
        [FunctionName("HttpTrigger")]
        publicstatic async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
            ExecutionContext context,
            ILogger log)
        {
            var config = new ConfigurationBuilder()
                .AddJsonFile("local.settings.json", true)
                .AddEnvironmentVariables()
                .Build();
            returnnew OkObjectResult(config["secret"]);
        }
    }
}

ローカルで動かすときは local.settings.json に以下のように設定足せば動きます。

{"IsEncrypted": false,
    "Values": {"AzureWebJobsStorage": "",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet",
        "secret": "秘密だよ"
    }}

ローカルで実行して URL を叩くと 秘密だよって返ってきます。

Azure 上にデプロイして secret を Key Vault から読むようにしてみます。Function App のプラットフォーム機能で ID を選択します。

f:id:okazuki:20181213113739j:plain

システム割り当て済みをオンにします。

f:id:okazuki:20181213113827p:plain

これで Azure AD に登録されます。

次に Key Vault にいきます(なかったら作る)。そしてアクセスポリシーを開いて新規追加で、先ほどの Function App を追加します。シークレットのアクセス許可で取得をつけておきます。 そして、シークレットに適当な値を設定します。

f:id:okazuki:20181213114058p:plain

作成したシークレットの現在のバージョンを選択すると、シークレット識別子という URL がゲットできるので控えておきます。

f:id:okazuki:20181213114228p:plain

そして Function App に secret という設定名で @Microsoft.KeyVault(SecretUri=さっきゲットしたシークレット識別子)という値を設定します。

以上で完了です。関数を実行してみましょう。

f:id:okazuki:20181213122737p:plain

ちゃんと Key Vault に設定した Top secret value が返って来てますね。素晴らしい。

今後

現状特定バージョンのシークレットしか取れないっぽいけど改善していくっぽい。GA が楽しみな機能ですね。


Azure DevOps の Boards と GitHub の連携機能が追加されてる!?

$
0
0

Azure DevOps のプロジェクトの設定画面を開くと Boards に GitHub connections の項目が増えてました(アカウントによってはまだ来てないみたい)

f:id:okazuki:20181213145808p:plain

このページにあるアップデートがついに来た!!って感じですね。

azure.microsoft.com

Connect your GitHub account をクリックしてログインすると関連付けるリポジトリを選ぶ感じになります。

f:id:okazuki:20181213150700p:plain

リポジトリを選んだら後は GitHub の issue や Pull Request やコミットログに AB#タスクのIDという書式で書けば OK です。

試しに以下のような感じのコミットや、プルリクを作ってみました。

f:id:okazuki:20181213153116p:plain
コミット

f:id:okazuki:20181213153004p:plain
Pull Request

そうすると、Azure Boards のタスクの関連項目の中に…

f:id:okazuki:20181213153228p:plain
Azure Boards のタスク

出てきた!!GitHub のほうから Azure DevOps には飛べない(将来的に飛べるようになると嬉しいなぁ)けど、連携出来るようになったのはいいね!

Visual Studio 2019 Preview のリリースノート読んでみた

$
0
0

読んでみました。英語で見るか~って思ってたら日本語があってびっくりしました。ありがたい!!

docs.microsoft.com

読みながら気になってるところを適当に呟いてました。

JavaScript で作る UWP の機能が無くなりました。PWA で作ってから appx に固めてねってことみたい。 PWA で作って appx に固めてインストールした場合は JavaScript から Windows 10 の API 呼べるので強くなる。

あとは、個人的には白 or 黒のどちらか(黒が好きだけどデモするときは白のほうが見やすいと言われたことがあるので白にしてる)がいいんだけど、青テーマが新しくなった!!しばらく青にしようかな。

あと、これが個人的には一番ありがたいと思ってるのですがデバッグ中にブレークポイントとかで止めたときのローカル変数のウィンドウで検索できるの凄くいい。

Azure Repos (Azure DevOps の Git のリポジトリ機能)のプルリクエストが Visual Studio 2019 から触れるのとてもいい。ツイートにも書いたけど GitHub 対応も期待したいところ。

しばらく前に .editorconfig に Visual Studio が対応してたけど 2019 では Visual Studio の設定を .editorconfig にエクスポートできる。これは GUI でぽちぽち設定してエクスポートしてソリューションに含めてチーム共有できるのでチーム開発時のコーディングルール作るのが楽になりそう。

リファクタリングも凄く頑張ってて foreach を LINQ にするのがヤバイ。LINQ で書くと可読性低いという意見も見たような気がするけど、この foreach を読み解いて「あぁ、末尾が 10 の奴だけに絞って連結したものを集めてるのね」って解釈するのと LINQ を見て「Where で絞って Select で連結したものをリストに入れてるのね」って解釈するのだと LINQ のほうが理解しやすいと思うんだよね。

まぁ、お化けみたいな LINQ 書くとそれは可読性低いけど!でも、どこからお化けみたいな LINQ と思うかは練度にもよりそう。

あとはコードを綺麗にしてくれる機能(未使用の変数削除とか etc...)も強くなりました。結構色々ルールがありますね!プロジェクト全体に一括適用とかしたいな。何処かにあるのかなぁ。

因みに呟きのメソッドの先頭に汎用変数 int I, j, k;と書いてあるのは実際に現場で見たことのある 1000 行以上のメソッドで書いてありました。注意深く読んでいったら未使用の変数だったので若干殺意の波動に目覚めそうになったのを思い出しました。

そして、デフォルトでは PCL の機能が入らない。もう時代は完全に .NET Standard ですね。これから採用するライブラリは PCL のままだとしたら採用ためらってもいいかもしれません。

そして、OSS でいいものがあるから独自で作るのやめようぜって流れ系が 2 つ。

そして、ついに Xamarin.Forms の XAML でプロパティウィンドウが使えるようになりました!!デザイナーは無い(プレビュー + プロパティウィンドウで個人的には大体満足)

LiveShare も正式リリースが楽しみな機能ですよね。VS2019 で開発して共有先が VSCode とかもいけるので、みんなどんどんコラボレーションしていくといいと思う!というかプロトコルやらなんやらをオープンにしてくれたら、色々な開発環境でシェアが出来るようになるのでは?とか思った。

あとはインテリコードの実力が気になる。まだ入れて有効化してみた段階だから今後使っていってどう感じるかは呟いていくと思います。

まとめ

Visual Studio は個人的に一番手に馴染んでる IDE なので、今後もより軽く、そしてより強くなってほしいと思ってるから目が離せません! そして週刊 Visual Studio Update は、もうちょい作業の中断時間が少なくなるようにしてほしいw

Xamarin.Forms で AI をアプリに組み込んでみよう(UWP, Android, iOS)

$
0
0

Android は TensorFlow、iOS は CoreML、Windows 10 は onnx という感じで各 OS でディープラーニングの学習モデルをサポートするような API が追加されてきてますね!

Xamarin.Forms を使えば Android, iOS, UWP アプリの開発が同時に出来る(UI を各 OS ごとに凝るなら Xamarin Native を選んだ方が最終的に楽なケースもあるけど)ので、うまいことやればインターネット接続不要で画像判別とかを AI ちっくにやるようなアプリが全部 C# で書けそうなのでやってみましょう。

やりながら書くので、最終的にダメでしたになる可能性もありますがとりあえずね。

モデルの作成

Tensorflow とか CNTK とか etc... を使ってディープラーニングするのが一番いんでしょうが、そこらへんから勉強してたらブログ記事書くのに何か月もかかるので今回は Microsoft Cognitive Services の Custom Vision を使ってみたいと思います。

これは、ポータルサイトで画像登録してタグづけしてトレーニングさせると WebAPI や CoreML や onnx や TensorFlow などの形で公開できる素敵なやつです。

docs.microsoft.com

そこに、今回は Microsoft の Drew さんが結構前に作ってくれたハンズオンにある揚げ物かどうかを判定する学習用の画像セットを使って学習されたものをエクスポートして使ってみたいと思います。

github.com

余談ですが Drew さんが先日行われた TechSummit でセッションしたのですが、最初に powerpoint.exe をデリートしてデモとコードメインで色んな人とやりとりしながらやってたセッションは面白かったです。

そうこうしてるうちにリポジトリのクローンが終わったので https://customvision.aiにアクセスしてプロジェクトを作ります。

プロジェクトを作るときに Project Types は Classification、Domains は General (compact) を選びます。

Add images を選択してダウンロードしたファイルの中の assets/Training/Fries 以下の画像を Fries とタグ付けしてアップロードします。 NotFries フォルダの画像を NotFries でタグ付けしてアップロードします。

アップロードが完了したら Train をして Performance から Export を選んでモデルをダウンロードします。

f:id:okazuki:20181217155056p:plain

ONNX だけバージョンが選択できるのですが、今回は最新の Windows 10 SDK を使おうと思うので ONNX 1.2 を選択しました。

Xamarin.Forms アプリの作成

Xamarin.Forms でアプリを作りましょう。Blank で Android, iOS, UWP で .NET Standard を選択して OK を選びます。

f:id:okazuki:20181217155559p:plain

画像撮影機能の追加

前は Xamarin Plugins を使うのが一般的でしたが今は Xamarin.Essentials を使う方がいいのかな?

docs.microsoft.com

と思ってみたらカメラ無い??えっ???まじで???ということで Media Plugin for Xamarin and Windows を使います。

www.nuget.org

全プロジェクトに上記パッケージをインストールします。インストールすると readme.txt が出てくるので、それに従って各プロジェクトの設定を変更します。

設定が終わったら MainPage.xaml を以下のようにします。画像表示用コントロールと写真をとるためのボタンを置いてます。

<?xml version="1.0" encoding="utf-8"?><ContentPagex:Class="AIApp.MainPage"xmlns="http://xamarin.com/schemas/2014/forms"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"xmlns:local="clr-namespace:AIApp"Title="Safe Area"ios:Page.UseSafeArea="True"><StackLayout><Imagex:Name="picture"Aspect="AspectFill"VerticalOptions="FillAndExpand" /><Label x:Name="output"HorizontalOptions="CenterAndExpand" /><StackLayout Orientation="Horizontal"><ButtonClicked="PickPhotoButton_Clicked"HorizontalOptions="FillAndExpand"Text="Pick a picture" /><ButtonClicked="TakePhotoButton_Clicked"HorizontalOptions="FillAndExpand"Text="Take a picture" /></StackLayout></StackLayout></ContentPage>

として、MainPage.xaml.cs を以下のようにして AI を使う手前まで実装します。

using Plugin.Media;
using Plugin.Media.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace AIApp
{
    publicpartialclass MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private async void TakePhotoButton_Clicked(object sender, EventArgs e)
        {
            await ProcessPhotoAsync(true);
        }

        private async void PickPhotoButton_Clicked(object sender, EventArgs e)
        {
            await ProcessPhotoAsync(false);
        }

        private async Task ProcessPhotoAsync(bool useCamera)
        {
            await CrossMedia.Current.Initialize();
            if (useCamera ? !CrossMedia.Current.IsTakePhotoSupported : !CrossMedia.Current.IsPickPhotoSupported)
            {
                await DisplayAlert("Info", "Your phone doesn't support photo feature.", "OK");
                return;
            }

            var photo = useCamera ? 
                await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions()) : 
                await CrossMedia.Current.PickPhotoAsync();
            if (photo == null)
            {
                picture.Source = null;
                return;
            }

            picture.Source = ImageSource.FromFile(photo.Path);
            // TODO: using AI.
        }
    }
}

これで撮影して画像を表示するところまで出来ました。Android 9 のエミュレーターで動かすと以下のように動きます。

f:id:okazuki:20181217165621p:plain
起動直後

f:id:okazuki:20181217165654p:plain
カメラ起動中

f:id:okazuki:20181217165725p:plain
撮影後

では、ここに AI の機能を追加していきましょう。

インターフェースを考える

ここから先は各プラットフォームごとの実装が必要になります。.NET Standard のプロジェクトにはインターフェースを作成します。画像のストリームを受け取って Fries か NotFries の enum を返すようにしました。

using System.IO;
using System.Threading.Tasks;

namespace AIApp
{
    publicinterface IPhotoDetector
    {
        Task<FriesOrNotFriesTag> DetectAsync(Stream photo);
    }

    publicenum FriesOrNotFriesTag
    {
        None,
        Fries,
        NotFries,
    }
}

そして MainPage.xaml.cs に、このインターフェースを使った処理を書きます。

using Plugin.Media;
using Plugin.Media.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace AIApp
{
    publicpartialclass MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private async void TakePhotoButton_Clicked(object sender, EventArgs e)
        {
            await ProcessPhotoAsync(true);
        }

        private async void PickPhotoButton_Clicked(object sender, EventArgs e)
        {
            await ProcessPhotoAsync(false);
        }

        private async Task ProcessPhotoAsync(bool useCamera)
        {
            await CrossMedia.Current.Initialize();
            if (useCamera ? !CrossMedia.Current.IsTakePhotoSupported : !CrossMedia.Current.IsPickPhotoSupported)
            {
                await DisplayAlert("Info", "Your phone doesn't support photo feature.", "OK");
                return;
            }

            var photo = useCamera ? 
                await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions()) : 
                await CrossMedia.Current.PickPhotoAsync();
            if (photo == null)
            {
                picture.Source = null;
                return;
            }

            picture.Source = ImageSource.FromFile(photo.Path);

            var service = DependencyService.Get<IPhotoDetector>();
            if (service == null)
            {
                await DisplayAlert("Info", "Not implemented the feature on your device.", "OK");
                return;
            }

            using (var s = photo.GetStream())
            {
                var result = await service.DetectAsync(s);
                output.Text = $"It looks like a {result}";
            }
        }
    }
}

この状態で実行すると、まだ実装がないので以下のようになります。

f:id:okazuki:20181217171239p:plain

プラットフォームごとの実装

では、各プラットフォームごとに実装していきます。

UWP

まずは UWP からいってみましょう。UWP では October 2018 Update で追加されたものを使うので UWP プロジェクトのプロパティの Application の Target version と Min version を 1809 に設定します。

f:id:okazuki:20181217171515p:plain

Windows 10 で使う API は Windows Machine Learning です。

docs.microsoft.com

Custom Vision からダウンロードした onnx ファイルを FriesOrNotFries.onnx` にリネームして UWP プロジェクトの Assets フォルダに追加します。 ビルドアクションはコンテンツにしておきましょう。

f:id:okazuki:20181217172253p:plain

こんな感じのコードが生成されているはずです。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Media;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.AI.MachineLearning;
namespace AIApp.UWP
{
    
    publicsealedclass FriesOrNotFriesInput
    {
        public ImageFeatureValue data; // BitmapPixelFormat: Bgra8, BitmapAlphaMode: Premultiplied, width: 227, height: 227
    }
    
    publicsealedclass FriesOrNotFriesOutput
    {
        public TensorString classLabel; // shape(-1,1)public IList<Dictionary<string,float>> loss;
    }
    
    publicsealedclass FriesOrNotFriesModel
    {
        private LearningModel model;
        private LearningModelSession session;
        private LearningModelBinding binding;
        publicstatic async Task<FriesOrNotFriesModel> CreateFromStreamAsync(IRandomAccessStreamReference stream)
        {
            FriesOrNotFriesModel learningModel = new FriesOrNotFriesModel();
            learningModel.model = await LearningModel.LoadFromStreamAsync(stream);
            learningModel.session = new LearningModelSession(learningModel.model);
            learningModel.binding = new LearningModelBinding(learningModel.session);
            return learningModel;
        }
        public async Task<FriesOrNotFriesOutput> EvaluateAsync(FriesOrNotFriesInput input)
        {
            binding.Bind("data", input.data);
            var result = await session.EvaluateAsync(binding, "0");
            var output = new FriesOrNotFriesOutput();
            output.classLabel = result.Outputs["classLabel"] as TensorString;
            output.loss = result.Outputs["loss"] as IList<Dictionary<string,float>>;
            return output;
        }
    }
}

では、これを使って先ほど作ったインターフェースの実装を作っていきます。

PhotoDetector.cs を UWP プロジェクトに作成して以下のようなコードを書きます。先ほどの自動生成されたファイルを使って画像を判別しています。

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.AI.MachineLearning;
using Windows.Graphics.Imaging;
using Windows.Media;
using Windows.Storage;
using Xamarin.Forms;

[assembly: Dependency(typeof(AIApp.UWP.PhotoDetector))]
namespace AIApp.UWP
{
    publicclass PhotoDetector : IPhotoDetector
    {
        private FriesOrNotFriesModel _model;
        public async Task<FriesOrNotFriesTag> DetectAsync(Stream photo)
        {
            await InitializeModelAsync();
            var bitmapDecoder = await BitmapDecoder.CreateAsync(photo.AsRandomAccessStream());
            var output = await _model.EvaluateAsync(new FriesOrNotFriesInput
            {
                data = ImageFeatureValue.CreateFromVideoFrame(VideoFrame.CreateWithSoftwareBitmap(await bitmapDecoder.GetSoftwareBitmapAsync())),
            });
            var label = output.classLabel.GetAsVectorView().FirstOrDefault();
            return Enum.Parse<FriesOrNotFriesTag>(label);
        }

        private async Task InitializeModelAsync()
        {
            if (_model != null)
            {
                return;
            }

            var onnx = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/FriesOrNotFries.onnx"));
            _model = await FriesOrNotFriesModel.CreateFromStreamAsync(onnx);
        }
    }
}

この状態で UWP のプロジェクトをスタートアッププロジェクトにすると以下のように画像認識機能が動きます。

f:id:okazuki:20181217174955p:plain
ちらし寿司はフライじゃないと判定された様子

f:id:okazuki:20181217175041p:plain
ポテトフライがフライと認識された様子

Android

Android で使う API は…、ネイティブの Android だと以下のライブラリになるみたいです。

Maven Repository: org.tensorflow » tensorflow-android

実際に blog.xamarin.com の中に、これをネイティブバインディングしてるライブラリの Xam.Android.Tensorflow を使っている記事があります。

(Using TensorFlow and Azure to Add Image Classification to Your Android Apps)https://blog.xamarin.com/android-apps-tensorflow/

NuGet はこちら。

www.nuget.org

実際に Android プロジェクトに Xam.Android.Tensorflow を追加します。そして、上記ブログと以下の Java のサンプルを参考にしてコードを書きます。

github.com

まず、model.pb と labels.txt を Android プロジェクトの Assets フォルダーに追加して、ビルドアクションを AndroidAsset に設定します。PhotoDetector.cs を Android プロジェクトに追加して以下のように実装します。

using Android.Graphics;
using Org.Tensorflow.Contrib.Android;
using Plugin.CurrentActivity;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;

[assembly: Dependency(typeof(AIApp.Droid.PhotoDetector))]
namespace AIApp.Droid
{
    publicclass PhotoDetector : IPhotoDetector
    {
        privatestaticreadonlystring ModelFile = "model.pb";
        privatestaticreadonlystring LabelFile = "labels.txt";
        privatestaticreadonlystring InputName = "Placeholder";
        privatestaticreadonlystring OutputName = "loss";
        privatestaticreadonlyint InputSize = 227;
        privatereadonly TensorFlowInferenceInterface _inferenceInterface;
        privatereadonlystring[] _labels;

        public PhotoDetector()
        {
            _inferenceInterface = new TensorFlowInferenceInterface(CrossCurrentActivity.Current.Activity.Assets, ModelFile);
            using (var sr = new StreamReader(CrossCurrentActivity.Current.Activity.Assets.Open(LabelFile)))
            {
                _labels = sr.ReadToEnd().Split('\n').Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x)).ToArray();
            }
        }

        public async Task<FriesOrNotFriesTag> DetectAsync(Stream photo)
        {
            var bitmap = await BitmapFactory.DecodeStreamAsync(photo);
            var floatValues = GetBitmapPixels(bitmap);
            var outputs = newfloat[_labels.Length];
            _inferenceInterface.Feed(InputName, floatValues, 1, InputSize, InputSize, 3);
            _inferenceInterface.Run(new[] { OutputName });
            _inferenceInterface.Fetch(OutputName, outputs);
            var index = Array.IndexOf(outputs, outputs.Max());
            return (FriesOrNotFriesTag)Enum.Parse(typeof(FriesOrNotFriesTag), _labels[index]);
        }

        private async Task<byte[]> LoadByteArrayFromAssetsAsync(string name)
        {
            using (var s = CrossCurrentActivity.Current.Activity.Assets.Open(name))
            using (var ms = new MemoryStream())
            {
                await s.CopyToAsync(ms);
                ms.Seek(0, SeekOrigin.Begin);
                return ms.ToArray();
            }
        }

        privatestaticfloat[] GetBitmapPixels(Bitmap bitmap)
        {
            var floatValues = newfloat[InputSize * InputSize * 3];
            using (var scaledBitmap = Bitmap.CreateScaledBitmap(bitmap, InputSize, InputSize, false))
            {
                using (var resizedBitmap = scaledBitmap.Copy(Bitmap.Config.Argb8888, false))
                {
                    var intValues = newint[InputSize * InputSize];
                    resizedBitmap.GetPixels(intValues, 0, resizedBitmap.Width, 0, 0, resizedBitmap.Width, resizedBitmap.Height);
                    for (int i = 0; i < intValues.Length; ++i)
                    {
                        var val = intValues[i];
                        floatValues[i * 3 + 0] = ((val & 0xFF) - 104);
                        floatValues[i * 3 + 1] = (((val >> 8) & 0xFF) - 117);
                        floatValues[i * 3 + 2] = (((val >> 16) & 0xFF) - 123);
                    }
                    resizedBitmap.Recycle();
                }
                scaledBitmap.Recycle();
            }

            return floatValues;
        }
    }
}

Android のエミュレーターで実行すると…

f:id:okazuki:20181218121351p:plain
フライとして認識された

f:id:okazuki:20181218121531p:plain
フライじゃないと認識された

iOS

続いて iOS の CoreML をしていきます。Xamarin の公式ドキュメントはこちら。

docs.microsoft.com

前に試したときは mac に mlmodel を持っていってコマンドを打ってとかしてましたが、そこらへんの手順は簡略化されてるみたいですね。iOS プロジェクトの Resources フォルダに mlmodel を追加します。そしてビルドアクションを CoreMLModel にします。

そして、PhotoDetector.cs を iOS プロジェクトに追加して以下のように実装します。

using CoreFoundation;
using CoreImage;
using CoreML;
using Foundation;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Vision;
using Xamarin.Forms;

[assembly: Dependency(typeof(AIApp.iOS.PhotoDetector))]
namespace AIApp.iOS
{
    publicclass PhotoDetector : IPhotoDetector
    {
        privatereadonly MLModel _mlModel;
        privatereadonly VNCoreMLModel _model;

        public PhotoDetector()
        {
            var assetPath = NSBundle.MainBundle.GetUrlForResource("FriesOrNotFries", "mlmodelc");
            _mlModel = MLModel.Create(assetPath, out var _);
            _model = VNCoreMLModel.FromMLModel(_mlModel, out var __);
        }

        public Task<FriesOrNotFriesTag> DetectAsync(Stream photo)
        {
            var taskCompletionSource = new TaskCompletionSource<FriesOrNotFriesTag>();
            void handleClassification(VNRequest request, NSError error)
            {
                var observations = request.GetResults<VNClassificationObservation>();
                if (observations == null)
                {
                    taskCompletionSource.SetException(new Exception("Unexpected result type from VNCoreMLRequest"));
                    return;
                }

                if (!observations.Any())
                {
                    taskCompletionSource.SetResult(FriesOrNotFriesTag.None);
                    return;
                }

                var best = observations.First();
                taskCompletionSource.SetResult((FriesOrNotFriesTag)Enum.Parse(typeof(FriesOrNotFriesTag), best.Identifier));
            }

            using (var data = NSData.FromStream(photo))
            {
                var ciImage = new CIImage(data);
                var handler = new VNImageRequestHandler(ciImage, new VNImageOptions());
                DispatchQueue.DefaultGlobalQueue.DispatchAsync(() =>
                {
                    handler.Perform(new VNRequest[] { new VNCoreMLRequest(_model, handleClassification) }, out var _);
                });
            }

            return taskCompletionSource.Task;
        }
    }
}

実行すると…

f:id:okazuki:20181218163815p:plain
iPhone でフライを選んだ場合

f:id:okazuki:20181218163855p:plain
フライじゃないものを選択した場合

動いてますね!

ソースコード

GitHub に置いてます。

github.com

Livet 2.2.0 をリリースしました

$
0
0

しました。

www.nuget.org

LivetExtensions も更新しました。

www.nuget.org

プロジェクトテンプレートも最新版を初期状態で参照しているように Visual Studio の拡張機能も更新しています。

marketplace.visualstudio.com

変更内容

GitHub のリリースページをご確認ください。

github.com

ドキュメント更新

尾上さんの Blog は私は更新できないので内容を、ほぼほぼ GitHub の README のほうにうつしました。

github.com

まとめ

良いお年を。

ReactiveProperty のドキュメントを MkDocs から VuePress に変えてみたときの作業ログ

$
0
0

PC を修理交換したので ReactiveProperty のドキュメント生成に使ってた MkDocs を再セットアップするのではなく、もっと気軽に導入できる、今後も使い続けれて楽しそうなものはないか?という感じで探したところ VuePress が目についたので以降してみました。

vuepress.vuejs.org

前準備

ブランチを切って空コミットをして Pull Request を作って作業を開始です。

git checkout -b migrate-tovuepress
git commit --allow-empty -m "work in progress"
git push --set-upstream origin migrate-to-vuepress

フォルダー構造とかを変えました。

  • 旧 Docs フォルダーは古いドキュメントやプレゼン資料やアイコンが入っていたので Assets に移動しました。
  • MkDocs フォルダーに今のドキュメントが入っているので docs フォルダーに移動しました。

導入

node.js が入ってれば以下のコマンドで完了

npm i -g vuepress@next

docs フォルダーに package.json を作って .gitignore もおいて下準備は完成です。

npm init -y

ライセンスやらをなおして scripts に以下のように vuepress のコマンドのショートカットを追加しておきます。

"scripts": {"docs:dev": "vuepress dev docs",
  "docs:build": "vuepress build docs"
}

README.md がエントリーポイントっぽいので index.md からリネームします。

とりあえず動かしてみます。

npm run docs:dev

画像出ない(悲

f:id:okazuki:20190122092206p:plain

設定変更

ここを参考に設定ファイルを追加します。

vuepress.vuejs.org

docs/.vuepress/config.jsを追加して以下のようにしました。

module.exports = {
    title: 'ReactiveProperty documentation',
    description: 'Official document for ReactiveProperty(https://github.com/runceel/ReactiveProperty)'}

画像表示問題対処

.から始まる相対パスで書けばいいみたいなので書き直しました。

Before

![New Cross Platform App dialog](images/xf-create-project.png)

After

![New Cross Platform App dialog](./images/xf-create-project.png)

無事画像が出るようになりました。

f:id:okazuki:20190122093612p:plain

ナビゲーションバー生成

コンテンツ一覧的なものを出したいです。今のドキュメントは以下のように左にメニューがありますが VuePress で生成したものにはない。

f:id:okazuki:20190122093734p:plain

MkDocs で作った方には mkdocs.yml というファイルがあって、そこで指定してたという記憶があるので類似にものを探すことにした。

config.js にデフォルトテーマの設定を書けて、そこで色々できるみたいです。とりあえず Navbar をカスタマイズ。

vuepress.vuejs.org

ということで config.js は以下のようになりました。

module.exports = {
    title: 'ReactiveProperty documentation',
    description: 'Official document for ReactiveProperty(https://github.com/runceel/ReactiveProperty)',
    themeConfig: {
        nav: [{ text: 'Home', link: '/'},
            {
                text: 'Getting started',
                items: [{ text: 'Windows Presentation Foundation', link: '/getting-started/wpf.html'},
                    { text: 'Universal Windows Platform', link: '/getting-started/uwp.html'},
                    { text: 'Xamarin.Forms', link: '/getting-started/xf.html'},
                    { text: 'Avalonia', link: '/getting-started/avalonia.html'},
                ]},
            {
                text: 'Features',
                items: [{ text: 'ReactiveProperty', link: '/features/ReactiveProperty.html'},
                    { text: 'ReactivePropertySlim', link: '/features/ReactivePropertySlim.html'},
                    { text: 'Commanding', link: '/features/Commanding.html'},
                    { text: 'Collections', link: '/features/Collections.html'},
                    { text: 'Work together with plane model layer objects', link: '/features/Work-together-with-plane-model-layer-objects.html'},
                    { text: 'Useful classes which implement IObservable', link: '/features/Notifiers.html'},
                    { text: 'Extension methods', link: '/features/Extension-methods.html'},
                    { text: 'Transfer event to ViewModel from View', link: '/features/Event-transfer-to-ViewModel-from-View.html'},
                ]},
            {
                text: 'Advanced topics',
                items: [{ text: 'Thread control', link: '/advanced/thread.html'},
                    { text: 'Work with await operator', link: '/advanced/awaitable.html'},
                ]},
            { text: 'Samples', link: '/samples.html'},
        ]}}

上に出ました!

f:id:okazuki:20190122101742p:plain

C# のシンタックスハイライト

C# のコードを埋め込んでる部分は以下のように書いているのですが

 ```cs
 some code here!
 ```

これだとシンタックスハイライトが効かない(ビルド時に cs なんて知らんよって言われてる) ので csharp に変更しました。 これでシンタックスハイライトもばっちりです。

f:id:okazuki:20190122104735p:plain

プルリクをマージ

ということで作成してたプルリクエストをマージして完了。

公開

公式にちゃんと GitHub Pages への公開方法も書いてあるので助かりますね。

vuepress.vuejs.org

ReactiveProperty のドキュメントは https://runceel.github.io/ReactiveProperty/で公開してるのでベースを設定します。

module.exports = {
    base: '/ReactiveProperty/',
    title: 'ReactiveProperty documentation',

そして docs フォルダーにデプロイ用のスクリプトを書いて…

#!/usr/bin/env sh

set -e

rm -rf docs/.vuepress/dist
npm run docs:build

cd docs/.vuepress/dist

git init
git add --all
git commit -m 'deploy'
git push -f https://github.com/runceel/ReactiveProperty.git master:gh-pages

Git Bush で deploy.sh を叩くと無事デプロイされました。

Google Analytics 対応

config.js の title や description の並びに ga で ID を指定するだけ。お手軽。

vuepress.vuejs.org

まとめ

VuePress お手軽でいい感じでした。 テーマを自作したり見た目に凝ろうとすると Vue.js の知識がいりそうですが軽く使うだけならいい感じ。

Vue.js やろうかなぁ。

ソフトウェア開発系技術に興味はあるけど詳しくない人のための GitHub でサイトを公開する方法

$
0
0

要は GitHub pages に見た目は置いといてページをアップするための方法のメモです。OS は Windows 10 を想定してますが、まぁ OS による差異はパス区切り文字が \/や Ctrl か Cmd くらいなもんだと思います。

インストールするもの

Visual Studio Code に追加する拡張機能(必須じゃないけどあると便利なので入れる)

VSCode を起動したら拡張機能を選んで

f:id:okazuki:20190124141942p:plain

Paste Image で検索して Install を押します。記事を書くときに書くマークダウンファイルにクリップボードから画像を貼れるやつです。

f:id:okazuki:20190124142036p:plain

あと Markdown All in One も Install しておきます。

f:id:okazuki:20190124142301p:plain

インストールしたらリロードと書いてあるところを押すか VSCode を再起動します。

GitHub のアカウント登録

アカウントを作ってログインできるようにしておきます。

github.com

記事を書く

任意の場所に空のフォルダーを作ります。フォルダーを作ったら Visual Studio Code のファイルメニューからフォルダーを開くで、そのフォルダーを開きます。

新しいファイルをぽちっと作って index.md という名前にします。 マークダウンは HTML のタグみたいな煩わしい書き方ではなく、なるべくテキストファイルとして自然な感じで文書が書けるやつです。書き方はいろいろなサイトがありますが、Markdown で検索したら以下のサイトがひっかかりました。きれいにまとまってると思います。

www.asobou.co.jp

例えば index.md の中身を以下のようにします。

#最初のページ

ここに色々書きます。

-箇条書き1
-箇条書き2

##他にも書きます

色々

以上

ちゃんとハイライトもされたりして便利。

f:id:okazuki:20190124143403p:plain

Git で管理するようにする

Git で差分管理するようにします。因みに Git と GitHub は違うものです。Git はファイルの差分や変更履歴を管理するツール。GitHub は Git で管理してるファイルをインターネット上に置かせてくれるサービス(付帯サービスもいっぱいあって便利)

VSCode のソースコントロールを開いて Git で管理するための初期化ボタンを押します。

f:id:okazuki:20190124143632p:plain

フォルダーを選択するように言われるので、今開いてるフォルダーを選択します。

f:id:okazuki:20190124143708p:plain

こうすると履歴管理してくれるようになります。早速変更されたファイルをソースコントロールに出てくる(この場合は index.md)のでメッセージを入れて Ctrl + Enter かチェックマークを押して怒られます。

f:id:okazuki:20190124144707p:plain

履歴に残す自分の名前とメアドを入れないといけません。ターミナルを起動します。

f:id:okazuki:20190124144827p:plain

ターミナルが出てくるので以下のコマンドを打ちます。

git config --global user.name "your name"
git config --global user.email yourmailaddress@example.com

この設定は最初だけなので次からは怒られません。気を取り直してもう一度。

f:id:okazuki:20190124143823p:plain

なんか出てきますが変更を反映するかって感じなので Yes か Always しておきましょう。Git に興味が出てきてちゃんと勉強したいときはステージとかなんとか勉強するといいと思います。ここらへんとかで。

backlog.com

こんな感じでファイルを作ったり変更したりして、きりがいいタイミングで変更を反映していきます。このコメントをつけて変更の履歴をつけることをコミットって言います。

公開しよう

GitHub にいって新しいリポジトリーを作ります。リポジトリーはローカルのフォルダーをアップロードするような場所のイメージです。 GitHub にログインしたら New を押して

f:id:okazuki:20190124144320p:plain

適当に名前を入れて Create repository をします。

f:id:okazuki:20190124144426p:plain

そして URL が出てくるのでコピーしておきます。

f:id:okazuki:20190124144545p:plain

ここが最大の難関かもしれませんがアップロード先の登録をコマンドでやります。ターミナルを表示させて以下のコマンドを打ちます。コマンドの後ろ側の URL は GitHub でゲットした URL です。これもリポジトリー単位に 1 回やるだけで OK です。

git remote add origin https://github.com/youraccount/yourrepositoryname.git

ソースコントロールの ...から Pushを選びます。Pushするとアップロードされます。

f:id:okazuki:20190124145405p:plain

最初の Pushではアップロード先に、まだ自分の作業履歴と関連付けられた履歴がないけど作る?みたいな感じのこと言われるのでさくっと OK して作りましょう。

f:id:okazuki:20190124145506p:plain

ブラウザーでリロードするとなんかアップロードされたのがわかる感じになります。

f:id:okazuki:20190124145617p:plain

これだと、まだファイルがあるだけみたいな感じなので、最後に GitHub Pages で公開します。GitHub のリポジトリーで Settings の Options を開きます。

f:id:okazuki:20190124145744p:plain

下にスクロールすると GitHub Pages という項目があります。以下のように master というのをドロップダウンから選びましょう。master ってのが Git でデフォルトで作業してる一連の流れみたいなものです。

f:id:okazuki:20190124145843p:plain

最後に Choose a theme を押して気に入った見た目のものを選びましょう。

f:id:okazuki:20190124150020p:plain

閲覧

GitHub Pages の設定のところに URL が出てるはずなので、それを押すと閲覧できます。

f:id:okazuki:20190124150154p:plain

更新

ファイルを変更します。

f:id:okazuki:20190124150228p:plain

ソースコントロールで差分をコミットします。

f:id:okazuki:20190124150301p:plain

ソースコントロールで Push します。失敗することもあります(特にこの記事の流れでやってると失敗しますが心配いりません)

f:id:okazuki:20190124150330p:plain

これはアップロードした先でテーマの設定用のファイルとかが追加されてるので、このままアップロードすると履歴が変になるからです。ということでアップロードしたい自分の手元にリモートの変更を取り込みます。ソースコントロールで Pullします。

f:id:okazuki:20190124150625p:plain

_config.ymlというのが追加されます。これが先ほど GitHub の設定でテーマを選んだものとかが書かれてます。 手元のファイルがサーバーのものと合体したので Pushします。

f:id:okazuki:20190124150755p:plain

GitHub Pages をリロードすると変わります。

f:id:okazuki:20190124150849p:plain

更新時にはローカルで変更。Pull を押して念のため(自分ひとりで 1 台のマシンで使ってる限りはほぼないけど)リモートの変更を取り込みます。そして Push してアップロードします。

その他、よくやること

画像を入れたい

マークダウンの書き方見てねってなりますが、最初にいれた Paste Image 拡張機能のおかげでクリップボードに画像があれば画像を入れたい場所にフォーカスを持っていって Ctrl + Alt + V で画像を表示するためのマークダウンを入れてくれます。 Windows 10 は Win + Shift + D でデスクトップの矩形切り抜きができるので作業手順とかを作るときは便利です。

Ctrl + Alt + V をすると以下のように画像が自動的に追加されて表示するための記述が挿入されます。 f:id:okazuki:20190124151216p:plain

別ページのリンク作りたい

適当に hoge.mdとかでファイルを作って何か書きます。リンク元で以下のように書けばリンクになります。

[Hogeへのリンク](./hoge.md)

こんな感じになります。

f:id:okazuki:20190124151456p:plain

まとめ

とりあえず一人でコツコツ簡単なサイトを上げれるようになったので必要に応じて Git や GitHub の使い方やマークダウンを勉強する土台にするといいと思います。

GitHub を使うと他の人にしてもらった作業をレビュー記録つきで取り込んだり(プルリクエスト)、課題管理をしたり(Issues)、別のマシンで作業してそれをまた自分の別のマシンに取り込んだり(Git の分散バージョン管理あたり)いろいろ便利なことができるようになります。

マークダウンも、どんなことができるのかというのはざっと見ておくと後で捗ると思います。どう書くのかというのは使うやつから都度調べてたら覚える。

Microsoft Learn の学習用環境(サンドボックス)の有効化方法

$
0
0

やってみようと思います!というのも先日こんなコメントがついたので

f:id:okazuki:20190203011229p:plain

https://docs.microsoft.com/ja-jp/learn/paths/create-serverless-applications/

有効化までしてみたいと思います。 ということで、Azure Functionsのサンドボックスを有効化するボタンを押してみます。

ちなみにインプライベートで開いていいるブラウザで試してます。

ぽちっとな

f:id:okazuki:20190203011348p:plain

これがアフィリエイトのリンクを押したような画面なら仕方ないですがMSアカウントのサインイン画面です。 今回は私のプライベートなMSアカウントでサインインしてみました。

f:id:okazuki:20190203011453p:plain

サインインが完了するとサンドボックスの情報を読み込むというメッセージが出て…

f:id:okazuki:20190203011540p:plain

私の場合はこんなメッセージが出ました。プライベートのほうでもサブスクリプションがあったりするのが関係するのかな?

f:id:okazuki:20190203011623p:plain

承諾しましょう。

f:id:okazuki:20190203011655p:plain

そうすると晴れてサンドボックス環境が有効化されます!といってもそこそこ長いことサンドボックスの情報を読み取っていますが…ドキドキ。最終的には以下のように表示されると思います。

f:id:okazuki:20190203011843p:plain

教材によってはAzure CLIだけで完結する場合はターミナルが教材に表示されたり、あとは実際にAzureポータルにサインインするように案内されるものがあります。同じアカウントで複数のサブスクリプション(Azureでいう契約みたいなもんだと思ってもらえればいいです)持ってる場合はポータルの右上のアイコンからディレクトリの切り替えを押して Microsoft Learn Sandbox という名前のに切り替えれば OK です。

ということで、Microsoft Learn は実際にものを触りながら学習できるので個人的にはおすすめです。 Typoとかみつけたら reporting an issue を送ってあげると喜ばれるかもしれません。

docs.microsoft.com


Prism 7.x で DI コンテナ固有の機能を使いたい

$
0
0

Prism 7 系で色々仕様変更がありましたが、その中でも大きな変更のうちの 1 つが DI コンテナにクラスを登録する処理の変更です。今までは各 DI コンテナのクラスを直接触っていましたが IContainerRegistry インターフェースというインターフェースが Prism から提供されて、それに対してクラスを登録するという形になりました。

IContainerRegistry は、各々の DI コンテナごとに実装があって、その先で DI コンテナの個別の API を呼び出しています。利用者からしたら DI コンテナに何を使っていたとしても Prism の提供する箱庭の中にいる限りは同じコードでよくなるというようになっています。

IContaienrRegistry インターフェースには以下のようなメソッドが定義されています。

  • RegisterInstance
  • RegisterSingleton
  • Register
  • IsRegistered

登録系機能オンリーって感じですね。

そのほかに Prism.Wpf や Prism.Forms で画面遷移用にページを追加するための RegisterForNavigation などの拡張メソッドが定義されています。

DI コンテナ固有の API を呼びたい

ということで、Prism を基本的に使う上で必要な処理が、どの DI コンテナを使っていたとしても同じように書けるように改善されています。 ところで、DI コンテナ固有のパワフルな機能を呼びたいとしたらどうするの?という疑問が出てきますが、これも一応逃げ道が用意されています。

IContainerRegistry インターフェースには、各 DI コンテナ固有の実装が入っているパッケージ(Prism.Unity.Forms や Prism.DryIoc.Forms や Prism.Unity.Wpf など) に GetContainer という拡張メソッドが定義されています。

これを使うと各 DI コンテナの生のインスタンスが取れるので、これを使うことで DI コンテナ固有の機能 (AOP や Resolve 時の引数の解決に独自処理を入れたりなど) を使うことができるようになっています。

ということで、これを使うと App クラスの RegisterTypes あたりでこんな風に書けるようになります。

protectedoverridevoid RegisterTypes(IContainerRegistry containerRegistry)
{
    // コンテナのインスタンスを取得する
    IUnityContainer container = containerRegistry.GetContainer(); // using Prism.Unity; をすると使えるようになる// コンテナ固有の機能にアクセス可能(この例では Unity の機能でコンストラクタに固定値を渡すようにしてる
    container.RegisterType<Sample>(new InjectionConstructor(10));
}

まとめ

Prism の用意してくれた IContainerRegistry という箱庭でおさまる場合はいいですけど、そうじゃない場合は今回紹介した GetContainer というメソッドを使ってお使いの DI コンテナ固有の機能を使って、よりパワフルな機能を使って便利に開発するのもいいかなって思います。

それでは、良い Prism Life を。

Surface Go を買ってタブレットモードで便利に使うときのメモ

$
0
0

こんなつぶやきをしました。

基本的には失敗と認知されてる Windows 8 系の UI の変化好きだったのですが、これと同じような動作をするモードは Windows 10 にも搭載されていてタブレットモードといいます。

下の図にあるように右下の①の部分を押すと出てくるWindows 10 のアクションセンターにある②の部分を押すとタブレットモードと普通のモードを切り替えることができます。

f:id:okazuki:20190206184601p:plain
アクションセンター

タブレットモードにすると基本的にアプリケーションが自動的に全画面で表示されるようになります。下の図が今まさにブログ記事を書いてるところのスクリーンショットなのですが、何もしなくてもこんな感じに全画面表示になります。集中できていい。

f:id:okazuki:20190206184748p:plain
今記事を書いてる状態

タブレットモードにしなくても有効なジェスチャーはあるのですが、タブレットモードで使うときに知ってると便利だと思う操作をいくつか紹介したいと思います。

画面左端からのスワイプ

画面の左端から右に向かってスワイプをするとタスクバーのコルタナさんのアイコンの右にあるアイコンを押したときと同じようにタイムラインを表示する画面になります。

ここで今使ってるアプリを切り替えることができたりします。

f:id:okazuki:20190206185206p:plain

画面の上からのスワイプ

画面の上の端から下に向かってスワイプするとアプリを閉じることができます。閉じるのは正直右上のバッテンを押すことが多いのですが、上から下にスワイプしてアプリを終了するまでおろさずに、途中で止めるとウィンドウが小さくなって移動できる感じになります。

この状態で左や右端にもっていくと画面の左側と右側で別々のアプリを表示することができるようになります。下の図のような感じです。

f:id:okazuki:20190206185612p:plain

これはアプリの境界線をタッチで移動させることで比率を変更できます。

f:id:okazuki:20190206185651p:plain

今左にある Twitter アプリの上側の中央から下に向かってスワイプすると、Twitterアプリを移動できるようになります。その状態で右側にもっていくと左右が入れ替わります。

f:id:okazuki:20190206185756p:plain

ちなみにこれは、タブレットモードじゃなくても疑似的にできて Windows キー + 右 や Windows キー + 左でウィンドウが左半分や右半分に張り付きます。 そうすると残りの部分に表示するアプリの選択をするような UI が表示されるので、残り半分に表示したいアプリを選択すると左右均等に表示されます。

この状態で 2 つのアプリの境界にマウスカーソルを持っていくと、縦に黒いバーがでて、それをつまんで動かすと 2 ウィンドウを同時にリサイズしてくれます。 そこそこ使ってます。

右端からのスワイプ

アクションセンターがでます。以上。タブレットモードじゃなくてもできます。

2 本指タップ

これもタブレットモードじゃなくてもいいのですが画面を 2 本指でタップするとタッチ用の右クリックメニューが出ます。タッチしやすい(1 本の指で長押しでもいいけど 2 本指タップのほうが出るのが早い)

f:id:okazuki:20190206190010p:plain
2 本指タップでメニューが出たところ

まとめ

LTE 版待っててよかった。 がっつり開発する用途でなければこのスペックでも割と動く。

その際に画面が物理的に小さいなら小さいなりに使う 1 つの方法としてタブレットモードはありだと思います。

Bot Framework SDK v4 を使うときの 2019/02/12 時点で最適だと思う ConfigureServices メソッドの書き方

$
0
0

なんというか Bot Framework SDK v4 は v3 と違って細かくカスタマイズ可能な部品が提供されてて、それを組み合わせて使うという感じになってるので、より挙動のカスタマイズがしやすくなっていたりします。

例えば LUIS や QnA Maker との連携機能の提供のされかたも v3 では LUIS や QnA Maker と連携するためのダイアログの基本クラスが提供されていて、利用者は基本的には Attribute を付けたメソッドを適切に定義していれば、よしなにライブラリ側が呼び出してくれるといった形になってます。

v4 は API 叩くためのクラス用意してやるから、それ使って結果の処理はお好きにどうぞといった感じです。個人的には好きなアプローチ。 例えば v3 の頃は細かい挙動を変えたいときに基本クラスのソースコードを見て、該当の処理をしているメソッドをオーバーライドして親クラスの処理を上書きしたり、場合によっては基本クラスでは意図されていなかったハードコードされてる挙動を変えたい場合は泣く泣くソースコードを丸コピして該当部分を書き換えたものを自分のプロジェクトに含めたりといった対応が必要でした。(レアケースだとは思いたいですが)

v4 は API 呼ぶだけの機能がある感じなので、あとは好きにしろ。お前の自由だって感じなのでそういう心配がいりません。

ということで、それらの部品群のおぜん立てをするのが ASP.NET Core を使う場合には Startup.cs クラスの ConfigureServices メソッドになるのですが、今日時点の Bot Framework の Visual Studio 向けのプロジェクトテンプレートで新規作成すると非推奨なプロパティを使ってたりしてて個人的に気持ち悪かったので、今のところこう書くのがいいんじゃない?っていうのを書いておこうと思います。

というわけでコード

前置きが長くなったのでコードをさくっと。因みに各 Bot Framework SDK 関連の NuGet は v4.2.2 使ってます。

// Copyright (c) Microsoft Corporation. All rights reserved.// Licensed under the MIT License.using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Configuration;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Linq;

namespace HelloWorldBot
{
    publicclass Startup
    {
        privatereadonlybool _isProduction;
        private ILoggerFactory _loggerFactory;

        public Startup(IHostingEnvironment env)
        {
            _isProduction = env.IsProduction();

            Configuration = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables()
                .Build();
        }

        public IConfiguration Configuration { get; }

        publicvoid ConfigureServices(IServiceCollection services)
        {
            services.AddBot<HelloWorldBot>(options =>
            {
                var secretKey = Configuration.GetSection("botFileSecret")?.Value;
                var botFilePath = Configuration.GetSection("botFilePath")?.Value ?? @".\xxx.bot"; // 定義がない場合はデフォルトのボットファイルで// 定義ファイルを読み込んで IServiceCollection に追加しとく
                var botConfig = BotConfiguration.Load(botFilePath, secretKey);
                services.AddSingleton(botConfig);

                // 環境に応じたエンドポイントの定義を探す
                var service = botConfig.Services.Where(s => s.Type == "endpoint"&& s.Name == (_isProduction ? "production" : "development")).FirstOrDefault();
                if (!(service is EndpointService endpointService))
                {
                    thrownew InvalidOperationException($"The .bot file does not contain a development endpoint.");
                }

                // 認証機能とエラー時の処理を追加
                options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
                var logger = _loggerFactory.CreateLogger<HelloWorldBot>();
                options.OnTurnError = async (context, exception) =>
                {
                    logger.LogError($"Exception caught : {exception}");
                    await context.SendActivityAsync("Sorry, it looks like something went wrong.");
                };
            });

            // アクセサーの登録
            services.AddSingleton(sp =>
            {
                // ここら辺で State 関連は定義しておく。(使う前で定義されてればいい)// テンプレートだと非推奨な BotFrameworkOptions クラスの State プロパティで管理してる
                var dataStore = new MemoryStorage(); // 本番は Blog か CosmosDB で
                var userState = new UserState(dataStore);
                var conversationState = new ConversationState(dataStore);

                returnnew HelloWorldBotAccessors(conversationState, userState)
                {
                    MyUserState = userState.CreateProperty<MyUserState>($"{nameof(HelloWorldBotAccessors)}.{nameof(HelloWorldBotAccessors.UserState)}"),
                    MyConversationState = conversationState.CreateProperty<MyConversationState>($"{nameof(HelloWorldBotAccessors)}.{nameof(HelloWorldBotAccessors.ConversationState)}"),
                };
            });

            // その他サービスもこの下らへんで登録しておく
            services.AddSingleton<BotServices>();
        }

        publicvoid Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {
            _loggerFactory = loggerFactory;
            app.UseDefaultFiles()
                .UseStaticFiles()
                .UseBotFramework();
        }
    }
}

因みに Bot Framework SDK のサンプルを見ると、よく BotServices っていうクラス名見るけど、これは単なる自作クラスで以下のような感じで定義されてます。

using Microsoft.Bot.Builder.AI.Luis;
using Microsoft.Bot.Builder.AI.QnA;
using Microsoft.Bot.Configuration;
using System.Collections.Generic;

namespace HelloWorldBot
{
    publicclass BotServices
    {
        public Dictionary<string, QnAMaker> QnAServices { get; } = new Dictionary<string, QnAMaker>();
        public Dictionary<string, LuisRecognizer> LuisServices { get; } = new Dictionary<string, LuisRecognizer>();
        public BotServices(BotConfiguration botConfiguration)
        {
            foreach (var service in botConfiguration.Services)
            {
                switch (service.Type)
                {
                    case ServiceTypes.QnA:
                        {
                            var qna = (QnAMakerService)service;
                            // ここらへんで QnAMaker クラスの初期化と登録break;
                        }
                    case ServiceTypes.Luis:
                        {
                            var luis = (LuisService)service;
                            // ここらへんで LuisRecognizer の初期化と登録break;
                        }
                }
            }
        }
    }
}

LUIS や QnA Maker と連携したかったら、この BotServices クラスを DI して使ってねって感じです。サンプルでは Dictionary 型で定義されてることが多いけど、かちっとやるなら個別プロパティかなぁ?どっちがいいんだろう。個人的な感覚では Dictionary よりもきちんと個々のプロパティ定義する方が好みだけど。

まとめ

そろそろテンプレート安定してほしい。

C# の複数ファイルの一括フォーマットツール

$
0
0

今日、Visual Studio 2019 Preview 3 のリリースノートを読んでたら dotnet-format ツールが紛れてました。

github.com

一括で .editorconfigの適用してくれるとか。ありがたい。

試してみよう

ということでインストールしてみます。.NET Core の最新版が入ってれば以下のコマンドで OK

dotnet tool install -g dotnet-format --version 3.0.0-beta4-19105-10

2019/02/15 現在のプレビュー版のバージョンなので、実際に試すときは GitHub のページ見てバージョン等確認してください。

こんなメッセージが出れば成功です

You can invoke the tool using the following command: dotnet-format
Tool 'dotnet-format' (version '3.0.0-beta4-19105-10') was successfully installed.

ということで適当に dotnet new consoleとかしてぐちゃぐちゃなフォーマットのファイルを用意します。

f:id:okazuki:20190215092450p:plain

f:id:okazuki:20190215092530p:plain

.editorconfigは roslyn のリポジトリーからとってきました。

roslyn/.editorconfig at master · dotnet/roslyn · GitHub

準備ができたのでコマンドをたたきます。

dotnet-format -v diag

オプションは無くていいのですが詳細メッセージ出してもらうためにつけてます。

$ dotnet-format -v diag
  ワークスペース 'D:\Repos\DotnetFormatTest\DotnetFormatTest.csproj' でコード ファイルを書式設定します。
  ワークスペースを読み込んでいます。
  ワークスペース 2343 ミリ秒で読み込まれます。
  プロジェクト 'DotnetFormatTest' でコード ファイルを書式設
定しています。
  コード ファイル 'Person.cs' をフォーマットしています。
  コード ファイル 'Program.cs' をフォーマットしています。
  Formatted 0 of 2 files in 1752ms.
  フォーマットが完了しました。

てっきり英語のメッセージが出るかと思ってたら日本語が出てびっくり。

ファイルもこんな感じになってました。

f:id:okazuki:20190215093144p:plain

f:id:okazuki:20190215093222p:plain

Person クラスのほうが .editorconfigとかで指定できてないようなところどうなるかな?って思ったのですが、そこについては普通にノータッチっぽいですね。いいと思う。

余談ですが .editorconfigにエンコーディング指定もあったので EUC-JP で保存したのちに dotnet-format してみましたが、そこはまだっぽいですね。EUC-JP のままでした。(予定があるのかとかはチェックしてない)

まとめ

プロジェクト単位やソリューション単位で .editorconfigでフォーマットしてくれるの欲しかったのでちょうどよかったです。 先日、そこまで膨大な数じゃないけど 1 ソリューションのコードフォーマットする機会があったのですが調べる時間もめんどうで手動で 1 ファイルずつ心を込めてやったのですが、その時に欲しかった。

Visual Studio で名前変更のリファクタリングの一番簡単な方法

$
0
0

個人的な主観が入った小ネタです。

Visual Studio には様々なリファクタリングのための機能がついています。 個人的に、その中で一番お世話になってるのが名前変更です。例えば…

  • あっ、クラス名イケてない…
  • このプロパティ名ちょっと失敗したな
  • この変数名ちょっと変えたい
  • etc...

まぁいっぱいあります。

そんなときは名前を変えたいものの上で右クリックして名前の変更を選ぶ or Ctrl + R, Ctrl + Rを押すとかあると思います。

f:id:okazuki:20190215114739j:plain

自分のやってる方法

普通に名前を変えたいものの名前を変更します。そして Ctrl + .を押します。そうすると以下のようなものが表示されます。

f:id:okazuki:20190215114945j:plain

そのまま Enter を押します。

完了。

Vue.js の勉強メモ(TypeScriptで!)

$
0
0

Vue.js ずっと気になってたんですよね。

jp.vuejs.org

かなり前に React やったんですが、今度は Vue.js に手を出してみたいと思います。

やるならやっぱり…

TypeScriptですよね!以下のコマンドで最新入れておきます。

npm i -g typescript

エディターの設定

Visual Studio Code の Vetur 拡張というのをお勧めされてるので入れておきました。

marketplace.visualstudio.com

ブラウザーの拡張機能

デバッグが便利らしいので、ここら辺も入れておきます。(Chromeに)

chrome.google.com

もしくは、以下のコマンドで専用のツールも入れることが出来るみたい。

npm install -g @vue/devtools

ブラウザー拡張使うのが楽そう。

ガイドをやってみよう

続けて CLI ツールも入れて…と思ったのですが、最初はガイド見てみるといいよ!って書いてあるので見てます。

index.htmlというファイルを作って以下のようにコードを書きます。

<htmllang="ja"><head><scriptsrc="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script></head><body><divid="app">
            {{ message }}
        </div><scripttype="text/javascript">var app = new Vue({                el: '#app',                data: {                    message: 'Hello Vue!'}});</script></body></html>
````

ブラウザーで表示させると…

[f:id:okazuki:20190218153735p:plain]

いいね!因みに VSCode に Live Server 拡張機能を入れてるので Go Live をぽちっとするだけで html の中身見れて最高。


[https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer:embed:cite]

この状態で `app.message` を書き換えるといい感じに更新される。

[f:id:okazuki:20190218154459p:plain]

そして、属性のバインドも試してみる。これもガイドにある通りに。

{{ message }}
<div id="app-2">
        <span v-bind:title="message">
            ホバーしてね
        </span>
    </div>

    <script type="text/javascript">
        var app = new Vue({
            el: '#app',
            data: {
                message: 'Hello Vue!'
            }
        });

        var app2 = new Vue({
            el: '#app-2',
            data: {
                message: 'Hello Vue!! ' + new Date().toLocaleString(),
            }
        });
    </script>
</body>

その他にも `v-if` や `v-for` で分岐やループが出来る。

こんな感じので

  1. {{ message }}
こうなる

[f:id:okazuki:20190218155222p:plain]

`app.seen = false` にするとリストも消える。

[f:id:okazuki:20190218155308p:plain]

`app.seen = true` にして `app.messages.push('Added item')` すると再度表示されて要素も追加された。手軽でいいね。

[f:id:okazuki:20190218155409p:plain]

`v-on` でイベントハンドラーの追加と `v-model` で双方向バインディング。覚えたし。ということでこういうのを書くと…


  1. {{ x.timestamp }}: {{ x.text }}
ボタンを押すとリストに追加される

[f:id:okazuki:20190218160355p:plain]

## コンポーネント

これまではアプリ 1 つで 1 つの Vue オブジェクトだったけどコンポーネントというのを作ることで小さな部品をくみたててアプリに仕立てるということが出来るみたい。

Angular でも React でも同じように小さな部品作ってくみ上げるアプローチだったのできっと Vue.js にもあるだろうと思ってたものがあった。

作り方は 

Vue.component('名前', { props: [ 'プロパティ名' ], data: 初期データを返す関数, methods: { メソッド名: function() {}, } template: 'レンダリングに使用するタグ', });

という感じです。

先ほどの入力フォームと結果のリストを、それぞれコンポーネント化するとこんな感じ。

<input-form v-on:add-click='onAddClick'></input-form>
<output-list v-bind:messages="items"></output-form>

````

f:id:okazuki:20190218170749p:plain

ガイドの中身 + αの内容はこんな感じ。じゃぁ TypeScript してみよう。

TypeScript で作ってみる

ここら辺を参考に。

jp.vuejs.org

上記ドキュメントにも書いてありますが公式の型定義があるのがありがたいですね。

まずは CLI ツールを入れます。

npm i -g @vue/cli

そして、以下のコマンドを打ちます。

vue create hello-world-ts

そうするとウィザードみたいなのが走るので manual を選択して babel じゃなくて TypeScript を選んで適当に進めます。とりあえず、こんな感じで。

$ vue create hello-world-ts


Vue CLI v3.4.0
? Please pick a preset: Manually select features
? Check the features needed for your project: TS, Linter
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript for auto-detected polyfills? No
? Pick a linter / formatter config: TSLint
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

実行が終わると、こんなファイルが生成されてる。

f:id:okazuki:20190218172603p:plain

npm run serveで開発用サーバーを立てたり、npm run buildで本番向けビルドもできる。至れり尽くせり。

.vue ファイル

ガイドからいきなりすっ飛んだ気がするけど、Vue.js でのコンポーネントの定義は最終的に .vueというのでやることになるみたい?

まぁそこらへんは今後ドキュメントを読むとして、今のところ .vueファイルには templateタグと scriptタグと styleタグがある。ここにそれぞれテンプレートの HTML とコンポーネントのクラスの定義と、CSS が定義されているように見える。

ということで、新たに components フォルダーに InputForm.vueOutputList.vueを作って内容を以下のようにしてみた。

まずは InputForm.vue

<template><div><inputtype="text" v-model="input" /><button v-on:click="addItem">Add</button></div></template><scriptlang='ts'>import{ Component, Prop, Vue, Emit } from 'vue-property-decorator';@Componentexportdefaultclass InputForm extends Vue {public input: string = "";public addItem(){this.addClick(this.input);this.input = "";}    @Emit('add-click')
private addClick(value: string){}}</script>

次に OutputList.vue

<template><ol><li v-for="x in messages">
            {{ x.timestamp }}: {{ x.text }}
        </li></ol></template><scriptlang="ts">import{ Component, Prop, Vue } from 'vue-property-decorator';import{ Message } from '../Message';@Componentexportdefaultclass OutputList extends Vue {    @Prop()
public messages!: Message[];}</script>

そして App.vue

<template><divid="app"><InputForm v-on:add-click='onAddClick'></InputForm><hr /><OutputList v-bind:messages="items"></OutputList></div></template><scriptlang="ts">import{ Component, Vue } from 'vue-property-decorator';import InputForm from './components/InputForm.vue';import OutputList from './components/OutputList.vue';import{ Message } from './Message';@Component({  components: {    InputForm, OutputList},})
exportdefaultclass App extends Vue {  items: Message[] = [];  onAddClick(x: string){this.items.push(new Message(x));}}</script>

これで実行すると動いた!

f:id:okazuki:20190218183551p:plain

とりあえず、今日はこんなところで。

Microsoft Teams にボットを追加したい

$
0
0

オレオレボット足したいじゃないですか。 Bot Framework を使って作るのが正攻法ですが、一応 Outgoing Webhook を使ってもボット作れます。

docs.microsoft.com

作ってみてる人もいます。お手軽でいいですね。

hkou.hatenablog.com

Bot Framework を使ったオレオレボットをデプロイするときは、manifest.json と指定のサイズのアイコン用画像を 1 つの zip にまとめたファイルを Teams にアップロードします。あ、もちろん Azure のボットチャンネル登録で Teams 連携は有効化しておきます。

マニフェストのスキーマは以下にあります。

docs.microsoft.com

実際のボットのみに絞った場合のマニフェストの例です。

{"$schema": " https://developer.microsoft.com/en-us/json-schemas/teams/v1.3/MicrosoftTeams.schema.json",
    "manifestVersion": "1.3",
    "version": "1.0.0",
    "id": "ボットチャンネル登録にあるボットのAppIdでいいのかな?",
    "packageName": "com.example.mysamplebot",
    "developer": {"name": "開発者名",
        "websiteUrl": "https://example.com/",
        "privacyUrl": "https://example.com/privacy",
        "termsOfUseUrl": "https://example.com/app-tos"
    },
    "name": {"short": "Sample",
        "full": "Sample"
    },
    "description": {"short": "サンプルのボット",
        "full": "サンプルのボットです。"
    },
    "icons": {"outline": "icon32x32.png",
        "color": "icon192x192.png"
    },
    "accentColor": "#ff0000",
    "bots": [{"botId": "ボットチャンネル登録にあるボットのAppId",
            "needsChannelSelector": false,
            "isNotificationOnly": false,
            "scopes": ["personal", "team", "groupchat"
            ],
            "supportsFiles": false,
            "commandLists": []}]}

bots の下の scopes は個人用なのかチーム用なのかグループチャット用なのかで変える感じですかね。 上記の manifest.json の場合は icon32x32.png という名前で 32x32 のサイズの PNG ファイルと icon192x192.png という名前で 192x192 のサイズの PNG ファイルを一緒に zip に固めます。

アップロードは Teams クライアントのチーム名の横の ...からチームの管理画面の Apps タブで Upload a custom appという項目(英語表示時の表記)があるので、そこから zip をアップロードすれば OK です。


Vue.js の勉強メモ 2 (TypeScript で!)

$
0
0

今回は TypeScript あんまり関係ないんですが、Vue.js は既存サイトに少しずつ適用可能と書いてあるじゃないですか。 これがどういうことなのかというのを試してみました。

本当に書きながらやってるのでまとまりありませんが、学習ログってことで。

プロジェクト作成

例えば、超シンプルですが以下のように index.html, css/site.css, scripts/index.js だけで構成される既存サイトがあるとします。(今時ねーよってのは置いといて)

<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title>Sample Web site</title><linkrel="stylesheet"type="text/css"href="css/site.css"><scripttype="text/javascript"src="scripts/index.js"></script></head><body><h1>ようこそ!!私のホームページへ!</h1><spanclass="welcome-message">Welcome!!</span><p>あなたは 201923 人目の訪問者です。</p><p>現在は <spanid="clock"></span>です。</p></body></html>
document.addEventListener('DOMContentLoaded', function() {
    setInterval(function() {var now = newDate();
        var message = `${now.getFullYear()}年${now.getMonth() + 1}月${now.getDay()}日${now.getHours()}時${now.getMinutes()}分${now.getSeconds()}秒`;
        document.getElementById('clock').innerText = message;
    }, 1000);
});
html{width: 100%;
}body{width: 100%;
    text-align: center;
}.welcome-message{font-family: monospace; font-size: 24pt; background-color: yellow; color: red; 
}

よくあるこんな感じですね。

f:id:okazuki:20190219143542p:plain

これに Vue.js を一部当て込んでみましょう。

Vue.js の CLI でこんな感じで TypeScript のプロジェクトを作ります。

$ vue create vue-item


Vue CLI v3.4.0
? Please pick a preset: Manually select features
? Check the features needed for your project: TS
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript for auto-detected polyfills? No
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

出来上がったら public フォルダーにサイトの中身をごそっと入れます。

そして id が app の div タグを追加。

<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title>Sample Web site</title><linkrel="stylesheet"type="text/css"href="css/site.css"><scripttype="text/javascript"src="scripts/index.js"></script></head><body><h1>ようこそ!!私のホームページへ!</h1><spanclass="welcome-message">Welcome!!</span><p>あなたは 201923 人目の訪問者です。</p><p>現在は <spanid="clock"></span>です。</p><divid="app"></div></body></html>

npm run serveしてみて結果を確認。なんか違和感を感じる見た目だけど動いた。

f:id:okazuki:20190219144652p:plain

ん?でも

でも create-react-app で作ったやつも同じ手順でこんな見た目のが出来ますね。

f:id:okazuki:20190219145514p:plain

ということは?

特別な JavaScript をビルドするような構成無しで以下のように追加できるのが、既存サイトに少しずつ適用可能ってことなのかな。

<!DOCTYPE html><htmllang="ja"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title>Sample Web site</title><linkrel="stylesheet"type="text/css"href="css/site.css"><!-- <script type="text/javascript" src="scripts/index.js"></script> --></head><body><h1>ようこそ!!私のホームページへ!</h1><spanclass="welcome-message">Welcome!!</span><p>あなたは 201923 人目の訪問者です。</p><p>現在は <spanid="clock">{{ message }}</span>です。</p><scriptsrc="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><scripttype="text/javascript"src="scripts/vueclock.js"></script></body></html>

んで vueclock.js はこんな感じ。

function generateMessage() {var now = newDate();
    return `${now.getFullYear()}年${now.getMonth() + 1}月${now.getDay()}日${now.getHours()}時${now.getMinutes()}分${now.getSeconds()}秒`;
}new Vue({
    el: '#clock',
    data: function() {return{
            message: generateMessage(),
        };
    },
    created: function() {varself = this;
        this.handle = setInterval(function() {self.message = generateMessage();
        }, 1000);
    },
    destroyed: function() {
        clearInterval(this.handle);
    },
});

実行結果は最初と同じでこんな感じです。

f:id:okazuki:20190219151320p:plain

Vue.js 勉強メモ 3 (TypeScript で!!)

$
0
0

色々勉強する前に TypeScript で確認出来るようにしておきたい

とりあえず簡単な方法は Vue.js の CLI を使って TypeScript を有効にしたプロジェクトをスタート地点とするみたいなので、それでやっていこうと思う。

プロジェクト作っていらないものを削除

vue create ts-lab

とか打ち込んで TypeScript だけを有効化したプロジェクトを作る。

src フォルダーの下の .vue ファイルを丸っと削除。

ハローワールドを TypeScript で

public/index.htmlを以下のように書き換え。

<!DOCTYPE html><htmllang="en"><head><metacharset="utf-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width,initial-scale=1.0"><linkrel="icon"href="<%= BASE_URL %>favicon.ico"><title>ts-lab</title></head><body><noscript><strong>We're sorry but ts-lab doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><divid="app">{{ message }}</div><!-- built files will be auto injected --></body></html>

div の中に {{ message }}を足してます。そして src/main.tsを以下のように書き換え。

import Vue from'vue'const app =new Vue({
  el: '#app',
  data: {
    message: 'Hello TypeScript',},});

最後に runtimeCompiler のオプションを true にしないといけない。これは今回のように実行時にコンパイルされるテンプレートがある場合には必須みたいですね。

cli.vuejs.org

これを設定しないと以下のようなメッセージがブラウザーのコンソールに出る。

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

(found in <Root>)

ということで vue.config.jsをプロジェクトルートに作って中身を以下のようにします。

module.exports = {runtimeCompiler: true,}

そして、npm run serveとすれば開発サーバーが立ち上がってブラウザーで URL を開くと以下のように結果が確認できます。

f:id:okazuki:20190219170604p:plain

まとめ

ということで TypeScript に読み替えながら Vue.js のドキュメントを読みながら試すための環境が整いました。 おおまかな手順としては…

  • vue-cli で TypeScript のプロジェクトを作る
  • .vueを全部削除
  • vue.config.jsで runtimeCompiler を true にする
  • main.ts を編集
  • npm run serveして Let's go.

ということで次からはドキュメントを読みながら遊んでいこうと思います。

Vue.js 勉強メモ 4 (TypeScript で!!)

$
0
0

ということで以下のドキュメントを読んでいきます。

jp.vuejs.org

英語のドキュメントも読むのはいいけど、最初の勉強では英語の読解とターゲット技術の読解の二重苦になるので、母国語ドキュメントがあるのは凄く理解のスピードが速いのでありがたい。

Vue.js のアプリは Vue クラスのインスタンスがいる。こんな感じで new して使う。

import Vue from'vue'const app =new Vue({});

んで、特に重要なのが data プロパティ。ここに渡したオブジェクトのプロパティが監視されて値を書き換えるとテンプレートに従って見た目も更新されるみたいですね。

import Vue from'vue'const app =new Vue({
  data: {// ここのプロパティが変わると紐づく見た目も変わる
    prop1: 'hoge',
    prop2: 'boo!!!',}});

なので、見た目に反映させたいデータ突っ込むプロパティは初期値が特になくても定義が必要ということです。freeze に関する説明もドキュメントにはあるけど、これは頭の片隅に入れておけばいいと思った。

Vue クラスには $ で始まる特別なプロパティがある。$el, $data, $watch あたりが紹介されてる。 API リファレンスを見ると、それ以外にも親を取るための $parent やルートをとるための $root とかもあります。

へ~って思ったのが $watch を使って以下のようにプロパティの監視をしている場合に…

var unwatch = app.$watch('prop1',function(newValue, oldValue){});
unwatch();

こんな風に $watch 関数の戻り値の関数を呼ぶと監視が解除されるっていうところです。C# でいう IDisposable とかみたいな型があるわけじゃなくて関数が返ってくるのね。

そのほかにも $on でイベントハンドラーを登録して $emit でイベント発火っていうのは知ってたけど $once で一度だけ実行されるイベントハンドラーとか登録できるものがあった。便利そう。

ライフサイクル

Vue クラスのライフサイクルをフックするためのコールバックも指定できる。

import Vue from'vue'const app =new Vue({
  data: {// ここのプロパティが変わると紐づく見た目も変わる
    prop1: 'hoge',
    prop2: 'boo!!!',},
  created: function(){// 作成時},
  destroyed: function(){// 破棄時}});

ドキュメントに記載にある図が凄くわかりやすい。

Vue インスタンス — Vue.js

順番としては以下の順番で呼び出されるみたい。

  • beforeCreate
  • created
  • beforeMount
  • mouted
  • beforeUpdate : マウント後に値が更新されるタイミングの前に呼ばれる
  • updated ; マウント後に見た目が更新された後に呼ばれる
  • beforeDestroy
  • destroyed

これだけあればいい感じに色々できそう。

JavaScript のややこしいところ

ここで JavaScript の初見殺しだと思ってる this が状況によって変わる問題についての言及があった。 JavaScript は呼び出し側が this を指定することが出来るんだけど、どうも Vue.js は、これを使ってライフサイクルや $watch のコールバックの this を Vue のインスタンスにしてるみたいだけど、他の言語と同じように this が勝手に変わらないアロー関数(() => { ... })を使うと this が思ったのと違うのになってつらいということみたい。

個人的にはアロー関数のほうが好きだけど、ここでは使えない (this 使わないんならいらないけど、大体 Vue クラスのインスタンスのプロパティを書き換えたいよね) ってことで覚えておこう。

まとめ

ライフサイクルは暗記しよう。

Vue.js 勉強メモ 5 (TypeScript で!!)

$
0
0

次はテンプレート構文だ。

jp.vuejs.org

テンプレートってあれね。各コンポーネントの template プロパティや、.vueの template タグで書いてた下のようなあれ。

<div>
  {{ message }}
  <ol><li v-for:"x in hogeeee">{{ x.text }}</li></ol></div>

んで、これは別にテンプレートじゃなくて自分で JavaScript でこねくり回すこともできるみたい。 こういうのをコードでこねくり回すのはつらいのは世の常なので、ドキュメントのインデックスの下の方に JSX についての項目があるのは、そういうことなんだろうなぁと思った。

描画関数とJSX — Vue.js

テンプレートのポイント

テキストを出力するための {{ xxx }}だけど自動で変更を検知して見た目を更新してくれる。

v-onceディレクティブを指定すると、変更を監視しなくなるらしい。たくさんの変更されないデータがあるときは、これを指定しておくと捗りそう。SIer では必須っぽいなってなんとなく思った。 (偏見だけど、すごいたくさんの項目を出しまくる画面とかがありそうって思ってる)

v-htmlディレクティブはエスケープされない状態で出力できるのでマークダウンエディターのプレビュー画面みたいなのを作るときに重宝する。デフォルトでエスケープしてくれるのはいいね。

タグの属性に Vue のプロパティの値を流し込みたいときは v-bindディレクティブを使う。

例えばこんな Vue クラスのインスタンスで

import Vue from'vue'const app =new Vue({
  el: '#app',
  data: {
    tooltip: 'ぽっぷあっぷ!',
    text: 'ぽっぷあっぷあるよ!',},});

こんなテンプレートだと

<!DOCTYPE html><htmllang="en"><head><metacharset="utf-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width,initial-scale=1.0"><linkrel="icon"href="<%= BASE_URL %>favicon.ico"><title>ts-lab</title></head><body><noscript><strong>We're sorry but ts-lab doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><divid="app" v-bind:title="tooltip">{{ text }}</div><!-- built files will be auto injected --></body></html>

こうなる

f:id:okazuki:20190219174446p:plain

そして、v-bind{{ ... }}の中には単一の式も書ける。便利。

v-if, v-for, v-bind, v-onあたりが登場してた。 特に気になったのが v-on:submit.preventみたいに修飾子という機能を使うことで、event.preventDefault()をしてくれたりとかがあること。これは知らないと謎だったわ。

そのほかに v-bind;で省略できたり v-on@になったりとか省略形がある。確かにめんどいよね v-xxxってうつの。

:title="xxx" -> v-bind:title="xxx"
@click="onHoge" -> v-on:click="onHoge"

まとめ

本文と関係ないけど、ここらへんの変更があるということは目を通しておきながら 2.x 勉強したほうがいいかも。

qiita.com

Xamarin.Forms の最近の機能で気になってるやつ

$
0
0

Xamarin.Forms 3 系あたりからさっぱり追いかけてなかったので復習も兼ねて。

といいつつ 2.x の機能を見てると、ここらへんもきちんとやったことないので、2.x 系から怪しいかもしれない?

docs.microsoft.com

因みに Xamarin Android や iOS のほうは OS の進化についていってるし、プラグイン使ってやってたことも Xamarin Essentials っていうので結構カバーされてるなぁってのとか、Android のデザイナーとかも改善されてたり、VS 2019 ではインストールサイズが激減してたりとか凄い。

Xamarin.Forms 3.0

FlexLayout

FlexLayout が追加されてる。HTML/CSS の文脈でよく聞くあれかな? ドキュメントはこちら。

docs.microsoft.com

ドキュメント内の聖杯レイアウトって単語初見です。よろしくお願いします。古いたとえですが Java の Swing にある BorderLayout で作れたようなレイアウトを再現するものらしい。WPF では DockPanel 使うような案件ですね。

自分の WPF の記事からですが、こんなの。

f:id:okazuki:20190220112958p:plain

CSS

スタイルシート使えるようになってる。StyleClass っていうプロパティで CSS のクラス名が指定可能で、css ファイルの読み込みは <StyleSheet Source="xxx.css" />を ResourceDictionary に追加して使うみたい。

Visual State Manager

WPF や UWP でもあった状態に応じて見た目を変えるのに使うやつですね。

docs.microsoft.com

VisualElement から派生したすべてのクラスで CommonStatus という Visual State Group で Normal, Disabled, Focused という Visual State があるので、これを設定することでいい感じに一般的な状態に対する見た目を個々に設定できる。今まではTriggerとか使ってやる感じだったけどわかりやすくなりましたね。

Xamarin.Forms 3.1

Android での下側にタブを表示する機能がサポートされてる~~~!!

docs.microsoft.com

上のリリースノートに書いてあるけど、こんなんで出来るってお手軽かよ。

<?xml version="1.0" encoding="utf-8"?><TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"android:TabbedPage.ToolbarPlacement="Bottom"

Xamarin.Forms 3.2

個人的にはピンとくるものはなかった。

docs.microsoft.com

Xamarin.Forms 3.3

ここもピンとくるものはなかった。

docs.microsoft.com

Xamarin.Forms 3.4

ここも特に

docs.microsoft.com

Xamarin.Forms 3.5

docs.microsoft.com

Features in Preview のところに注目が。

Shell、CollectionView や Visual とかが試験的に追加されはじめてる。マテリアルデザインのレンダラーのところで見つけたものとしては、こんなパッケージあるの知らなかった。

www.nuget.org

Visual の GitHub の Issue を見てる感じだと、頑張れば見た目カスタマイズできるようになるのかなぁ??

Xamarin.Forms 3.6(プレビューリリース)

やっとプレビューリリースのところまで追いついた。 ここも特に目新しいものはなさそうかな。

docs.microsoft.com

Xamarin.Forms 4.0(プレビューリリース)

4.0 のリリースタイミングで再キャッチアップしてもいいのではないかなって思った。Shell とか CollectionView とか Visual あたりが来ると思うので。

docs.microsoft.com

Viewing all 1388 articles
Browse latest View live


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