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

2016年に最も読まれた記事 TOP10 世間はDataTableと戦っている

$
0
0

2017年も始まってしばらく経ったので2016年の振り返りをして見たいと思います。 ということで、2016年に最も読まれた記事TOP10でも見て見たいと思います。

1位

blog.okazuki.jp

あっはい。

世間は、まだDataTable全盛期なんでしょうか。2009年に書いてる記事がNo1でした。

2位

blog.okazuki.jp

WPF!!

DataTableと比べて大分進化した気がします。2014年の記事ですね。 コレクションをデータバインドするのは、基本ですので是非抑えたいところ。

3位

blog.okazuki.jp

スマホでListView的な表記が流行ってるとはいえDataGridの人気も根強いみたいです。 そういえば、エクセルソフトさんでDataGridのセッションしたりしたなぁ

4位

blog.okazuki.jp

小ネタですね。 まぁヒープに無慈悲にインスタンス作りまくるので、あまり大きなサイズのデータではやりたくないところですが、知りたいことって多いですよね。

5位

blog.okazuki.jp

Excelを出力したいというネタも根強いのでしょう。 何故かNPOIの公式サイトから、Japanese Documentationとしてリンクが貼られてるこのブログ記事が5位にランクインしました。

6位

blog.okazuki.jp

これは何ででしょうね? スクロールが上手くいかないことが多いんでしょうか。

7位

blog.okazuki.jp

WPFで複雑なレイアウトを組むならこのGridですよね。 ということで、こいつが7位にランクインしました。

8位

blog.okazuki.jp

ComboBoxも地味に使うコントロール。WPFではテンプレート使えばかなりリッチな見た目できますよね。

9位

blog.okazuki.jp

多分、Windows Formsから来た人がつまづくであろうポイントの1つですね。 これは重要なので抑えておきましょう!!

10位

blog.okazuki.jp

WPFもルーティングイベントとかあって特殊な感じですよね。 この挙動を知ってるか知らないかで細かな動きの制御ができるかどうかとかが関わって来たりします。

まとめ

来年はDataTableの記事を超えるものを書きたいな。


Xamarin.Formsでグラフを描こう(OxyPlot)

$
0
0

WPFでグラフを描けるライブラリを探してたらOxyPlotというのを見つけました。 見つけたと思ったら、こいつXamarin.Formsでもできるぞ!?ということで試して見ました。

NuGetの追加

以下のパッケージを追加します。

  • OxyPlot.Xamarin.Forms

初期化

以下のコードをXamarin.Forms.Forms.Init()の後に追加します。

PlotViewRenderer.Init();

画面構築

画面にPlotView部品を置きます。そしてModelプロパティをバインドします。

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"    xmlns:local="clr-namespace:OxyApp"    xmlns:oxy="clr-namespace:OxyPlot.Xamarin.Forms;assembly=OxyPlot.Xamarin.Forms"    x:Class="OxyApp.OxyAppPage">
    <ContentPage.BindingContext>
        <local:OxyAppPageViewModel />
    </ContentPage.BindingContext>
    <oxy:PlotView Model="{Binding Model}" />
</ContentPage>

ViewModelの構築

ViewModelでグラフのモデルを構築します。

using System;
using OxyPlot;
using OxyPlot.Series;

namespace OxyApp
{
    publicclass OxyAppPageViewModel
    {
        public PlotModel Model { get; }

        public OxyAppPageViewModel()
        {
            this.Model = new PlotModel { Title = "Hello OxyPlot" };
            this.Model.Series.Add(new LineSeries
            {
                Points =
                {
                    new DataPoint(0, 10),
                    new DataPoint(1, 20),
                    new DataPoint(2, 15),
                    new DataPoint(3, 40),
                }
            });
        }
    }
}

Seriesにグラフの種類を表すSeries(今回の場合は折れ線グラフ)を追加していく形になります。DataPointをPointsに追加することで折れ線のデータを指定できます。

実行して動作確認

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

f:id:okazuki:20170116083107p:plain

感想

パッと触った感じ、なかなかいいんじゃないんでしょうか?? ライセンスもMITで扱いやすそう。

Xamrin.FormsでBLEのアドバタイズパケットを拾おう(要はビーコン)

$
0
0

ということで、Xamarin.FormsでBLEやってみようと思います。 スキャンして、近くのパケット拾って来るのがゴールくらいにしておきましょう。

BLEを使おうとすると、ネイティブのコードを書かないといけないのですが、幸いにもプラグインのリストにBluetooth LEの文字があります。

github.com

こいつですね。

github.com

www.nuget.org

ということで、Acr.BleパッケージをXamarin.Formsのプロジェクトに追加します。

このAcr.BleですがReactive Extensionsに依存しています。なんてこったいいじゃないですか。 対応Rxのバージョンは2.5なので、Xamarin.Formsでも安心して使えます(最新のRxは.NET Standardじゃないと入らない雰囲気)

Androidのプロジェクトで以下のパーミッションを追加します。

  • Bluetooth
  • BluetoothAdmin
  • AccessCoarseLocation

iOSはplistにこれを追加しておけばいいみたいです。

<key>UIBackgroundModes</key><array><string>bluetooth-central</string></array>
<key>NSBluetoothPeripheralUsageDescription</key><string>なんかメッセージ</string>

今回は手抜きで画面には出さずにDebug.WriteLineで表示させるだけにします。Pageに以下のようなコードを追加します。

using System;
using System.Diagnostics;
using System.Reactive.Linq;
using Acr.Ble;
using Xamarin.Forms;

namespace BleDemo
{
    publicpartialclass BleDemoPage : ContentPage
    {
        public BleDemoPage()
        {
            InitializeComponent();

            BleAdapter.Current.Scan().Subscribe(result =>
            {
                Debug.WriteLine($"{result.Device.Name}:{result.Device.Uuid}:{result.Rssi}");
            });
        }
    }
}

これだけ。

これで実行すると以下のような結果が得られます。ちなみにBluetoothをOFFにしてるとScanメソッドがnullを返すみたいなので要注意。

SensorTag:00000000-0000-0000-0000-b4994c6430c4:-59
SensorTag:00000000-0000-0000-0000-b4994c6430c4:-60
SensorTag:00000000-0000-0000-0000-b4994c6430c4:-59
SensorTag:00000000-0000-0000-0000-b4994c6430c4:-58
SensorTag:00000000-0000-0000-0000-b4994c6430c4:-57
SensorTag:00000000-0000-0000-0000-b4994c6430c4:-58
SensorTag:00000000-0000-0000-0000-b4994c6430c4:-60
SensorTag:00000000-0000-0000-0000-b4994c6430c4:-61
SensorTag:00000000-0000-0000-0000-b4994c6430c4:-59

いい感じなのかな。

JXUGC 22 最新事例&お前のアプリを説明してもらおうの会で発表してきました

WPFでWindowが閉じられたときにViewModelの後始末メソッドを呼ぶ

$
0
0

そういう動きをするビヘイビアを作ればOKです。こんな感じで。

publicclass ViewModelCleanupBehavior : Behavior<Window>
{
    protectedoverridevoid OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.Closed += this.WindowClosed;
    }

    privatevoid WindowClosed(object sender, EventArgs e)
    {
        (this.AssociatedObject.DataContext as IDisposable)?.Dispose();
    }

    protectedoverridevoid OnDetaching()
    {
        base.OnDetaching();
        this.AssociatedObject.Closed -= this.WindowClosed;
    }
}

使い方はこんな感じ。

<Window x:Class="CloseAndDisposeSample.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:CloseAndDisposeSample"xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"mc:Ignorable="d"Title="MainWindow"Height="350"Width="525"><Window.DataContext><local:MainWindowViewModel /></Window.DataContext><i:Interaction.Behaviors><local:ViewModelCleanupBehavior /></i:Interaction.Behaviors><Grid></Grid></Window>

これでMainWindowViewModelがIDpsopsableを実装していれば、Windowが閉じられたときにDisposeメソッドが呼び出されます。

めでたしめでたし。

ReactivePropertyでXAMLで.Valueのつけ忘れを教えてくれるReSharperプラグイン

$
0
0

ガチでありがたいことに、よく頂く要望に対するプラグインを作っていただけました。 残念ながら私は、R#先生を使ってないので試せないですがR#を使ってる方はぜひ試してみてください。

resharper-plugins.jetbrains.com

Xamarin.FormsでBotFrameworkのDirectLineを試す

$
0
0

自分用メモです。(雑です)

PCLを辞めます。 .NET Standardにプロジェクトを変換します。

以下をプロジェクトに追加する。

  • Micrsoft.Bot.Connector.DirectrLine
  • Microsoft.Rest.ClientRuntime

こんな感じに書けば動いた!とりあえず

<?xml version="1.0" encoding="utf-8"?><ContentPage xmlns="http://xamarin.com/schemas/2014/forms"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"prism:ViewModelLocator.AutowireViewModel="True"x:Class="PrismUnityApp4.Views.MainPage"Title="MainPage"><StackLayout HorizontalOptions="Center"VerticalOptions="Center"><Button Text="Send"Command="{Binding SendCommand}" /><Button Text="NewConversation"Command="{Binding NewConversationCommand}" /><Label Text="{Binding Message}" /></StackLayout></ContentPage>

C#

using Microsoft.Bot.Connector.DirectLine;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System.Linq;

namespace PrismUnityApp4.ViewModels
{
    publicclass MainPageViewModel : BindableBase, INavigationAware
    {
        privatestaticreadonlystring Secret = "DirectLine の シークレット";
        privatestaticreadonlystring Id = "okazuki";

        private Conversation Conversation { get; set; }

        public DelegateCommand SendCommand { get; }

        public DelegateCommand NewConversationCommand { get; }

        privatestring message;

        publicstring Message
        {
            get { returnthis.message; }
            set { this.SetProperty(refthis.message, value); }
        }

        public MainPageViewModel()
        {
            this.SendCommand = new DelegateCommand(Send);
            this.NewConversationCommand = new DelegateCommand(() => this.Conversation = null);
        }

        private async void Send()
        {
            var client = new DirectLineClient(Secret);
            if (this.Conversation == null)
            {
                this.Conversation = await client.Conversations.StartConversationAsync();
            }

            var activity = Activity.CreateMessageActivity() as Activity;
            activity.From = new ChannelAccount { Id = Id };
            activity.Text = "Hello world";
            var response = await client.Conversations.PostActivityAsync(this.Conversation.ConversationId, activity);
            var activities = await client.Conversations.GetActivitiesAsync(this.Conversation.ConversationId);
            var result = activities.Activities.FirstOrDefault(x => x.ReplyToId == response.Id);

            this.Message = result?.Text;
        }

        publicvoid OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        publicvoid OnNavigatedTo(NavigationParameters parameters)
        {
        }
    }
}

ReactiveProperty v3.5.0をリリースしました

$
0
0

IFilteredReadOnlyObservableCollectionのフィルタ条件をリフレッシュするRefreshメソッドを追加しました。 これで、フィルター条件を途中で変えることができます。

WPFでは無事動いてるのですがUWPとXamarinがちゃんと動くか未知です。というのもRefreshしたあとに要素がある状態というのを許してるかどうかというのがあります。

www.nuget.org


SIerから日本マイクロソフトへ転職しました

$
0
0

2月17日付で富士通の子会社のSIerを退職しました。2月20日から日本マイクロソフトで働きはじめました。 ということで振り返りを。

富士通の子会社に入社

2005年に富士通の子会社に入社しました。確か当時1500人規模だったと記憶しています。 新宿に拠点を構えて、中部、大阪にも支社がある感じです。

そんなところで何をしてきたかというと、半年間みっちりJavaでWebアプリケーション開発を行う研修をしました。 当時はまだVisual Studioとかは企業向けでは無償ではなくてSharpDevelop?とかExpressEditionがではじめたかも?というくらいでした。それを使ってチャットアプリケーションを作って同期にばらまいて、授業中にチャットでわからないところとか質問し合ってたのはいい思い出です。

研修が終わり配属されたのは全社の技術支援を行うような感じの部門。(ちなみに配属先希望の選択肢にはなかった) そこで待ち受けてたのは社内向けJava用Webアプリケーションの開発をすることになりました。

Spring Framework, Hibernateを使った感じのライブラリで、IDEがまさかのSun Java Studio Creator(現在はNetBeansに吸収されて消滅した感じ)。 渋い感じのチョイスだなと思ってました。

既存のDBのスキーマを読み取ってHibernate関連のファイルを自動生成する処理が気に入らなかったので、velocityというテンプレートエンジンを使うように作り替えたりしました。

割と好きにさせてもらえたので当時のリーダーの人には感謝してますし、当時の目標でもありました。途中でいなくなっちゃったけど。

あとは、プロジェクトで何かしら問題が起きたら手伝いに行くとかということをしてました。 DataTableの性能が出ない問題とか、サーバーサイドExcelオートメーションとか辛い問題もありましたが、性能改善とか設計レビューとかそいうのもやってました。あと技術相談とか。

3日のはずが…

そんなこんなで過ごしていたら「クラウドに興味のある人?」という問いが上司から来たので「はーい!」と手をあげたら3日間の教育にぶち込まれることが決まりました。しかも対象は幹部社員クラスの人たちらしい…。やだなぁと思い4連休へ突入したのでした。

4連休明けて会社に戻ってくると3日間の教育は別の人が割り当てられてました。 何があったのか聞いてみると、大田くんには2年間の教育に行ってもらうと言われて??????となりました。

そんなこんなで2年間のクラウドの教育をするという壮大な計画に巻き込まれてしまいました。 ただし、これはあくまで部署として推薦するだけで会社として1名選出する過程で別の部署の人を推すので大田くんがなることはないと言われたのを今でも覚えてます。その日の夕方「大田くんに決まったよ」と言われました。

2年間の本社勤務

ということで電車だけで1時間30分くらいかかる通勤生活が2年間決まりました。 富士通のクラウドの勉強や商談支援などをしてたのですが、当時富士通が富士通のデータセンターにAzureをホストしてたのもあって、それをやりたいやりたいオーラを出してたら、1年たった頃にチームがえがあってそちらに移してもらえました。 そして、さらに半年後なぜかチームリーダーに…。嫌だと言ったのに!!

ここでは、大企業のスピード感(遅い)というものを肌を持って感じました。関係者が増えれば増えるほど色々なしがらみができて意思決定や資料のレビューなどに時間がかかるんだなぁ大変なんだなぁと思いました。

でも、本当にすごい人もいるんだなというのもここで学びました。 マネジメントがきちんとできる人のもとでのチームの作業はとてもやりやすかったですし、Windows系をガチでやってる人スゲェ…というものや、ハードの世界もちょろっとのぞき見しました。振り返ってみるといい経験でした。

パッケージ製品の面倒をみる

出向も無事終わり、戻ってくると共通技術をみる部門は無くなって、その後継の部門に配属という感じになりました。 そこで待ち受けてたのはExcel方眼紙とのにらめっこ。

ちょっと、ここでやる気がマジでなくなって転職を考えたりもしましたが踏みとどまりました。

今と同じようなお仕事が始まる

その後、チーム移動をしてもらってドローンを見たり、UnityしたりAzureしたりして今に至ります。

ドローンは、ARDroneを使ってカメラ画像から地面に貼り付けた色付きガムテープを認識してその上を移動して端っこまで行くと折り返すというのを作ったりしてました。 100万円以上する馬鹿でかいディスプレイの前でやったのが怖かったです。緊急ホバリングボタンを割と押しまくってことなきを得た感じではありました。

最後の我儘

人権がない開発環境(メモリ8GB + HDD)でお仕事してたのですが、その開発環境のリース期限が切れるということで、どんなマシンがいい?と聞かれた時に駄目もとでSurface Bookがいいって言ったのですが、リースの係りの人がめっちゃ頑張ってくれて1月31日に納品されてしまいました。

普通は富士通製のしかダメなんだけどね…本当に頑張ってくれてありがとうございましたという感想しか出て来ません。

納品されたときには辞めるということを伝えていたので上司のもとに行ってしまいました。

PCのリースの切り替えと転職活動は丁度平行で進んでいて、落ちた場合に満足できる環境で仕事したいと思って言って見た我儘でしたが、本当に実現してしまうとは思ってませんでした。結果は転職してしまうことになってしまいました。

大きめのSIerのいいところ

何と言っても新入社員研修がちゃんとあるというのがいいですね。半年間。そのあとも仮配属という形をとっていて本配属まで色々な経験を積ませてもらえます。いきなり配属されると何十年も1つの部署しか知らないっていう人も多くなるので若いうちに色々経験させておこうという試みみたいです。

部署によると思いますが、私の場合は色々な経験を積ませてもらいました。 プロジェクト支援から、社内FWのメンテや、技術資料の整備、プロジェクト体験諸々です。

まぁ人によっては10年間同じ分野のプロジェクトにどっぷりという人もいるので、一概には言えないですが、私の場合は割と好きなことをさせてもらえたと思っています。ここら辺は、入る会社というよりも配属と上司がどんな人かという運が大きいと思います。

あと、教育も色々揃ってたりするので、その気になればいろんな教育を受けることもできますし、富士通向けセミナーとかいうのも大きな会社さんがやってくれるので、そういうところで技術のキャッチアップをすることもできます。まぁここら辺は、一般公開されてるイベントとかセミナーに積極的に参加するのでも構わないと思うんですが、社内で開催されているということで仕事として参加しやすいというのはあるかもしれませんね。

あと、本当に色々なことを会社全体で見たらやっているので、積極的に動いていれば多分いろんなことに出会える確率は高いのではないかと思います。

大きめのSIerのダメなところ

何でしょうね。動きは遅い気がします。 企画がなかなか通らなくて、実装にしわ寄せがきたりとかそういうめんどくさいことはありますね。

Microsoft MVP

日本マイクロソフトで働くことになるということで、Microsoft MVPも卒業することになります。(社員はMVPになれないので) Microsoft MVPも6年やってきてて、そろそろベテランとは行かないまでも、中堅どころなのかな?と思うようになってきてたので、丁度いい引き際なのかもしれません。

Microsoft MVPは、過去1年間のコミュニティ活動などを申請して、それを評価してもらって受かると1年間Microsoft MVPを名乗っていいという表彰制度です。あくまで過去1年間の活動に対する評価なので、MVPになったからと言って何か責任が発生したりするわけではありません。

当然、更新しようと思ったら、7月に申請して更新をしないといけないので、それなりのコミュニティ活動などをしないといけないというのはあります。

Microsoft MVPになって良かったのは、本を書く機会に恵まれたというところでしょうか。id:ch3cooh393先生に、Windows 8のストアアプリの本を書かないかと誘われた時はうれしかったというのを覚えています。Windows store appの本の中では割と売れた本っぽいので満足満足。

https://www.amazon.co.jp/Windows8ストア-アプリ開発入門-大田-一希/dp/4798035440

あと、私は割とコミュ障なのでMVPだということで人に話しかけてもらえるのが割とありがたかったです。 そうでもないと、誰とも話さずに勉強会が終わるとかいうのもザラなので…。

Global Summit

Microsoft MVPの数ある特典の中で一番でかいのがこれです。

1年に1度、マイクロソフト本社でNDAセッションを3日間ぶっ通しで聴ける貴重な機会です。 まぁ全部英語なので、英語できない私にとっては80%くらい情報は素通りしてしまうのですが…。 英語できるようにならないとなぁ。

ちなみに、NDAなもんでMicrosoft MVPがGlobal Summitに行くとNDAじゃないご飯とかパーティのことばかり写真をあげたりツイートしたりするので、アメリカにパーティしに行ってるように見えるのは仕様です。

日本マイクロソフト

ということで、日本マイクロソフトで働くことになりました。 プレミアムフィールドエンジニアというところで働くことになります。 プレミアムサポートのお客様相手のお仕事になるらしいので、私を引っ張り出そうとするとプレミアムサポートを結んで、営業にお願いすればいいのかしら? よくわかんないですね。

まぁ中でいろんなことやって行くことになると思うので楽しみです。

最後に

今までお仕事一緒にしてくれた人達からは、学ぶことがたくさんあり未熟な大学院卒業したての新社会人を、ここまで育ててくれたことに感謝でいっぱいです。 これで、途切れることなく、何かしらで絡むことができたらいいなぁと思っています。

これからも、日々学ぶこと、そしてそれをアウトプットすることを目標に頑張って行きたいと思います。

ということで私の退職エントリはこれでおしまい。最後まで見てくれてありがとうございました!

Python入門者の集い #4 でLTしてきました

$
0
0

転職してはじめての発表はPythonの勉強会でVisual Studioはいいぞ!してきました。インテリセンスが無いと開発できない体になってしまってる自分がいる。

Azure AD対応のマルチテナントアプリケーションを作ってみよう

$
0
0

以下のサイトにサンプルがあるので、それに沿ってやっていきます。

github.com

Azure ADのディレクトリを作る

というわけでさくっとクラシックポータルからAzure ADのディレクトリを作ります。 ここでは、okazukimultitenanttestという名前で作りました。

アプリケーションも作りましょう。multitenanttestappという名前でWebアプリケーションとして作っておきます。 サイオンオンURLと応答URLを、以下のような感じにします。

https://これから作るWebAppsの名前.azurewebsites.net/

アプリケーション ID/URIを以下のようにします。

https://<テナント名>.onmicrosoft.com/なんか適当

テナント名は今回はokazukimultitenanttestなのでhttps://okazukimultitenanttest.onmicrosoft.com/multitenantappみたいにしました。

アプリケーションの追加から、Microsoft Graphを追加してデリゲートされたアクセス許可にSign in and read user profileを選択します。

そして、キーを生成してメモっておきます。

アプリの作成

空のASP.NET Webアプリケーションを作ります。MVCにチェックを入れておきましょう。 クラウドにホストするを選んでAzureのWeb appsにデプロイできるようにしておきます。(後からでもできる)

ControllersフォルダにHomeControllerを作ります。Indexに対してViewも作っておきましょう。ここら辺は、Indexアクションを右クリックしてビューを追加を選ぶと楽です。

NuGetの追加

NuGetを使って以下のパッケージをインストールします。

  • Microsoft.Owin.Host.SystemWeb
  • Microsoft.Owin.Security.OpenIdConnect
  • Microsoft.Owin.Security.Cookies

Startup.csの追加

OWINのスタートアップのクラスを作ります。Visual StudioにOWIN Stgartupクラスのテンプレートがあるので、そこから選んで作りましょう。

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
using Microsoft.IdentityModel.Protocols;
using System.Configuration;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using System.IdentityModel.Tokens;
using System.IdentityModel.Claims;

[assembly: OwinStartup(typeof(MultiTenantApp.Startup))]

namespace MultiTenantApp
{
    publicclass Startup
    {
        publicvoid Configuration(IAppBuilder app)
        {
            var clientId = ConfigurationManager.AppSettings["ida:ClientId"];
            var authority = "https://login.microsoftonline.com/common";

            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions());

            app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    ClientId = clientId,
                    Authority = authority,
                    TokenValidationParameters = new TokenValidationParameters
                    {
                        // 自分でやるのでfalse
                        ValidateIssuer = false,
                    },
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        RedirectToIdentityProvider = ctx =>
                        {
                            var appBaseUrl = $"{ctx.Request.Scheme}://{ctx.Request.Host}{ctx.Request.PathBase}";
                            ctx.ProtocolMessage.RedirectUri = appBaseUrl;
                            ctx.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
                            return Task.FromResult(0);
                        },
                        SecurityTokenValidated = ctx =>
                        {
                            // ここで本当はテナントIDとかのチェックをする
                            var issuer = ctx.AuthenticationTicket.Identity.FindFirst("iss").Value;
                            var upn = ctx.AuthenticationTicket.Identity.FindFirst(ClaimTypes.Name).Value;
                            var tenantId = ctx.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;

                            // 上記値をチェックして不正なテナントやユーザーだったら// SecurityTokenValidationExceptionを投げるreturn Task.FromResult(0);
                        },
                        AuthenticationFailed = ctx =>
                        {
                            // エラーの時のリダイレクト
                            ctx.OwinContext.Response.Redirect("/Home/Error");
                            ctx.HandleResponse();
                            return Task.FromResult(0);
                        }
                    }
                });
        }
    }
}

画面の作成

サインインして情報を表示するプログラムを書いてみましょう。HomeControllerを以下のように書き換えます。

using System;
using System.Collections.Generic;
using System.IdentityModel.Claims;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MultiTenantApp.Controllers
{
    [Authorize]
    publicclass HomeController : Controller
    {
        // GET: Homepublic ActionResult Index()
        {
            var ctx = this.HttpContext.GetOwinContext();
            var issuer = ctx.Authentication.User.FindFirst("iss").Value;
            var upn = ctx.Authentication.User.FindFirst(ClaimTypes.Name).Value;
            var tenantId = ctx.Authentication.User.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;

            this.ViewBag.Issuer = issuer;
            this.ViewBag.Upn = upn;
            this.ViewBag.TenantId = tenantId;

            return View();
        }

        public ActionResult SignOut()
        {
            this.HttpContext.GetOwinContext().Authentication.SignOut();
            return Redirect("/Home");
        }
    }
}

Authorize属性をつけている点とOwinContext経由で認証情報を取っているところがポイントです。

Index.cshtmlでは以下のようにViewBagの情報を画面に出しています。

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2><ul><li>@this.ViewBag.Issuer</li><li>@this.ViewBag.Upn</li><li>@this.ViewBag.TenantId</li></ul>

Web.configへの設定の追加

Web.configに設定を追加します。ADにアプリ作ったときに取得できるクライアントIDとキーをappSettingsのida:ClientIdとida:Passwordに設定します。

<add key="ida:ClientId"value="クライアントID" /><add key="ida:Password"value="パスワード" />

実行して動作確認

デプロイして実行してみると以下のようにログイン画面が出ます。(httpsでアクセスしてね) ログインが求められるのでお手持ちのAzure ADのテナントに登録されているユーザーでログインしてみます。(無ければAzureクラシックポータルから作ってください)

ログインすると、以下のように情報が表示されます。

f:id:okazuki:20170302140747p:plain

テナントIDの取得方法

Azure ADのテナントIDの取得方法ですが、以下のようにして取得できるみたいです。

stackoverflow.com

以下のURLにアクセス

https://login.windows.net/YOURDIRECTORYNAME.onmicrosoft.com/.well-known/openid-configuration

出てきたJSONのauthorization_endpointにあるGUIDっぽいのがテナントIDになります。

一定時間やりなおしが出来るようにする

$
0
0

メール削除とかしたけど、一定時間取返しがきくみたいなUIがありますよね。 あぁいうのどうやるんだろうというのを考えてみました。

UWPでやってみますが、WPFでもXamarin.Formsでも基本的に同じ感じになると思います。見た目凝るのが一番難しそう。

Modelの作成

とりあえず、ReactivePropertyとPrism.Coreを参照に追加して以下のコードを書きます。

using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;

namespace App2
{
    class LazyActionSampleApp
    {
        public ObservableCollection<Guid> Guids { get; } = new ObservableCollection<Guid>();

        public Task<bool> AddGuidAsync(IObservable<Unit> cancel)
        {
            var guid = Guid.NewGuid();
            this.Guids.Add(guid);

            var tcs = new TaskCompletionSource<bool>();

            Observable.Return<Func<bool>>(() => true)
                .Delay(TimeSpan.FromMilliseconds(3000))
                .Amb(cancel.Take(1).Select<Unit, Func<bool>>(_ => () => { this.Guids.Remove(guid); returnfalse; }))
                .Subscribe(x => tcs.SetResult(x()));

            return tcs.Task;
        }
    }
}

Reactive ExtensionsのAmbを使うと先に発火したほうを後ろに流すことができます。これを使ってコミット処理(今回はtrueを返すだけ)とキャンセル処理(今回はコレクションに追加したものを削除する)といったFuncを合成してSubscribeして実行しています。一応処理が成功したのかキャンセルされたのかは戻り値で返すようにしました。

あとは、VMとVを作っておしまい。

using Prism.Mvvm;
using Reactive.Bindings;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;

namespace App2
{
    publicclass MainPageViewModel : BindableBase
    {
        private LazyActionSampleApp Model { get; } = new LazyActionSampleApp();

        public ObservableCollection<Guid> Guids => this.Model.Guids;

        public ReactiveProperty<bool> IsBusy { get; } = new ReactiveProperty<bool>(false);

        public AsyncReactiveCommand AddCommand { get; }

        public AsyncReactiveCommand CancelAddCommand { get; }

        public MainPageViewModel()
        {
            this.AddCommand = new AsyncReactiveCommand(this.IsBusy.Select(x => !x).AsObservable());
            this.CancelAddCommand = new AsyncReactiveCommand(this.IsBusy.AsObservable());

            var cancelTrigger = new Subject<Unit>();
            this.AddCommand
                .Subscribe(async x =>
                {
                    this.IsBusy.Value = true;
                    await this.Model.AddGuidAsync(cancelTrigger);
                    this.IsBusy.Value = false;
                });

            this.CancelAddCommand.Subscribe(_ =>
            {
                cancelTrigger.OnNext(Unit.Default);
                return Task.CompletedTask;
            });
        }
    }
}
using Windows.UI.Xaml.Controls;

namespace App2
{
    publicsealedpartialclass MainPage : Page
    {
        private MainPageViewModel ViewModel { get; } = new MainPageViewModel();

        public MainPage()
        {
            this.InitializeComponent();
        }
    }
}
<Pagex:Class="App2.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App2"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition /></Grid.RowDefinitions><Button Content="Add"Command="{x:Bind ViewModel.AddCommand}"HorizontalAlignment="Stretch"/><Button Content="Cancel"Command="{x:Bind ViewModel.CancelAddCommand}"HorizontalAlignment="Stretch"Grid.Row="1" /><ListView ItemsSource="{x:Bind ViewModel.Guids}"Grid.Row="2" /></Grid></Page>

実行結果

こんな感じになります。

www.youtube.com

ASP.NET CoreでAngularをする下準備

$
0
0
2017/03/05 日時点の情報です

@angular/cliとか使って始めるのもいいですが、これだとサーバーサイド何でつくるの??という感じになってしまうので、C#erとしては今やるならASP.NET Coreでしょ!ということで始め方をメモっておきます。

結論としてはdotnetコマンドで作れます。ということで最新の.NET Core SDK 1.1のRC4を以下のページからダウンロードしてきました。

github.com

インストールしてdotnet --infoと打ち込むと以下のように表示されます。

PS C:\Users\kaota\Documents\Visual Studio Code\Projects> dotnet --info
.NET Command Line Tools (1.0.0-rc4-004771)

Product Information:
 Version:            1.0.0-rc4-004771
 Commit SHA-1 hash:  d881d45b75

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.14393
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\1.0.0-rc4-004771

次にAngularとかSPA関連のプロジェクトテンプレートをダウンロードします。以下のコマンドを打ち込みます。

dotnet new --install Microsoft.AspNetCore.SpaTemplates::*

しばらく待ってるとインストールが終わります。終わったらdotnet newと打ち込んでみましょう。以下のようにAngularとかのテンプレートがインストールされてることがわかります。(Vue.jsはないのね)

PS C:\Users\kaota\Documents\Visual Studio Code\Projects> dotnet new
Template Instantiation Commands for .NET Core CLI.

Usage: dotnet new [arguments] [options]

Arguments:
  template  The template to instantiate.

Options:
  -l|--list         List templates containing the specified name.
  -lang|--language  Specifies the language of the template to create
  -n|--name         The name for the output being created. If no name is specified, the name of the current directory is
 used.
  -o|--output       Location to place the generated output.
  -h|--help         Displays help for this command.
  -all|--show-all   Shows all templates


Templates                                     Short Name      Language      Tags
------------------------------------------------------------------------------------------
Console Application                           console         [C#], F#      Common/Console
Class library                                 classlib        [C#], F#      Common/Library
Unit Test Project                             mstest          [C#], F#      Test/MSTest
xUnit Test Project                            xunit           [C#], F#      Test/xUnit
Empty ASP.NET Core Web Application            web             [C#]          Web/Empty
MVC ASP.NET Core Web Application              mvc             [C#], F#      Web/MVC
MVC ASP.NET Core with Angular                 angular         [C#]          Web/MVC/SPA
MVC ASP.NET Core with Aurelia                 aurelia         [C#]          Web/MVC/SPA
MVC ASP.NET Core with Knockout.js             knockout        [C#]          Web/MVC/SPA
MVC ASP.NET Core with React.js                react           [C#]          Web/MVC/SPA
MVC ASP.NET Core with React.js and Redux      reactredux      [C#]          Web/MVC/SPA
Web API ASP.NET Core Web Application          webapi          [C#]          Web/WebAPI
Solution File                                 sln                           Solution

Examples:
    dotnet new mvc --auth None --framework netcoreapp1.0
    dotnet new classlib --framework netstandard1.4
    dotnet new --help

では、適当なフォルダ(ここではdotnet-angular-helloworldという名前にしました)を作って、そこでdotnet new angularをしてみましょう。

そして、npm installdotnet restoreして必要なパッケージをダウンロードします。

最後にdotnet runで実行してみます。そして http://localhost:5000/にアクセスしてみましょう。

以下のように、ちょっとハローワールド…?というくらい作りこまれたページが表示されます。

f:id:okazuki:20170305091636p:plain

ふむ。とりあえず、これでAngular始めるときのとっかかりとしてC#erがやるにはちょうどいいかも?サーバーサイドはASP.NET Coreでいけるしね!

ASP.NET CoreでAngularをするHello world

$
0
0

以下の記事の続きです。

blog.okazuki.jp

ひな形に自分のプログラムを追加してHello worldしてみます。

ClientAppフォルダにAngularのプログラムの本体がいます。そこを弄っていきます。

ClientApp/app/componentsフォルダにhelloworldフォルダを作りましょう。そして、その中にhelloworld.component.tsファイルとhelloworld.component.htmlを作ります。

Angularのお作法に従いコンポーネントを作っていきます。helloworld.component.tsは以下のような感じで。

import { Component, OnInit } from '@angular/core';

@Component({
    selector: 'helloworld',
    templateUrl: './helloworld.component.html'
})
export class HelloWorldComponent implements OnInit {
    constructor() { }
    
    title:string = "Hello world";

    ngOnInit() { }
}

helloworld.component.htmlは以下のような感じにします。

<h1>{{title}}</h1>

コンポーネントのプロパティを単純に表示してるだけですね。

ClientApp/app/app.module.tsに作成したモジュールを追加します。ルーティングもhelloworldという文字列で遷移するように設定しておきます。

import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { UniversalModule } from 'angular2-universal';
import { AppComponent } from './components/app/app.component'
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';
import { HelloWorldComponent } from "./components/helloworld/helloworld.component";

@NgModule({
    bootstrap: [ AppComponent ],
    declarations: [
        AppComponent,
        NavMenuComponent,
        CounterComponent,
        FetchDataComponent,
        HomeComponent,
        HelloWorldComponent
    ],
    imports: [
        UniversalModule, // Must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too.
        RouterModule.forRoot([
            { path: '', redirectTo: 'home', pathMatch: 'full' },
            { path: 'home', component: HomeComponent },
            { path: 'counter', component: CounterComponent },
            { path: 'fetch-data', component: FetchDataComponent },
            { path: 'helloworld', component: HelloWorldComponent },
            { path: '**', redirectTo: 'home' }
        ])
    ]
})
export class AppModule {
}

そして、ナビゲーションメニューのClientApp/app/components/navmenu/navmenu.component.htmlにメニューを追加します。

<divclass='main-nav'><divclass='navbar navbar-inverse'><divclass='navbar-header'><buttontype='button'class='navbar-toggle'data-toggle='collapse'data-target='.navbar-collapse'><spanclass='sr-only'>Toggle navigation</span><spanclass='icon-bar'></span><spanclass='icon-bar'></span><spanclass='icon-bar'></span></button><aclass='navbar-brand' [routerLink]="['/home']">dotnet_angular_helloworld</a></div><divclass='clearfix'></div><divclass='navbar-collapse collapse'><ulclass='nav navbar-nav'><li [routerLinkActive]="['link-active']"><a [routerLink]="['/home']"><spanclass='glyphicon glyphicon-home'></span> Home
                    </a></li><li [routerLinkActive]="['link-active']"><a [routerLink]="['/counter']"><spanclass='glyphicon glyphicon-education'></span> Counter
                    </a></li><li [routerLinkActive]="['link-active']"><a [routerLink]="['/fetch-data']"><spanclass='glyphicon glyphicon-th-list'></span> Fetch data
                    </a></li><li [routerLinkActive]="['link-active']"><a [routerLink]="['/helloworld']">
                        Hello world
                    </a></li></ul></div></div></div>

他のを真似て書いてるだけで何がおきてるのか私にもわかりません。

そして、アプリケーションのルートフォルダでwebpackコマンドを実行します。(webpack入れてないひとはnpm install -g webpackしてね)

PS C:\Users\kaota\Documents\Visual Studio Code\Projects\dotnet-angular-helloworld> webpack
Hash: 58aabf820ca74ec21bf0a8dac7da2d1b48f06b83
Version: webpack 2.2.1
Child
    Hash: 58aabf820ca74ec21bf0
    Time: 10190ms
                 Asset     Size  Chunks             Chunk Names
        main-client.js  24.1 kB       0  [emitted]  main-client
    main-client.js.map  27.2 kB       0  [emitted]  main-client
Child
    Hash: a8dac7da2d1b48f06b83
    Time: 10155ms
             Asset    Size  Chunks             Chunk Names
    main-server.js  154 kB       0  [emitted]  main-server

そして、dotnet runをしましょう。

http://localhost:5000/にアクセスするとHello worldメニューが増えています。そして、それをクリックすると以下のようにハローワールドが表示されます。

f:id:okazuki:20170305095150p:plain

やったね!

ASP.NET CoreでAngularをする VSCodeを使おう

$
0
0

dotnet new angularでプロジェクト作成して、webpackしてdotnet runで実行できることはわかりました。 では、Visual Studio Codeで快適に作業するにはどうするのがいいのか?ということになりますよね!

Visual Studio Codeで開発するには以下のような感じでとりあえずやってます。

まずは、PowerShellあたりで適当なフォルダを掘ってdotnet new angularします。

> mkdir AngularVSCode
> cd AngularVSCode
> dotnet new angular

必要パッケージを入れます

> npm install
> dotnet restore

VSCodeを立ち上げましょう!

> code .

ターミナルを表示して、webpack -wと打ち込みます。これでTypeScriptに変更があったら自動でwebpackが走るようになります。もう1つターミナルを立ち上げてdotnet runしましょう。

これで下準備完了です! あとは、Angular弄ってブラウザをリフレッシュすれば変更が反映された状態になります。

では、快適Angular生活をVisual Studio Codeで!!


Visual Studio 2017 リリース記念勉強会で発表してきました

$
0
0

3月11日にC#ユーザー会が開催したVisual Studio 2017 リリース記念勉強会で発表してきました。

発表資料を公開します。

30分というあわただしい感じの発表になってしまいましたが、なにか1つくらい知らない機能を持ち帰っていただけたらいいなと思って発表しました。どうだったのかなぁ。

Visual Studio 2017 リリース記念勉強会の動画が公開されました

$
0
0

先日開催されたVisual Studio 2017リリース記念勉強会ですが

blog.okazuki.jp

録画されていた動画が公開されました。

www.youtube.com

実際に当日来れてないかたは是非見てみてね!

ReactiveProperty 3.6.0をリリースしました

$
0
0

昨日の夜にやろうと思ったんですが今日になってしまいました。

www.nuget.org

今回は、iOS向けのSetBindingメソッドが正しく動いてなかった奴の修正と、ReactiveCommandAsyncReactiveCommandクラスのSubscribeメソッドに引数無し版を追加しました。どちらもプルリクエストです。ありがたやありがたや。

Xamarin + Visual Studio Mobile Centerをやってみよう

$
0
0

Visual Studio Mobile Centerは、モバイルアプリを開発する際に必要になる以下の機能を提供してくれる便利なやつです。

  • リポジトリ(GitHub, VSTS, Bitbucket)と連携しての自動ビルド
  • Xamarin.UITestなどのテストコードをプールされた様々な実機上で実行
  • アプリのテスト配布
  • クラウド上のデータストア
  • SNSやAzure ADと連携した認証機能
  • テレメトリ収集
  • クラッシュレポート

1つ1つ見て行こうと思います。

アプリの作成

まず、機能を使うためにMobile Center上でアプリを作らないといけません。Mobile Centerの画面の右上にアプリを作るためのボタンがあるので、Add New Appします。

f:id:okazuki:20170408163559p:plain

そうすると、アプリの情報を入れる画面が出てきます。

f:id:okazuki:20170408163820p:plain

名前などの必要な情報を入れて画面下部のボタンを押すだけです。 今回は、AndroidでXamarinを選択して見ました。

これでアプリができました。

f:id:okazuki:20170408164016p:plain

リポジトリと連携しての自動ビルド

自動ビルドしたいですよね? ということで簡単にできます。アプリの画面の左側からBuildを選びます。 そうすると以下のように、どのサービスと繋ぐかが画面に出てきます。

f:id:okazuki:20170408164114p:plain

ここではGitHubを選んで適当なXamarinのプロジェクトの入ったリポジトリを選択します。

リポジトリを選ぶと、どのブランチに対してビルドをするのかというのを選択する画面になります。今回選んだリポジトリは、masterブランチしかないため以下のように1つだけの選択肢になってます。

f:id:okazuki:20170408164254p:plain

ブランチを選ぶと構成してね見たいな画面になるのでSet up branchを選びます。

f:id:okazuki:20170408164423p:plain

Build on pushで、ブランチにプッシュが来るたびにビルドが走るようになります。

Sign buildsで、署名されたパッケージを作ってくれます。Androidの場合は、Xamarin StudioやVisual Studioでアーカイブ作るときに作れるkeystoreを指定します。iOSの場合は、プロビジョニングプロファイル?と証明書?を指定するみたいです。

iOSに必要なフィアルとかは、ここら辺のサイトが参考になりそうです。

macdays.hatenablog.com

Run a launch test on a deviceでは、とりあえずアプリを実機に入れて起動するかどうかだけの確認をしてくれるみたいです。

Distribute buildsは、アプリをテスト配布するかどうかが選べます。これをOnにすると、後述する、配布先のグループみたいなものに登録されてる人たちにメールを飛ばすことができるようになります。

これで自動ビルドの構成は終わりです。簡単ですね。VSTS使うよりお手軽です。(その代わりカスタマイズできない)

f:id:okazuki:20170408170452p:plain

Test

Testは、実機テストしてくれます。

Testを選んでNew test runを選択すると、デバイスを選んだり言語を選んだりする画面が出てきます。

f:id:okazuki:20170408170637p:plain

f:id:okazuki:20170408170720p:plain

色々なテストフレームワークに対応しています。ここではXamarin.UITestを選んで見ました。

そうすると、nodejsでmobile-center-cliを入れて以下のコマンドを叩けという案内が出てきます。

f:id:okazuki:20170408171639p:plain

言われた通りに叩きます。叩く場所は、書いてある通り、Xamarinのプロジェクトのpackagesフォルダになります。

こんな感じのコマンドになります。

mobile-center test run uitest --app "kaota/HelloWorld" --devices XXXXXXXX --app-path com.companyname.DemoDemoApp.apk  --test-series "master" --locale "ja_JP" --build-dir ../DemoDemoApp.UITest/bin/Release

このコマンドを叩くとmobile-center loginしろと言われるので、言われた通りmobile-center loginをします。このとき要注意なのが、MSアカウントとかと紐付けたMobile Centerのアカウントだと認証失敗するというところです。これは、パスワードを忘れた時のフローを流してパスワード再発行すると上手くいくようになります。

詳細は、ここに書いてあります。(こんなのわからんわ!)

docs.microsoft.com

テストの実行結果は以下のように閲覧することができます。

f:id:okazuki:20170408173056p:plain

f:id:okazuki:20170408173111p:plain

Distribute

これは、ビルド成果物や自分でパッケージングしたapkとかをテスト配布するための機能を提供します。

グループを作って、そこにメアドで人を招待して新しいリリースを作ることでパッケージをダウンロードするためのURLの入ったメールが飛ぶという感じです。そんなに難しくないですね。

f:id:okazuki:20170408173512p:plain

Tables / Identity

これは、Microsoft Azure Mobile Appsを作ってくれて、それのEasy tableと認証機能を使えるようにしているだけです。詳しくはMobile Appsの説明を見て見てください。

Crashes / Analytics

これは、クラッシュレポートとテレメトリの収集なんかができるようになります。 導入方法は簡単で、Mobile Center作ったときに表示される手順に従ってNuGetからパッケージを入れて初期化コードを書いて、必要に応じてトレースログのコードを入れるだけです。

入れるパッケージは以下の2つになります。

  • Microsoft.Azure.Mobile.Analytics
  • Microsoft.Azure.Mobile.Crashes

そして、Xamarin.Formsの場合は、以下のような初期化コードをApp.xaml.csのOnStartメソッドの中に書きます。 キー情報が必要になりますが、これはMobile Centerでアプリを作った時の画面に表示されています。

protectedoverridevoid OnStart()
{
    base.OnStart();
    MobileCenter.Start("ios=iOS用にmobile centerでアプリを作って取得できるキー;" +
           "android=Androidようにmobile centerでアプリを作って取得できるキー",
        typeof(Analytics),
        typeof(Crashes));
}

あとは、以下のようにトレース取りたいところに以下のようなコードを差し込むだけです。

Analytics.TrackEvent(
    "ItemsPage appearing.",
    new Dictionary<string, string> { { "Category", "Trace" } });

これで、Analyticsのところで以下のようにトレースを確認できます。

f:id:okazuki:20170408174354p:plain

f:id:okazuki:20170408174408p:plain

また、アプリが例外でクラッシュしたときに自動的にレポートが上がるようになります。以下のように確認ができます。

f:id:okazuki:20170408174530p:plain

f:id:okazuki:20170408174544p:plain

まとめ

モバイルアプリを開発していると欲しくなるようなテレメトリ収集や、クラッシュレポートなんかもついてるし、自動ビルドや実機テスト、テスト配布なんかもできていい感じです。おまけにMobile Appsの機能も使えちゃう。 早く正式リリースされないかなぁ。(お値段いくらになるんだろう)

UWP Community Toolkitを使って今風?なListViewのUIを実現する

$
0
0

UWP Community Toolkitが、ちょっと目をはなしてる隙に1.4.1にまでバージョンアップしてました。 色々なコントロールとかが提供されているのですが、ListViewに絡むコントロールをいくつか紹介したいと思います。

インクリメンタルローディング

最近のアプリって下の方までスクロールすると続きを読み込むっていうUI多いですよね? ということで、そういう機能を実現する方法がUWPに提供されています。

ISupportIncrementlLoadingインターフェースがそれになります。

docs.microsoft.com

ただ、こいつの実装だるいんですよね。 ということで、UWP Community Toolkitでは、いい感じに扱ってくれる機能を提供していたりします。

ということでさっそく作ってみましょう。

まず、何でもいいので表示用のデータの入れ物クラスを準備します。

publicclass Person
{
    publicstring Name { get; set; }

    publicoverridestring ToString() => this.Name;
}

このクラスのデータをインクリメンタルローディングしてみようと思います。UWP Community ToolkitにはIIncrementalSource<T>というインターフェースがあります。こいつを実装することでインクリメンタルローディングが出来るようになります。

インターフェースは単純でページ数とデータ取得件数が渡ってくるので、それの部分に対応したデータを返すだけです。

ということで、NuGetでUWP Community Toolkitで検索してパッケージを追加しましょう。Microsoft.Toolkit.Uwp.UI.Controlsが今回のお目当てのパッケージになります。

IIncrementalSource<T>インターフェースの実装は以下のような感じになります。

publicclass PersonSource : IIncrementalSource<Person>
{
    public Task<IEnumerable<Person>> GetPagedItemsAsync(
        int pageIndex, 
        int pageSize, 
        CancellationToken cancellationToken = default(CancellationToken))
    {
        Debug.WriteLine($"GetPagedItemsAsync: {pageIndex}, {pageSize}");
        var results = Enumerable.Range(pageIndex * pageSize, pageSize)
            .Select(x => new Person { Name = $"tanaka {x}" });
        return Task.FromResult(results);
    }
}

今回は、ログを吐いて適当にデータを生成して返しています。本当は、ここでREST APIとか叩いてデータを取ってくる感じになります。

IIncremantlSource<T>を実装したら、それを扱うためのIncrementalLoadingCollection<TSource, T>クラスを作ります。今回はMainPageにプロパティとして持たせました。

publicsealedpartialclass MainPage : Page
{
    public IncrementalLoadingCollection<PersonSource, Person> People { get; } = new IncrementalLoadingCollection<PersonSource, Person>();

    public MainPage()
    {
        this.InitializeComponent();
    }
}

あとは、適当にXAMLでこれをバインドします。

<Page x:Class="App13.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App13"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><ListView ItemsSource="{x:Bind People}"></ListView></Grid></Page>

実行すると、以下のような結果になります。(といっても普通にデータが表示されるだけです)下までスクロールすると延々とデータが表示されるのがわかります。

f:id:okazuki:20170417111313p:plain

ひっぱって更新(PullToRefresh)

UWPのListViewに何故標準でないのか?という引っ張って更新機能ですが、UWP Community ToolkitにはPullToRefreshListViewというコントロールがあります。ListViewのかわりに使うことで、引っ張って更新が簡単に実現できます。

PullToRefreshContentプロパティで、引っ張ったときに表示されるメッセージやコンテンツを編集できます。ReleaseToRefreshContentプロパティで更新されるところまで引っ張ったときに表示されるメッセージやコンテンツを編集できます。

そして、RefreshRequestedイベントかRefreshCommandを処理することで引っ張って更新時の処理を実行できます。コード例は以下のような感じになります。

<Page x:Class="App13.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App13"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><controls:PullToRefreshListView ItemsSource="{x:Bind People}"PullToRefreshContent="引っ張って更新"ReleaseToRefreshContent="離すと更新します"RefreshRequested="PullToRefreshListView_RefreshRequested"></controls:PullToRefreshListView></Grid></Page>

イベントハンドラでは、今回の例ではIncrementalLoadingCollection<TSource, T>RefreshAsyncメソッドをたたいています。

publicsealedpartialclass MainPage : Page
{
    public IncrementalLoadingCollection<PersonSource, Person> People { get; } = new IncrementalLoadingCollection<PersonSource, Person>();

    public MainPage()
    {
        this.InitializeComponent();
    }

    private async void PullToRefreshListView_RefreshRequested(object sender, EventArgs e)
    {
        await this.People.RefreshAsync();
    }
}

実行すると以下のような感じで引っ張って更新が出来るようになります。

f:id:okazuki:20170417112248p:plain

f:id:okazuki:20170417112258p:plain

左右にスワイプ可能なListView

最近よくあるUIでListViewの項目を左右にスワイプすることでアクションを実行するというものがあります。これもUWP Community Toolkitで提供されています。

SlidableListItemListViewItemTemplateに設定することで実現可能です。注意点としては、SlidableListItemListViewItemいっぱいに表示するためにHorizontalContentAlignmentVertlcalContentAlignmentStretchに設定しておくという点があります。

<Page x:Class="App13.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App13"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><controls:PullToRefreshListView ItemsSource="{x:Bind People}"PullToRefreshContent="引っ張って更新"ReleaseToRefreshContent="離すと更新します"RefreshRequested="PullToRefreshListView_RefreshRequested"SelectionMode="None"><ListView.ItemTemplate><DataTemplate x:DataType="local:Person"><controls:SlidableListItem><TextBlock Text="{x:Bind Name}" /></controls:SlidableListItem></DataTemplate></ListView.ItemTemplate><ListView.ItemContainerStyle><Style TargetType="ListViewItem"><Setter Property="HorizontalContentAlignment"Value="Stretch" /><Setter Property="VerticalContentAlignment"Value="Stretch" /></Style></ListView.ItemContainerStyle></controls:PullToRefreshListView></Grid></Page>

これで実行すると以下のように左右にスワイプするとアイコンが表示されるようになります。

f:id:okazuki:20170417112834p:plain

アイコンのカスタマイズは、LeftIconプロパティとRightIconプロパティで行います。

<controls:SlidableListItem LeftIcon="Accept"RightIcon="AddFriend"><TextBlock Text="{x:Bind Name}" /></controls:SlidableListItem>

左右にスワイプされたときの処理はLeft/RightCommandRequestedイベントかLeft/RightCommandを処理することで出来ます。Left/RightCommandParameterを活用することでスワイプされた項目のデータを渡したりもできます。

ICommandインターフェースの実装がだるいのでPrism.CoreをNuGetから参照に追加して以下のようなコードを書きます。

publicsealedpartialclass MainPage : Page
{
    public IncrementalLoadingCollection<PersonSource, Person> People { get; } = new IncrementalLoadingCollection<PersonSource, Person>();

    public DelegateCommand<Person> LeftCommand { get; }

    public DelegateCommand<Person> RightCommand { get; }

    public MainPage()
    {
        this.InitializeComponent();
        this.LeftCommand = new DelegateCommand<Person>(x => Debug.WriteLine($"LeftCommand: {x}"));
        this.RightCommand = new DelegateCommand<Person>(x => Debug.WriteLine($"RightCommand: {x}"));
    }

    private async void PullToRefreshListView_RefreshRequested(object sender, EventArgs e)
    {
        await this.People.RefreshAsync();
    }
}

XAMLは以下のような感じになります。

<Page x:Class="App13.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App13"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"mc:Ignorable="d"x:Name="page"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><controls:PullToRefreshListView ItemsSource="{x:Bind People}"PullToRefreshContent="引っ張って更新"ReleaseToRefreshContent="離すと更新します"RefreshRequested="PullToRefreshListView_RefreshRequested"SelectionMode="None"><ListView.ItemTemplate><DataTemplate x:DataType="local:Person"><controls:SlidableListItem LeftIcon="Accept"RightIcon="AddFriend"LeftCommand="{Binding LeftCommand, ElementName=page}"LeftCommandParameter="{Binding}"RightCommand="{Binding RightCommand, ElementName=page}"RightCommandParameter="{Binding}"><TextBlock Text="{x:Bind Name}" /></controls:SlidableListItem></DataTemplate></ListView.ItemTemplate><ListView.ItemContainerStyle><Style TargetType="ListViewItem"><Setter Property="HorizontalContentAlignment"Value="Stretch" /><Setter Property="VerticalContentAlignment"Value="Stretch" /></Style></ListView.ItemContainerStyle></controls:PullToRefreshListView></Grid></Page>

実行すると以下のように、左右にスワイプしたらログが出るようになります。

f:id:okazuki:20170417113611p:plain

逆方向にスクロールするとすぐ表示されるヘッダー

これ名前なんて言うんですかね。Facebookアプリとか使ってるとコンテンツを見るために下にスクロールするとヘッダー(ボタンとか操作項目が並んでる)が非表示になって、コンテンツが画面いっぱいに表示されるけど、ちょっと逆方向にスクロールすると、すぐにヘッダーが表示されるっていうやつです。

これは、ScrollHeaderコントロールをListViewHeaderに設定することで実現できます。このとき、ScrollHeaderコントロールのModeプロパティをQuickReturnにしてホストするListViewコントロールをTargetListViewBaseに設定するのがポイントです。

<Page x:Class="App13.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App13"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"mc:Ignorable="d"x:Name="page"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><controls:PullToRefreshListView x:Name="listView"ItemsSource="{x:Bind People}"PullToRefreshContent="引っ張って更新"ReleaseToRefreshContent="離すと更新します"RefreshRequested="PullToRefreshListView_RefreshRequested"SelectionMode="None"><ListView.Header><controls:ScrollHeader Mode="QuickReturn"TargetListViewBase="{x:Bind listView}"><Border Background="Beige"><StackPanel Padding="10"Orientation="Horizontal"><Button Content="Foo"Margin="5" /><Button Content="Bar"Margin="5" /><Button Content="Baz"Margin="5" /></StackPanel></Border></controls:ScrollHeader></ListView.Header><ListView.ItemTemplate><DataTemplate x:DataType="local:Person"><controls:SlidableListItem LeftIcon="Accept"RightIcon="AddFriend"LeftCommand="{Binding LeftCommand, ElementName=page}"LeftCommandParameter="{Binding}"RightCommand="{Binding RightCommand, ElementName=page}"RightCommandParameter="{Binding}"><TextBlock Text="{x:Bind Name}" /></controls:SlidableListItem></DataTemplate></ListView.ItemTemplate><ListView.ItemContainerStyle><Style TargetType="ListViewItem"><Setter Property="HorizontalContentAlignment"Value="Stretch" /><Setter Property="VerticalContentAlignment"Value="Stretch" /></Style></ListView.ItemContainerStyle></controls:PullToRefreshListView></Grid></Page>

実行すると、以下のようにヘッダーが表示されて、スクロールすると消えるけど、逆方向にスクロールすると、すぐにょきっと生えてくるようになります。

f:id:okazuki:20170417114323p:plain

f:id:okazuki:20170417114358p:plain

f:id:okazuki:20170417114428p:plain

まとめ

ということで、UWP Community ToolkitでListViewまわりの機能を紹介しました。これ以外にも色々なコントロールや機能があるので、興味のある方は見てみるといいかも?より良いUIが簡単に手に入るかもしれませんしね!

github.com

Viewing all 1388 articles
Browse latest View live


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