ここまでさくさく実装し、コンパイル/実行すると…あれ?
Whatはともかくも、Whenの表示がスットコドッコイなことになっています。
この症状はWhenプロパティから手に入るDateTimeのメソッドToString()がこんな文字列を返すがために起こります。ちゃんと日付/時刻を描かせるにはDateTimeからString^への変換器(コンバータ)をデータとコントロールの間に差し込まにゃならんのです。
変換器はインターフェース:Windows::UI::Xaml::Data::IValueConverterを実装したクラスです。IValueConverterにはデータからコントロールに向かう変換:Convert()とコントロールからデータに向かう変換:ConvertBack()の2つのメソッドを定義します(このサンプルではConvertBack()は使われません)。
#pragma once
namespace CounterApp {
public ref class DateTimeToStringConverter sealed : Windows::UI::Xaml::Data::IValueConverter {
public:
// data to control
virtual Platform::Object^ Convert(
Platform::Object^ value,
Windows::UI::Xaml::Interop::TypeName targetType,
Platform::Object^ parameter,
Platform::String^ language);
// control to data
virtual Platform::Object^ ConvertBack(
Platform::Object^ value,
Windows::UI::Xaml::Interop::TypeName targetType,
Platform::Object^ parameter,
Platform::String^ language);
};
}
Convert/ConvertBackのいずれも、変換元(引数)/変換先(戻り値)の型はObject^です。なので参照型(ref class)相互の変換であれば問題ないのですが、今回の変換元であるDateTimeは値型(value struct)なので、Object^からBox<DateTime>^を介してDateTimeを取り出すことになります。
#include "pch.h"
#include "DateTimeToStringConverter.h"
using namespace Platform;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::Globalization::DateTimeFormatting;
using namespace Windows::UI::Xaml;
namespace CounterApp {
Object^ DateTimeToStringConverter::Convert(
Object^ value,
TypeName targetType,
Object^ parameter,
String^ language) {
DateTimeFormatter dfmt(L"longdate");
DateTimeFormatter tfmt(L"longtime");
auto boxedDateTime =
dynamic_cast<Box<Windows::Foundation::DateTime>^>(value);
return ( boxedDateTime != nullptr ) ?
dfmt.Format(boxedDateTime->Value) + L" " +
tfmt.Format(boxedDateTime->Value)
: DependencyProperty::UnsetValue;
/* この部分、Boxを介さずとも
auto dt = safe_cast<Windows::Foundation::DateTime>(value);
return dfmt.Format(dt) + L" " + tfmt.Format(dt);
でもいいみたい。試してみて! */
}
Object^ DateTimeToStringConverter::ConvertBack(
Object^ value,
TypeName targetType,
Object^ parameter,
String^ language) {
return DependencyProperty::UnsetValue;
}
}
変換器ができたので、こいつをデータとコントロールの間に割り込ますべく、XAMLに手を加えます。まず<Page.Resources>に追加:
<Page.Resources>
<x:String x:Key="AppName">はじめてのC++/CX</x:String>
<!-- DateTimeからStringへの変換器 -->
<local:DateTimeToStringConverter x:Key="D2S" />
</Page.Resources>
これで名前"D2S"のついたDateTimeToStringConverterが用意できました。んでもってListViewのItemTemplateには:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding What}" FontSize="40"/>
<!-- Whenにバインドすると共にDateTime→String変換を行う -->
<TextBlock Text="{Binding When,Converter={StaticResource D2S}}" FontSize="20"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
"Binding When"のうしろに",Converter={StaticResource D2S}"を挿入し、"Whenプロパティから得た値に変換器D2Sを適用したものをバインドせよ"と教えてあげます。今度こそ、ちゃんと表示されるはず。
最後に
Windows 8ストアアプリの多くはC#/VBで実装されています。Web屋さんがJavaScript使ってストアアプリに仕立てたものも、ちらほらと見かけます。一方C++/CXでのストアアプリの実装例ってお目にかかることがないんですよね。Windows Formsを使ったアプリならばC++/CLIで実装となるのですが、C++/CLIはかなりシバリがきつく、nativeとmanagedとの擦り合わせが厄介です。対してC++/CXによるストアアプリはベースがnativeですからnativeとmanagedの混在するC++/CLIよりはずっと楽に/自然に実装できます。C#/VBより面倒ではあるけれど、決して難しくはないと思っています。

