1703向けアプリで1809の最新機能を使う
それでは実際にバックポートしていきましょう。DataGridも使いたいので、プロジェクトのターゲットの最小バージョンは1703(15063)にします。1809の最新機能を使いたいので、ターゲットの最大バージョンは1809(17763)にします。
まず、ページのXAMLの先頭に、Windows UI Libraryの名前空間を追加しておきます(次のコード)。
<Page ……省略…… mc:Ignorable="d" xmlns:mux="using:Microsoft.UI.Xaml.Controls" >
1809のメニューバー
先ほど紹介したメニューバーのXAMLをコピー&ペーストし、MenuBarタグとMenuBarItemタグに名前空間の接頭辞mux:を追加します。その下のMenuFlyoutSubItemタグやMenuFlyoutItemタグなどは、Windows SDKのコントロールをそのまま使います(接頭辞mux:を付けません)。
すると、キーボードアクセラレータの定義がエラーになってしまいます。1703ではサポートされていないので、使えないのです。また、最小バージョンを1607に下げると、メニュー項目のアイコンもサポートされなくなります。前述したように、残念ながらOSに依存する機能などでは対応していない部分もあるのです。
1703ではサポートされていないキーボードアクセラレータを削ると、XAMLは次のコードのようになります(前と同じく、「Plain Text Document」メニューと「Save」メニューに関連する部分のみ抜粋)。
<mux:MenuBar Background="AliceBlue" >
<mux:MenuBarItem Title="File" AccessKey="F">
<MenuFlyoutSubItem Text="New" AccessKey="N">
<MenuFlyoutItem Text="Plain Text Document" AccessKey="P"
Command="{x:Bind _menuCommand}"
CommandParameter="Plain Text Document" />
<!-- ……省略…… -->
</MenuFlyoutSubItem>
<!-- ……省略…… -->
<MenuFlyoutItem x:Name="MenuSave" Text="Save" AccessKey="S" Icon="Save"
Command="{x:Bind _menuCommand}"
CommandParameter="Save" />
<MenuFlyoutSeparator />
<!-- ……省略…… -->
</mux:MenuBarItem>
<!-- ……省略…… -->
</mux:MenuBar>
これで、1703でも次の画像のようにメニューバーが表示できました。ただし、キーボードアクセラレータは付いていません。
最近のWindows 10で実行したときにはキーボードアクセラレータを出したいというときは、バージョンによって切り分けることになります。連載第1回で紹介した条件付きXAMLで可能なはずなのですが、上手く行きませんでした。そこで、ページのInitializeComponentメソッド呼び出しの後に、次のコードのSetupShortcutKeyメソッドの呼び出しを追加してやります。キーボードアクセラレータは1803からメニューに表示されるようになったので、それ以上のバージョンのときだけキーボードアクセラレータを付けるコードを実行します。
using TKH = Microsoft.Toolkit.Uwp.Helpers;
// ……省略……
private readonly ushort _osBuild = TKH.SystemInformation.OperatingSystemVersion.Build;
private const ushort Ver1803 = 17134;
private void SetupShortcutKey()
{
if (_osBuild >= Ver1803)
{
this.MenuSave.KeyboardAccelerators.Add(new KeyboardAccelerator
{ Modifiers = VirtualKeyModifiers.Control, Key = VirtualKey.S });
this.MenuCopy.KeyboardAccelerators.Add(new KeyboardAccelerator
{ Modifiers = VirtualKeyModifiers.Control, Key = VirtualKey.C });
this.MenuPaste.KeyboardAccelerators.Add(new KeyboardAccelerator
{ Modifiers = VirtualKeyModifiers.Control, Key = VirtualKey.V });
}
}
上のコードでは、Windows Community Toolkitを使ってWindows 10のビルド番号を取得しています。
これで、次の画像のように、上位バージョンで実行したときはキーボードアクセラレータも動作します。
バージョンアダプティブなコーディング
Microsoftのドキュメントには、ApiInformationクラスを使ってAPIの有無を調べるようにと書いてあります。上の例では、MenuFlyoutItemクラスがKeyboardAcceleratorsプロパティを持っているかどうかを調べて、それによって処理を切り分けるべきだ、となります。
ところが、APIが存在することと、それが期待通りに機能するかどうかは別の話です。KeyboardAcceleratorsプロパティは1709(16299)から存在しますが、メニュー項目に自動的にショートカットキーが表示されるのは、1803からなのです。それで、ここではWindows 10のバージョンを見て処理を切り替えるというコードを書きました。
1809のドロップダウン系ボタン
これはコピー&ペーストしたら、ドロップダウン系ボタンのタグとそれぞれのフライアウトのタグに接頭辞mux:を付けます。
やはり非互換性によるエラーがいくつか出ますので、それを修正します。MenuFlyoutコントロールのPlacementプロパティの設定値や、フライアウト内に置いたGridViewコントロールのSelectedIndexプロパティ、同じくフライアウト内に置いたListViewコントロールなどがエラーになりました(最後のListViewコントロールが置けない問題は、Windows UI Library自体の不具合だと思われます)。
MenuFlyoutコントロールのPlacementプロパティで左端合わせ/右端合わせが出来なかったので、ドロップダウンの出る位置が前とは違っています。
RichEditBoxのコンテキストメニュー
RichEditBoxコントロールはWindows UI Libraryで提供されていませんので、標準SDKのものをそのまま使うしかありません。次の画像のように、表示されるコンテキストメニューが違います。
この古いタイプのコンテキストメニューには、ボタンを追加したりできません。カスタマイズするコードは、キーボードアクセラレータと同じくバージョンアダプティブに修正する必要があります(次のコード)。
private const ushort Ver1809 = 17763;
// ……省略……
if (_osBuild >= Ver1809)
{
this.richEdit.Loaded += (s, e)
=> this.richEdit.ContextFlyout.Opening += ContextFlyout_Opening;
this.richEdit.Unloaded += (s, e)
=> this.richEdit.ContextFlyout.Opening -= ContextFlyout_Opening;
void ContextFlyout_Opening(object sender, object e)
{
// ……省略(前と同じ)……
}
}
DataGrid
Windows Community ToolkitのDataGridコントロール(バージョン4.0のもの)はWindows 10 1703で動作します。今回はバージョン5.0の機能を使っていなかったので、そのままコピー&ペーストするだけでちゃんと動きました(次の画像)。
