ちょっと迷ったのでメモしておきます。
C+/WinRT自体についてはこちら
上のドキュメントの、このページの部分に関連してます。
Step 1: Windows Runtime Component プロジェクトの作成と idl の定義
Windows Runtime Component (C++/WinRT)
のプロジェクトを作成します。今回は名前は MyComponents にしました。
Class.idl
があるのですが、これは消します。
そして Person.idl
を作成するのと Person.h
と Person.cpp
も作成します。
Person.idl
に Person クラスを定義します。
namespace MyComponents { runtimeclass Person : Windows.UI.Xaml.Data.INotifyPropertyChanged { Person(); String Name; } }
インテリセンスとかないので辛いです。
Step 2: Person クラスの作成
idl を書いてビルドしてプロジェクトのすべてのファイルを表示すると Person.g.h
と Person.g.cpp
が生成されています。
ここには winrt::MyComponents::implementation::Person_base
クラスと、その別名の winrt::MyComponents::implementation::PersonT
と、winrt::MyComponents::factory_implementation::PersonT
が定義されています。この PersonT
という名前の 2 つのクラスを継承して Person
クラスを自分のコードに追加します。
Person.h
は以下のような感じになります。
#pragma once #include "Person.g.h" // 生成されたヘッダーを include namespace winrt::MyComponents::implementation { struct Person : PersonT<Person> { }; } namespace winrt::MyComponents::factory_implementation { struct Person : PersonT<Person, implementation::Person> { }; }
PersonT
とか Person
とか同じ名前が出てきますが、これは名前空間が違うので気を付けてみましょう。
Step 3: Person クラスの実装
Person クラスの中を作っていきます。idl ファイルに定義していた Name
プロパティと INotifyPropertyChanged
の実装をします。Person.h
に、ここらへんを追加していきましょう。
#pragma once #include "Person.g.h" // 生成されたヘッダーを include namespace winrt::MyComponents::implementation { struct Person : PersonT<Person> { Person() = default; winrt::hstring Name() const; void Name(winrt::hstring const& name); winrt::event_token PropertyChanged(winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler); void PropertyChanged(winrt::event_token const& token); private: winrt::hstring _name; winrt::event<winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler> _propertyChanged; }; } namespace winrt::MyComponents::factory_implementation { struct Person : PersonT<Person, implementation::Person> { }; }
Person.cpp
に実装を書いていきます。頭にお決まりの include を書いて namespace
を定義したらヘッダーに戻って実装したいメソッドで Ctrl + .
で以下のように、いい感じにひな型が作られます。
#include "pch.h" #include "Person.h" #include "Person.g.cpp" // 自動生成された cpp を include namespace winrt::MyComponents::implementation { winrt::hstring Person::Name() const { return winrt::hstring(); } void Person::Name(winrt::hstring const& name) { } winrt::event_token Person::PropertyChanged(winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return winrt::event_token(); } void Person::PropertyChanged(winrt::event_token const& token) { } }
中身を実装してこんな感じ。
#include "pch.h" #include "Person.h" #include "Person.g.cpp" // 自動生成された cpp を include namespace winrt::MyComponents::implementation { winrt::hstring Person::Name() const { return _name; } void Person::Name(winrt::hstring const& name) { _name = name; _propertyChanged(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs(L"Name")); } winrt::event_token Person::PropertyChanged(winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _propertyChanged.add(handler); } void Person::PropertyChanged(winrt::event_token const& token) { _propertyChanged.remove(token); } }
Step 4: C# から使う
C# の UWP アプリを作ります。ここでは CSharpUWPApp
にしました。
CSharpUWPApp
から MyComponents
に参照設定を追加します。ここらへんは普通の C# のクラスライブラリ使うときと一緒ですね。という`か、ちゃんと C++/WinRT で Windows ランタイムコンポーネントが定義出来ていれば C# から使うのは普通の C# のクラスを使うときと変わりありません。
ということで MainPage.xaml.csで
Person` クラスをプロパティに持たせて…
using MyComponents; using Windows.UI.Xaml.Controls; // The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 namespace CSharpUWPApp { /// <summary> /// An empty page that can be used on its own or navigated to within a Frame. /// </summary> public sealed partial class MainPage : Page { private Person Person { get; } = new Person { Name = "Tanaka" }; public MainPage() { this.InitializeComponent(); } } }
MainPage.xaml
で適当にバインドしましょう。
<Page x:Class="CSharpUWPApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:CSharpUWPApp" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <StackPanel> <TextBox Text="{x:Bind Person.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Text="{x:Bind Person.Name, Mode=OneWay}" /> </StackPanel> </Page>
実行するとちゃんと使えています。
まとめ
ハイパフォーマンスで美しい Windows Runtime Component を C++/WinRT で!!…作れるようになる道のりは自分にとってははるか長いように見える。 がんばろっと。
ソースコードはこちら。