「そのほかにも」という箇所から簡単にですが、ReactivePropertyで追加しているIObservableの拡張メソッドで、紹介していないものを書いてみました。
ReactivePropertyオーバービューを更新しました。
Prism.WpfのApp.configによるModuleCatalogの構成の仕方をPrism自習用リポジトリに追加しました
Prism Template Packを使うと簡単にApp.configによるModuleCatalogの構成ができるということで、今まで書いてなかったそこの部分についてPrism自習用リポジトリに追記しました。
Xamarin版PrismのDependencyServiceサポート機能
Prism.Formsは、基本的にUnity(DIコンテナのほう)を使ってます。 こいつを使うとインスタンスの組み立てとかをお任せ出来るので楽ちんなのです!
Prism.Formsでは、そんなUnityの機能を拡張して、DependencyServiceから取得するインスタンスを自動でインジェクションしてくれる機能を提供しています。
従来
普通はDependencyServiceを使うときは以下のような手順になります。
インターフェースの作成
まず、PCLの所にインターフェースを定義します。ここではプラットフォームの名前を返すインターフェースみたいなのを作りました。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace PrismUnityApp20.Models { publicinterface IPlatformNameProvider { string GetName(); } }
各プラットフォームでの実装の作成
そして、次に各プラットフォームで実装を行います。 例えばAndroidだと以下のような感じですね。Dependency属性つけてるのがポイント。
using PrismUnityApp20.Models; using Xamarin.Forms; [assembly: Dependency(typeof(PrismUnityApp20.Droid.Models.AndroidPlatformNameProvider))] namespace PrismUnityApp20.Droid.Models { publicclass AndroidPlatformNameProvider : IPlatformNameProvider { publicstring GetName() => "Android"; } }
UWPだと以下のような感じ。
using PrismUnityApp20.Models; using Xamarin.Forms; [assembly: Dependency(typeof(PrismUnityApp20.UWP.Models.UWPPlatformNameProvider))] namespace PrismUnityApp20.UWP.Models { publicclass UWPPlatformNameProvider : IPlatformNameProvider { publicstring GetName() => "UWP"; } }
PCLでインスタンスを取得する
DependencyServiceのGetメソッドを使ってインスタンスを取得します。
var x = DependencyService.Get<IPlatformNameProvider>();
var name = x.GetName(); // メソッドを呼ぶと各プラットフォームの実装が呼ばれる
Prism.Formsの場合
最後のDependencyServiceのGetメソッドを呼ばなくてもよくなります。具体的には、使いたいクラスのコンストラクタの引数でインターフェースを受け取るようにするだけで自動的に各プラットフォーム固有の実装がインジェクションされます。
private IPlatformNameProvider PlatformNameProvider { get; } public MainPageViewModel(INavigationService navigationService, IPlatformNameProvider pnp) { // インスタンスをとっておいてよしなに使うthis.PlatformNameProvider = pnp; }
こうすることのメリットは、なんといってもユニットテストが簡単になるという点ですね! テスト時には、Mock実装を簡単に差し込めるようになります。すばらし。
あと、個人的にGetメソッドかっこわるいと思ってたので、この機能は歓迎です。
JXUGC 13回で発表してきました #JXUG
Xamarin版PrismでViewとViewModelの紐づけルールを変える
PrismはデフォルトでViews名前空間にあるViewとViewModels名前空間にあるViewModelを紐づける機能を持っています。この機能を有効化するには、PageのXAMLに以下の2行を追加します。
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" prism:ViewModelLocator.AutowireViewModel="True"
ということで、Viewに対して属性でViewModelの型を指定するようにしてみたいと思います。これでViewに対して任意のViewModelを紐づけることができます。
まず、ViewModelを指定するための属性を定義します。
publicclass ViewModelAttribute : Attribute { public Type ViewModelType { get; } public ViewModelAttribute(Type viewModelType) { this.ViewModelType = viewModelType; } }
そして、AppクラスのConfigureViewModelLocatorをオーバーライドして以下のようなに記述します。
protectedoverridevoid ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(viewType => { var vm = viewType.GetTypeInfo().GetCustomAttribute<ViewModelAttribute>(); return vm?.ViewModelType; }); }
ViewとViewModelの紐づけルールはViewModelLocationProviderのSetDefaultViewTypeToViewModelTypeResolverでラムダ式で指定します。ここでは、属性をとってきて、そのViewModelTypeで指定された型を返しています。
あとは、View側に以下のような感じでViewModelの型を指定すればOKです。
[ViewModel(typeof(MainPageViewModel))] publicpartialclass MainPage : ContentPage { public MainPage() { InitializeComponent(); } }
ReactivePropertyで自動でUIスレッドにイベント発行を変えるのを抑止する
普通はいいんですが、UWPとかでマルチウィンドウとか、共有コントラクトとか使うとUIスレッドが複数生成されて、UIスレッドが1つという前提に立ってるReactivePropertyだと、ちょっと嫌な感じになってしまいます。
そんなときは、Appクラスの初期化処理で以下の1行を追加すると、現在のスレッドでイベントが発行されるようになります。
// 余計なスレッド切り替えは自動でしないで
ReactivePropertyScheduler.SetDefault(CurrentThreadScheduler.Instance);
あとは、ViewModelあたりで明示的にSynchronizationContext.Currentを使ってObserveOnをしてやればUIスレッドに処理を移動させたり、もしくは、明示的にSynchronizationContextSchedulerを使ってReactiveProperty関係のクラスを作ればいいです。(明示的にSchedulerを指定する場合は、別にReactivePropertyScheduler.SetDefault
しなくても大丈夫。
// 現在のVMが今のUIスレッドでインスタンス化されること前提this.Time = new ReactiveProperty<string>(new SynchronizationContextScheduler(SynchronizationContext.Current));
ReactivePropertyで自動でUIスレッドにイベント発行を変えるのを抑止する その2
上記記事では、ViewModelの中でSynchronizationContextScheduler
を作成していましたが、これだと単体テストが辛いということになります。これをいい感じにするには、ちょっと工夫がいるのですが…。例えばPrism.Unityを使うと以下のようにViewModelのコンストラクタの引数に渡すものを上書きすることができます。Unityの機能ですね。
具体的には、App
クラスでConfigureViewModelLocator
メソッドをoverrideして、ViewModelLocationProvider.SetDefaultViewModelFactory
の処理を置き換えることで対応可能です。
protectedoverridevoid ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); ViewModelLocationProvider.SetDefaultViewModelFactory(x => this.Container.Resolve(x, new ParameterOverride("scheduler", new SynchronizationContextScheduler(SynchronizationContext.Current)))); }
こうしておくと、ViewModelでは、以下のような引数を定義しておくだけでUIスレッドに紐づいたSchedulerが設定されてきます。
public MainPageViewModel(IScheduler scheduler) { this.Time = new ReactiveProperty<string>(scheduler);
こうしておくと、単体テストでIScheduler
をTestSchedulerに差し替えたりということが簡単にできるようになります。
C#で好きな機能
Blogネタが尽きたので小ネタを。
C#でプログラムする時によく使う便利機能を3つ挙げたいと思います。
async/await
やはりC#といったらこれですよね。 非同期処理を簡単に書ける。
これがない言語でのプログラミングは、結構ストレスです。コールバック地獄になって。
文字列挿入
$使うやつですね。xxxFormat系メソッドを全て殺してしまうほどのインパクトがある。 これも地味によく使います。
null条件演算子
これのおかげでnullチェックをする機会がほとんどなくなりました。
地味にイベントを呼ぶときとかのnullチェックを省ける点や、LINQのFirstOrDefaultの後に続けて値を取得するときとかが楽でいいです。
ちなみにLINQは入らないの?
LINQは空気(なくてはならない)なので、わざわざ取り上げる機能ではない。
ReactivePropertyのコードスニペット
ReactivePropertyをインストールすると、地味にコードスニペットもNuGetパッケージ内に入ってます。ただインストールはされないので自分でインストールが必要です。
コードスニペットマネージャーからpackages/ReactiveProperty.2.x.x/Snippet/csharp6
フォルダにある以下のファイルをインストールします。csharp5フォルダは、C#5.0向けです。
- ReactiveCollection.snippet
- ReactiveCommand.snippet
- ReactiveCommandGeneric.snippet
- ReactiveProperty.snippet
- ReadOnlyReactiveCollection.snnipet
- ReadOnlyReactiveProperty.snnipet
rpropでReactivePropertyの宣言が展開されます。rcommでReactiveCommand、rcommgでReactiveCommandのパラメータつき、rcollでReactiveCollection、rrcollでReadOnlyReactiveCollection、rrpropでReadOnlyReactivePropertyに対応しています。
例えばrpropと打ち込んでtab tabをすると以下のようなコードが展開されます。
public ReactiveProperty<T> PropertyName { get; }
まぁ、手で書いても大したことないコードですが、慣れると快適ではあります。
コードスニペットでちょっと快適になりましょう!
LINQのメソッド構文、クエリ式の構文
LINQってメソッド構文とクエリ式の構文の2つがあります。どっち使います?という話ですが個人的な見解として、メソッド構文がLINQの全機能にフルアクセスできるという点でメソッド構文を使用しています。 LINQのメソッド構文とクエリ式の構文の対応は以下のページにまとまっています。
Query Expression Syntax for Standard Query Operators
ちなみにクエリ式の構文のメリットとしては多段SelectManyが見やすいという点があります。
var a = from h in hoge from f in fuga from b in bar select new { h, f, b };
メソッド構文だとこういう感じになる。
var a = hoge.SelectMany(_ => fuga, (h, f) => new { h, f }) .SelectMany(_ => bar, (hf, b) => new { hf.h, hf.f, b });
これの数がちょっと増えてくるとSelectManyのメソッド構文のほうがつらくなってきます。といっても、SelectManyを、超たくさん書くことって限られてるので、そんなに困ることはないです。
ということで、メソッド構文推しですよ!という話しでした。
Xamarin.FormsでInfragistics製コンポーネントのチャートコントロールを使ってみる
InfragisticsさんはXamarin.Forms向けのコントロールも提供しています。今時点だとチャート系コントロールを提供しているみたいなのでちょっと使ってみましょう。
Xamarin.Forms v2.0対応っぽいのでバージョンを2.2とかにあげてしまわないように注意が必要そうです。さて、まず、Portableのプロジェクトで以下のアセンブリを参照します。
- InfragisticsXF
- InfragisticsXF.Controls.Charts
- InfragisticsXF.Controls.Barcodes
- InfragisticsXF.Controls.Gauges
次に、Droidのプロジェクトで以下のアセンブリを追加します。
- InfragisticsAndroidBindings.dll
- InfragisticsXF.Android.dll
- InfragisticsXF.Controls.Barcodes.Android.dll
- InfragisticsXF.Controls.Charts.Android.dll
- InfragisticsXF.Controls.Gauges.Android.dll
最後にiOSプロジェクトに以下のアセンブリを追加します。
- InfragisticsXF.iOS.dll
- InfragisticsXF.Controls.Barcodes.iOS.dll
- InfragisticsXF.Controls.Charts.iOS.dll
- InfragisticsXF.Controls.Gauges.iOS.dll
- IG.Unified.dll
- IGChart.Unified.dll
追加したらあとはデータを準備してバインドするだけです。今回はとりあえずこんな感じのPersonクラスと、それを性別ごとに集計したPiChartModelクラスを準備してます。
using Prism.Mvvm; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace App34.Models { publicclass ChartApp { publicstatic ChartApp Default { get; } = new ChartApp(); public ObservableCollection<Person> People { get; } public IEnumerable<PiChartModel> PiChartModels => this.People .GroupBy(x => x.Sex) .Select(x => new PiChartModel { Label = x.Key == 0 ? "男" : "女", Value = x.Count(), }); public ChartApp() { var r = new Random(); this.People = new ObservableCollection<Person>(Enumerable.Range(1, 100) .Select(x => new Person { Name = $"tanaka{x}", Age = r.Next(50), Sex = r.Next(2), })); } } publicclass Person : BindableBase { privateint age; publicint Age { get { returnthis.age; } set { this.SetProperty(refthis.age, value); } } privatestring name; publicstring Name { get { returnthis.name; } set { this.SetProperty(refthis.name, value); } } privateint sex; publicint Sex { get { returnthis.sex; } set { this.SetProperty(refthis.sex, value); } } } publicclass PiChartModel : BindableBase { privatestring label; publicstring Label { get { returnthis.label; } set { this.SetProperty(refthis.label, value); } } privateintvalue; publicint Value { get { returnthis.value; } set { this.SetProperty(refthis.value, value); } } } }
ViewModelは、手抜きで、これらのデータを右から左にするだけです。すいません。
using App34.Models; using Prism.Mvvm; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace App34.ViewModels { publicclass MainPageViewModel : BindableBase { public ObservableCollection<Person> People => ChartApp.Default.People; public IEnumerable<PiChartModel> PiChartModels => ChartApp.Default.PiChartModels; } }
あとは、Infragisticsのコントロールにバインドするだけです。
<?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:ig="clr-namespace:Infragistics.XF.Controls;assembly=InfragisticsXF.Controls.Charts"xmlns:ViewModels="clr-namespace:App34.ViewModels;assembly=App34"x:Class="App34.Views.MainPage"><ContentPage.BindingContext><ViewModels:MainPageViewModel /></ContentPage.BindingContext><Grid><Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions><ig:XFDataChart><ig:XFDataChart.Axes><ig:CategoryXAxis x:Name="xAxis"ItemsSource="{Binding People}"Label="Name"LabelAngle="45"/><ig:NumericYAxis x:Name="yAxis" /></ig:XFDataChart.Axes><ig:XFDataChart.Series><ig:AreaSeries ItemsSource="{Binding People}"ValueMemberPath="Age"XAxis="{x:Reference xAxis}"YAxis="{x:Reference yAxis}"></ig:AreaSeries></ig:XFDataChart.Series></ig:XFDataChart><ig:XFPieChart Grid.Row="1"ItemsSource="{Binding PiChartModels}"LabelMemberPath="Label"ValueMemberPath="Value"VerticalOptions="Center"/></Grid></ContentPage>
XFDataChartが今回折れ線グラフを表示していて、x軸に名前y軸に年齢を表示しています。XSPieChartが円グラフで性別の分布を表示しています。実行するとこんな感じになります。
意外と簡単にチャートが出せますね。
UWPのItemsStackPanelで画面外に生成される項目の要素数を制御する
UWPのListViewって仮想化されますよね?
そのとき画面から見えてない領域にどれくらいの要素を生成されるのかという話しですが、これはCacheLength
プロパティで制御できます。
デフォルトは4で、このとき上に表示領域の4個ぶん、下に表示領域の4個ぶん、表示領域も含めて合計9個ぶんの項目が生成されることになります。
ItemsStackPanel.CacheLength property - Windows app development
これを小さくすることで、起動時間とかを短縮できるらしいです。大きくすると、スクロールのパフォーマンスが向上するらしいです。色々試して微調整が必要そうですね。因みに設定の仕方は以下のような感じです。
<ListView ...> ... <ListView.ItemsPanel><ItemsPanelTemplate><ItemsStackPanel CacheLength="2"/></ItemsPanelTemplate></ListView.ItemsPanel> ... </ListView>
UWPでgeneric.xamlを開く方法
基本的な色とかが定義されてるgeneric.xamlですが、こいつをたまに見たいことがあります。 そんなときどうするか?
プロジェクトを新規作成してMainPage.xaml
のApplicationPageBackgroundThemeBrush
でF12を押します。
そうするとgeneric.xamlをさくっと開けます。あとは見たい定義を見るだけ。
UWPでWindowのアクティブ状態を取りたい
ほしいときにぱっととる事はできなさそうです。
ということで、Window.Current.CoreWindowのActivatedイベントでとっておく必要があります。
こんな感じで
// どこかでイベントを購読して Window.Current.CoreWindow.Activated += CoreWindow_Activated; // こんな感じで状態をとっておくprivate CoreWindowActivationState State { get; set; } privatevoid CoreWindow_Activated(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.WindowActivatedEventArgs args) { // アクティブかどうかの状態をフィールドあたりにとっておくthis.State = args.WindowActivationState; }
CoreWindowActivationState列挙体は、以下の値をもってます。
CoreWindowActivationState Enumeration (Windows)
- CodeActivated: Activateによりアクティブ化された
- Deactivated: 非アクティブ
- PointerActivated: マウスやタッチでアクティブ化された
この値を見ることで、アクティブかどうかが判定できます。アクティブかどうかで見た目を変えるとかできそうですね。
HoloLensおさわり会の紹介
先日もBlogに書いた、Slikyfeelさんの開催しているHoloLensおさわり会ですが、日本各地で開催されてます。
直近は、今夜申し込み締め切りの岡山で開催のやつです。
そのほかに、名古屋、博多、愛媛・松山など、結構いろんなところで触れます。
HoloLensの感想
HoloLensですが、当たり前にホログラフィックが場所に固定されて出てくるのが、とてもすごいです。 ナチュラル過ぎて、凄さを忘れてしまうくらい凄いです。
例えば、部屋の壁にWindowを1つ貼り付けて置いておいたとします。 そして、色んなところに色んなオブジェクトを設置したり部屋をうろうろしたりして戻ってきても、ちゃんとそこにブレずにWindowが貼り付けられてる。 当たり前ですけど、凄いですよね?個人的にとても感動しました。
おさわり会ですが、私が参加した会と同じ感じなら、後半になればなるほど、色んな人が色んなものを置いてカオスになって楽しくなってきます。 行ける距離の範囲内で開催されてそうなら、是非一度参加してみて見るといいと思います。
未来をかぶれます。
Entity Framework Core RC2がリリースされたみたいです
待ちに待ったリリースに向けてあと一歩といったところですね!
UWPで使うときに必要だったおまじないも消えてるっぽい(未確認だけどドキュメントに記載されてない)ので気軽にUWPで使えるようになったっぽいです。UWPでSQLiteを使うときの選択肢として期待大ですね。
うまくいかない…DataFactoryを使ってSQL Databaseの中身をData Lake Storeにコピーする
DataFactoryでコピーするだけなのにうまくいかない
何が足りてないんだろう要調査。誰か教えて。
やったこと
まず、East US2(DataLakeの置き場の都合)にData Lake StoreとSQL Databaseを作成します。
SQL Database側に以下のようなテーブルを定義しておきます。
CREATETABLE [dbo].[People] ( [Id] INT NOTNULL PRIMARY KEY , [Name] nvarchar(256) NOTNULL )
そして、以下のようなデータを適当につっこんでおきます。
Azure DataFactoryのVisual Studioプラグインを入れてVSからいじれるようにしておきます。
visualstudiogallery.msdn.microsoft.com
そうすると、プロジェクトの新規作成にDataFactoryが追加されるので、その中のEmpty Data Factory Projectを選択して作成します。
LinkedServicesで追加の新規作成で先ほど作ったSQL Databaseへのリンクを作りましょう。「Azure SQL Linked Service」がそれっぽいです。
JSONが作られるのでサーバー名とかを先ほど作ったサーバーの名前に置き換えてJSONを完成させます。
{ "$schema": "http://datafactories.schema.management.azure.com/schemas/2015-09-01/Microsoft.DataFactory.LinkedService.json", "name": "AzureSqlLinkedService1", "properties": { "type": "AzureSqlDatabase", "typeProperties": { "connectionString": "Data Source=tcp:dfsampleserver.database.windows.net,1433;Initial Catalog=dfsample;User ID=okazuki@dfsampleserver;Password=*******;Integrated Security=False;Encrypt=True;Connect Timeout=30" } } }
続けて保存先のData Lake Store(こちらもあらかじめ作っておきましょう)のLinkedServiceを作っておきます。「Azure Data Lake Store linked Service」がそれっぽいです。 こちらは、SQL Databaseのような硬派はJSONいじれよという感じじゃなくて、既存のData Lakeから選択させるようなUIが出ます。全部こうならいいのに。
Nextを押すとData Factory Configurationに進むので、なんか作っておきましょう。
...
Visual Studioが異常終了しました。
ポータルでやろう
Visual Studioが何度やっても異常終了するのでポータルに戦場をうつします。
DataFactoryを新規作成します。名前はokazukidfにしました。リージョンはNorth Europeです。 okazukidfを選択して「Author and deploy」を選択します。
New data storeを選択して生成されるJSONのservernameとかを埋めます。
{ "name": "AzureSqlLinkedService", "properties": { "type": "AzureSqlDatabase", "description": "", "typeProperties": { "connectionString": "Data Source=tcp:dfsampleserver.database.windows.net,1433;Initial Catalog=dfsample;User ID=okazuki@dfsampleserver;Password=********;Integrated Security=False;Encrypt=True;Connect Timeout=30" } } }
入力したらDeployを押します。
Data Lake Storeも追加して、画面上部にあるAuthrizeボタンを押して認証をさせます。
dataLakeStoreUriにadl://ではじまるURLをつっこんでDeployします。
{ "name": "AzureDataLakeStoreLinkedService", "properties": { "type": "AzureDataLakeStore", "description": "", "typeProperties": { "authorization": "********", "dataLakeStoreUri": "adl://********.azuredatalakestore.net", "sessionId": "********" } } }
次にNew datasetを選らんでDatasetを作ります。Azure SQLを選んで生成されたJSONをもとにぽちぽち頑張ります。
{ "name": "AzureSQLDatasetTemplate", "properties": { "type": "AzureSqlTable", "linkedServiceName": "AzureSqlLinkedService", "structure": [], "typeProperties": { "tableName": "People" }, "availability": { "frequency": "Minute", "interval": 15 } } }
linkedServiceNameに、先ほど作ったSQL DatabaseへのLinkedServiceの名前を入れるのがぽいんとです。
DataLake Storeも同じ要領で作ります。ここら辺から心が折れてきます。
{ "name": "AzureDataLakeStoreDatasetTemplate", "properties": { "type": "AzureDataLakeStore", "linkedServiceName": "AzureDataLakeStoreLinkedService", "structure": [ { "name": "ID,Name", "type": "int,string" } ], "typeProperties": { "folderPath": "/data/dfsample", "fileName": "People.csv", "format": { "type": "TextFormat" }, "partitionedBy": [ { "name": "Year", "value": { "type": "DateTime", "date": "SliceStart", "format": "yyyy" } }, { "name": "Month", "value": { "type": "DateTime", "date": "SliceStart", "format": "MM" } }, { "name": "Day", "value": { "type": "DateTime", "date": "SliceStart", "format": "dd" } }, { "name": "Hour", "value": { "type": "DateTime", "date": "SliceStart", "format": "HH" } } ] }, "availability": { "frequency": "Minute", "interval": 15 } } }
心が折れるにはまだ早い、New pipelineをクリックしていきます。startとendに適当な日付を入れたらAdd activityを選択しましょう。 Copy activityを選択します
適当にJSONを埋めます(心が折れた)
{ "name": "PipelineTemplate", "properties": { "description": "description", "activities": [ { "name": "CopyActivityTemplate", "type": "Copy", "inputs": [ { "name": "AzureSQLDatasetTemplate" } ], "outputs": [ { "name": "AzureDataLakeStoreDatasetTemplate" } ], "typeProperties": { "source": { "type": "SqlSource", "sqlReaderQuery": "select * from People" }, "sink": { "type": "BlobSink" } }, "policy": { "concurrency": 1, "executionPriorityOrder": "OldestFirst", "retry": 3, "timeout": "01:00:00" }, "scheduler": { "frequency": "Minute", "interval": 15 } } ], "start": "2014-05-01T00:00:00Z", "end": "2019-05-05T00:00:00Z" } }
これで放置しておいても、一向にデータがコピーされる気配がない。何が悪いんだろうか。
DIコンテナのUnityで使用されるコンストラクタを指定する
InjectionConstructorAttribute
を使用します。指定しない場合はコンストラクタ引数の一番多いものが使われます。
例えば以下のようなケース。
publicclass Hoge { public Hoge(Foo foo, Bar bar) { } public Hoge(Foo foo) { } }
だと、Foo foo, Bar bar
のほうのコンストラクタが使われます。これをFoo foo
のほうにしたかったら以下のようにします。
publicclass Hoge { public Hoge(Foo foo, Bar bar) { } [InjectionConstructor] public Hoge(Foo foo) { } }
ということで、良いUnityライフを。
UWPで一定間隔で表示が切り替わるPivotを作ろう
小ネタです。ViewModelあたりにSelectedIndexというプロパティをはやして、これを一定時間でインクリメントするようにします。 UIスレッド上でやるのがポイントですね。DispatcherTimerかRx使うのがいいでしょう。(RxのInterval使う場合はObserveOnでUIスレッドに持ってくる)
using Prism.Mvvm; using System; using System.Reactive.Linq; using System.Threading; namespace App37 { publicclass MainPageViewModel : BindableBase { privateint selectedIndex; publicint SelectedIndex { get { returnthis.selectedIndex; } set { this.SetProperty(refthis.selectedIndex, value); } } public MainPageViewModel() { Observable.Interval(TimeSpan.FromSeconds(5)) .ObserveOn(SynchronizationContext.Current) .Subscribe(_ => this.ChangeSelectedIndex()); } publicvoid ChangeSelectedIndex() { var current = this.SelectedIndex; current++; current = current >= 3 ? 0 : current; this.SelectedIndex = current; } } }
今回はPivotが3画面あることを想定しているコードになります。XAML側は適当にPivotを用意して、PivotのSelectedIndexプロパティとTwoWayバインディングしてやります。
<Page x:Class="App37.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App37" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.DataContext> <local:MainPageViewModel x:Name="ViewModel" /> </Page.DataContext> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Pivot SelectedIndex="{x:Bind ViewModel.SelectedIndex, Mode=TwoWay}"> <PivotItem Header="Item1"> <TextBlock Text="Item1" /> </PivotItem> <PivotItem Header="Item2"> <TextBlock Text="Item2" /> </PivotItem> <PivotItem Header="Item3"> <TextBlock Text="Item3" /> </PivotItem> </Pivot> </Grid> </Page>
これで5秒間隔で表示の切り替わるPivotの完成です。
UWPでスクリーンショットを取らせないようにする
今日のde:codeのセッションで知ったことをメモしておきます。
UWPでは、以下のプロパティをfalseに設定することでスクリーンショットを取られることを抑止できます。 画面キャプチャされて、機密情報漏えいなくなりますね!
// これを入れる ApplicationView.GetForCurrentView().IsScreenCaptureEnabled = false;
SnippingToolでキャプチャすると以下のようになりました。(黒い部分が画面)
謎なところ
ボタンクリックに上記コードを書くとうまくいくのですが、PageのLoadedとかに書くとスプラッシュスクリーンから先に進まなくなります。この問題解決しないとちょっと使うの難しい…?