Xamarin.Formsで Windows (Classic) Desktop アプリ開発を今からしてみる

## はじめに

先日、ついに WPF 用の Platform 実装がマージされました。

[[WPF] Xamarin Forms WPF Backend - Platform Project #1334](https://github.com/xamarin/Xamarin.Forms/pull/1334)

また、 GTK の実装もすでにマージ済でした。

[Xamarin Forms Gtk# Backend - Platform Project #1174](https://github.com/xamarin/Xamarin.Forms/pull/1174)

もうすぐ普通に使えるようになるかなと思いますが、 GitHub からソースを取得すれば今すぐ Windows, Linux のデスクトップアプリを Xamarin.Forms で開発することができます (UWP って？) 。ということで早速やってみましょう。

今回の結果はこちら。環境は VS2017 15.5 。

https://github.com/aosoft/Xamarin.Forms/tree/master_gtk-wpf-test2

これをチェックアウトし、 "XF-GtkWpf/XF-GtkWpf.sln" を開いてビルドすればすぐに試せます。ちなみにこれは

https://github.com/aosoft/Xamarin.Forms/tree/master_gtk-wpf-test2/XF-GtkWpf

このフォルダ以下を Xamarin.Forms に追加したもので、本体側には変更は加えていません (参照しているだけ) ので、ここ以下だけ持って行っていただいても OK です。

この記事の内容は GTK, WPF の Platform 実装が正式に搭載されたらあんまり意味なくなるとは思いますが、 "Xamarin.Forms の master を追っかけてアプリ開発をする" 場合の Tips みたいなものになるかなあと思います。多分。

### ※2018/1/13 追記

Nightly Builds を利用することで GitHub のソースをビルドしなくても試すことができました。詳細は別記事にまとめました。

[もう少し簡単に Xamarin.Forms で Windows (Classic) Desktop アプリ開発を今からしてみる](https://qiita.com/tan-y/items/e08d297c2d013214eb83)


## Xamarin.Forms の準備

まず Xamarin.Forms のリポジトリから master ブランチを取得します。

現在の master の Xamarin.Forms のビルドには .NET Standard 2.0 を Target SDK にしてビルドできるようにしておく必要があります。準備されていない場合は .NET Core 2.0 SDK をインストールしておいてください。

で、準備できたらとりあえず Xamarin.Forms.sln をビルドしてみましょう。そこそこ時間かかりますが、待てないほど時間がかかるわけではないと思います。 iOS などちゃんと環境がそろっていないとビルドしきれないものは除き、ほどほどあっさりビルドは通ると思います。 GTK, WPF は通っているはず。

Xamarin.Forms は "Xamarin.Forms.ControlGallery" となっているものがサンプル／テストアプリとなっています。 "Xamarin.Forms.ControlGallery.GTK" や "Xamarin.Forms.ControlGallery.WPF" を実行してみましょう。

![20171222_2.png](https://qiita-image-store.s3.amazonaws.com/0/211064/64cafa5e-ed29-2259-55cf-4f2dd6570dcb.png)

こんな感じで動作したと思います。

## アプリ開発をする

では実際に GTK, WPF をターゲットにアプリ開発をしてみます。

### アプリ作成用の .sln を準備する

今回は GTK, WPF をターゲットにしますが、 Xamarin.Forms.sln は Xamarin.Forms の公式プロジェクトが全部入っているため重量級 .sln になっており、ちょっと扱いに困ります。なので必要なプロジェクトのみ使うようにしましょう。

まず 空の .sln ファイルを作成し、下記のプロジェクトを追加してください。

* 必須なもの
    * Xamarin.Forms.Core 
    * Xamarin.Forms.Platform
    * Xamarin.Forms.Xaml
    * Xamarin.Forms.Xaml.Xamlg
    * Xamarin.Forms.Build.Tasks
* WPF を使うのに必要なもの
    * Xamarin.Forms.Platform.WPF
* GTK を使うのに必要なもの
    * Xamarin.Forms.Platform.GTK

このうち "Xamarin.Forms.Build.Tasks" は一度ビルドが通ってバイナリが生成されていれば OK です。これは MSBuild で Xamarin.Forms 固有のビルドオプション (基本的に XAML 向け) に対応するためのものです。 "Xamarin.Forms.Xaml.Xamlg" も同じような理由ですが、 XAML のプリコンパイルをする場合は "Xamarin.Forms.Xaml.Xamlc" の方が必要になるのではないかと思います (未確認) 。

とりあえずここで一度ビルドしておきます (Xamarin.Forms.sln で一回ビルドまわしているなら不要) 。

### アプリケーションプロジェクトを作成する

通常の C# プロジェクトを作成します。

![2.png](https://qiita-image-store.s3.amazonaws.com/0/211064/243095fb-7dcb-cd39-318f-9dff2e72d20f.png)


WPF の場合は素直に "WPF アプリ" でよいですが、 GTK の場合は "コンソールアプリ" を選択し、作成後にプロジェクトのプロパティで "出力の種類" を "Windows アプリケーション" に変更するのがよいと思います。

GTK は Xamarin.Forms.Platform.GTK が .NET 4.7 になっていたのでアプリも .NET 4.7 にしてください。

次にプロジェクトとアセンブリの参照を追加します。

* WPF の場合
    * Xamarin.Forms のプロジェクト
        * Xamarin.Forms.Core 
        * Xamarin.Forms.Platform
        * Xamarin.Forms.Xaml
        * Xamarin.Forms.Platform.WPF
    * NuGet
        * WpfLightToolkit 1.0.1 (Xamarin.Forms.Platform.WPF で使っているものと同じバージョンで) https://www.nuget.org/packages/WpfLightToolkit/
* GTK の場合
    * Xamarin.Forms のプロジェクト
        * Xamarin.Forms.Core 
        * Xamarin.Forms.Platform
        * Xamarin.Forms.Xaml
        * Xamarin.Forms.Platform.GTK
    * アセンブリ
        * GTK# のアセンブリ (Xamarin.Forms.Platform.GTK\Libs\gtk-sharp\gtk-sharp-2.0 以下の .dll)

"Xamarin.Forms.Build.Tasks" "Xamarin.Forms.Xaml.Xamlg" はビルド作業に必要なもので、ここではアプリ用プロジェクトの設定になるのでこの二つは参照する必要はありません。

最後に .csproj に Xamarin.Forms 用の BuildTask を追加します。これやらないと Xamarin.Forms の XAML がビルド対象になりません。プロジェクトの右クリックメニュー "プロジェクトのアンロード" で .csproj が直接編集できるようになります。

![3.png](https://qiita-image-store.s3.amazonaws.com/0/211064/f0b82a7d-4132-6a34-b925-0cf21079edce.png)


アンロードしたらプロジェクトファイルを開き、まず先頭の方に Xamarin.Forms.props をインポートする定義を入れます。

```xml
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\..\.nuspec\Xamarin.Forms.props" />
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
```

3 行目のものです。パスは適宜修正してください。もう一か所、 .csproj の最後の方に Xamarin.Forms.targets をインポートする定義を入れます。

```xml
  <Import Project="..\..\.nuspec\Xamarin.Forms.targets" />
</Project>
```

終わったら "プロジェクトの再読み込み" で完了です。

![4.png](https://qiita-image-store.s3.amazonaws.com/0/211064/b2cf2fc2-13e2-a8ce-81ae-01153ee4f8ed.png)

.csproj を直接編集すると IDE 上では実現できない設定も可能になりますので、本件とは関係なく .csproj の編集ができるようになっておくとよいかと思います。

### Xamarin.Forms.Application 継承クラスを実装する

Application クラスがないとスタートアップコードの実装を記述しきれないので、とりあえずは中身空でもよいので実装しておきます。

XF-GtkWpf では GTK と WPF で共通実装とするため Shared Library プロジェクトに実装しましたが、もちろん Platform 依存のアプリケーションプロジェクトに実装しても構いません。

### スタートアップコードを実装する

スタートアップの手続きは Platform 毎に異なるため、それぞれに応じて個別に実装しなくてはなりません。最初に試した "Xamarin.Forms.ControlGallery" が具体的なアプリケーション実装になりますので参考にしてください。

#### WPF の場合

* WPF の MainWindow クラスを Xamarin.Forms.Platform.WPF.FormsApplicationPage 継承に変更します。 XAML も修正必要なので注意。
* コンストラクタに初期化コードを追加します。
* WPF の Application クラスに必須リソースを定義します。 "AccentColor" はないといきなり落ちます。

```xml:MainWindow.xaml
<wpf:FormsApplicationPage
  x:Class="WpfApp.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:wpf="clr-namespace:Xamarin.Forms.Platform.WPF;assembly=Xamarin.Forms.Platform.WPF"
```

```csharp:MainWindow.xaml.cs
public partial class MainWindow : Xamarin.Forms.Platform.WPF.FormsApplicationPage
{
    public MainWindow()
    {
        InitializeComponent();

        Xamarin.Forms.Forms.Init();
        LoadApplication(new SharedApp.App());
    }
}
```

```xml:App.xaml
<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="/WPFLightToolkit;component/Assets/Default.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <SolidColorBrush x:Key="AccentColor" Color="#303F9F" />
  </ResourceDictionary>
</Application.Resources>
```

#### GTK の場合

Main メソッドに Gtk# の初期化と合わせて Xamarin.Forms の初期化コードを記述します。これは "Xamarin.Forms.ControlGallery.GTK" のものほぼそのままです。

```csharp:Program.cs
[STAThread]
static void Main(string[] args)
{
    ExceptionManager.UnhandledException += OnUnhandledException;

    GtkOpenGL.Init();
    GtkThemes.Init();
    Gtk.Application.Init();
    Xamarin.Forms.Forms.Init();
    var app = new SharedApp.App();
    var window = new FormsWindow();
    window.LoadApplication(app);
    window.SetApplicationTitle("Xamarin.Forms GTK# Backend");
    window.Show();
    Gtk.Application.Run();
}
private static void OnUnhandledException(UnhandledExceptionArgs args)
{
    System.Diagnostics.Debug.WriteLine($"Unhandled GTK# exception: {args.ExceptionObject}");
}
```

### アプリケーションを実装する

これ以降は他 Platform 向けの作業と変わりません。初期ページの UI を定義し、 Xamarin.Forms の Application クラスで MainPage に初期ページを設定します。

## 実行例

![20171222.png](https://qiita-image-store.s3.amazonaws.com/0/211064/b0c2429e-047e-19ae-b6da-5890181cb0e8.png)

https://twitter.com/TANY_FMPMD/status/943867032109379584 で貼ったものです。

せっかくなので私が開発している Unity, Windows Forms の Platform での実行例も合わせてみました。

この Page の XAML ですが、 Windows Forms 版実装の動作確認で書いていたものをそのまま持ってきたものです。 Shared Library じゃないですけど、まあ実体としては共有ですので・・・

## おわりに

GTK, WPF ときて Windows, Linux でもデスクトップアプリ開発に Xamarin.Forms が使えるようになってきました。

デスクトップ開発はしない、あるいは Xamarin.Forms は使えない、としても例えば Mobile とクロス開発をしてある程度のロジック実装をデスクトップで行うというのもありかなあと思います。 Mobile はなんだかんだでデバッグ面倒ですし・・・　UWP もあんまり・・・

ちなみに今回のネタ、最初は現行正式版である 2.5.0 の Core を参照元とした改造版を作ってましたが、結局そんなことする必要はありませんでした。せっかくなのでこちら。

https://github.com/aosoft/Xamarin.Forms/tree/master_gtk-wpf-test

### 追記

WPF アプリで参照必須アセンブリが抜けていたので追加しました。

アプリケーションの参照アセンブリは最低限必要なもののみ記載しましたが、状況によっては追加する必要があります。例えば OpenGLView を使うなら OpenTK が必要になると思います。

### 追記 2

.NET Standard 2.0 についての記載を追記しました。