Windows ストアアプリ 8/8.1 開発者に捧げるUWP化のススメ


## はじめに
Windows10が物凄い勢いでpushされているなか、ついに私のところにもUWP化しなければいけない流れが来てしまいました。
折角なので8/8.1から移行する中で気付いたことを残しておきます。

## 対象者
Windows ストアアプリ 8/8.1 の開発を行っていたがUWPはまだ触ったことが無い人。
Windows10でも8/8.1向けアプリ動くんだしいーじゃん、的な人。

<!---
## 参考にしたところ
* [参考にしたサイトはここに書く](http://www.google.co.jp/)
--->

## 基本的には何もしなくていい
UWP化するにあたって、色々な機能追加はありますが、大きく変更された点はありません。
プロジェクトをUWP向けに変更するだけで動きます。
UWP向けプロジェクトから8/8.1向けのDLLなども利用することができます。


## 環境に関するもの
### NuGet v3 のproject.jsonが使えるようになった
8/8.1向けのプロジェクトではpackages.confingを利用していましたが、UWPではproject.jsonが利用できるようになりました。
ちなみに8/8.1向けのPCLではproject.jsonを利用することができます。

### System.Threading.Tasks.Dataflowが最初から組み込まれている
今まで利用していなかった人には関係ありませんが、Microsoft.Tpl.Dataflowを利用していた人は注意が必要です。
どちらもSystem.Threading.Tasks.Dataflow.dllを使用しており、Microsoft.Tpl.Dataflowを利用している場合、UWPからは参照できません。
バージョン番号を見る限り、置き換えられたものと思われるので、System.Threading.Tasks.Dataflowを利用するといいでしょう。

### シミュレーターでWindowの大きさがおかしくならない
WIndows10環境で開発する場合、Windows8.1向けアプリをシミュレーターで動作させるとWindowの大きさが正しくなりません。
1366 X 7680 (16:9, 100%)の解像度で全画面にした場合、アプリ内で取得できる大きさは1645.783.. X 925.301..になります。
UWPの場合、意図した通りの大きさが取得できます。

ちなみに、シミュレーターでなくてもWindows10環境で8.1向けアプリを動かした場合、スケールが100%であれば正しい大きさが取得できますが、スケールが100%より大きい場合はシミュレーターと同じように大きさが正しくなりません。
正確に検証したわけではないですが、スケーリングが動作していないような印象。
8インチタブレットや高解像度のタブレットなんかは該当する可能性があり、それらの端末で動かすと意図したものより一回り小さく表示されるようになってしまいます。

## デザインに関係するもの
### 既定で定義されているBrushの数が減った
8/8.1向けアプリではコントロール単位でBrushが設定されていたが、UWPでは複数のコントロールで同じBrushが設定されています。
そのため規定のBrushを変更せずそのまま利用している場合は見た目が異なります。
8.1で定義されていたBrushは規定では設定されていませんが、定義としては残っているのでそのまま利用することもできます。

* 8.1
    + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt\xaml\design\generic.xaml
* UWP(10240)
    + C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10240.0\Generic\generic.xaml
* UWP(10586)
    + C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10586.0\Generic\generic.xaml

### コントロールが増えた
TODO:各コントロールを掘り下げる
* [AutoSuggestBox](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt185404.aspx)
* [CalendarDatePicker](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299110.aspx)
* [CalendarViewDayItem](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299112.aspx)
* [CalendarView](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299113.aspx)
* [CommandBarOverflowPresenter](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299117.aspx)
* [ContentDialog](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299120.aspx)
* [DatePickerFlyoutPresenter](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299121.aspx)
* ListPickerFlyoutPresenter
* LoopingSelectorItem
* LoopingSelector
* [MediaTransportControls](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt414180.aspx)
* [MenuFlyoutSubItem](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299141.aspx)
* PickerFlyoutPresenter
* [PivotHeaderItem](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299142.aspx)
* [PivotItem](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299143.aspx)
* [Pivot](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt299144.aspx)
* [SplitView](https://msdn.microsoft.com/ja-jp/library/windows/apps/mt590204.aspx)
* TimePickerFlyoutPresenter

## 予期せぬ動作をするもの
### シフトJISのEncodingを利用するためにひと手間かかる
これを呼び出すことで利用できるようになります。
一度設定すれば以降どこでも利用できるようになるので、アプリの初期化処理に書いておくといいでしょう。

```csharp
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance)
```

### コードからResourceを参照すると例外が発生する
SolidColorBrush.ColorにResourceを設定した状態で、コードからResourcesプロパティを参照するとCOMExceptionが発生することがあります。
どうもColorを参照するBrushが2つ以上あると発生する模様。
StaticResource/ThemeResourceどちらでも発生します。
xamlで参照する分には例外は発生しません。
BrushだけでなくButtonでも発生したので、他の要素でも発生するものと思われます。
[再現コード。](https://bitbucket.org/Shigezo32/uwpverification/src/237eedb3d9406dd34da5363bdb21fa723069c523/ResourceError/?at=master)

```xml:xaml
<Color x:Key="ColorA">#333333</Color>
<SolidColorBrush x:Key="BrushA" Color="{ThemeResource ColorA}" />
<Color x:Key="ColorB">White</Color>
<SolidColorBrush x:Key="BrushB" Color="{ThemeResource ColorB}" />
<Style x:Key="ButtonStyleA" TargetType="Button">
</Style>
<Style x:Key="ButtonStyleB" TargetType="Button" BasedOn="{StaticResource ButtonStyleA}">
</Style>
```

```csharp:cs
//MergedDictionariesでリソースファイルを分割している場合
Application.Current.Resources.MergedDictionaries.SelectMany(x => x).ToList();
//自身のResourcesに含めている場合
Application.Current.Resources.ToList();
```

どうもCOMオブジェクトに対する列挙を行うことで発生する感じでしょうか。
ResourceDictionaryはCOMオブジェクトらしいです。
コードからxamlで定義したResourceを取得する場合、以下のような感じで取得できました。

```csharp:OK
if (Application.Current.Resources.ContainsKey(key) && Application.Current.Resources[key] is T)
{
    return Application.Current.Resources[key] as T;
}
foreach (var item in Application.Current.Resources.MergedDictionaries)
{
    if (item.ContainsKey(key) && item[key] is T)
    {
        return item[key] as T;
    }
}
return null;
```

```csharp:NG
return Application.Current.Resources.MergedDictionaries.SelectMany(x => x)
    .Union(Application.Current.Resources)
    .FirstOrDefault(x => x.Key.Equals(key))
    .Value as T;
```

## 要検証
検証が必要なもの。

### Resourceから別ファイルのResourceを参照する際の解決が厳しくなった
UWPだとMergedDictionariesで参照するResourceDictionaryを追加する必要があります。
TODO:サンプルコードを書いて要検証
NoneSelectionListViewStyle, NoneMarginListViewItemStyle