WinUI - データバインディング(Bindings->Update)
C++ で WinUI 3 ライブラリを使う
この記事は試行錯誤の結果をまとめたもので、WinUI の正しい使い方ではないかもしれません。
下図のようにTextBox に入力した文字列をもう一方のTextBoxに反映する方法を考えます。
XAMLはこんな感じです。入力用のTextBoxと出力用の読み取り専用のTextBoxがあります。
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBox Header="String" /> <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" Margin="8"/> <TextBox IsReadOnly="True" /> </StackPanel>
データバインディングで実現するためには、文字列の変数が1つあればよいので、.idl ファイルを開き、文字列型のValueStringプロパティを宣言します。
[default_interface] runtimeclass Binding1Page : Microsoft.UI.Xaml.Controls.Page { Binding1Page(); String ValueString; }
それと同時にクラスに ValueString のセッターとゲッターを実装します。
struct Binding1Page : Binding1PageT<Binding1Page> { Binding1Page(); void ValueString(const hstring& value) { m_valueString = value; } hstring ValueString() { return m_valueString; } private: hstring m_valueString; };
次に ValueString とバインディングするようにXAMLファイルを修正します。
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBox Header="String" Text="{x:Bind ValueString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" Margin="8"/> <TextBox Text="{x:Bind ValueString}" IsReadOnly="True" /> </StackPanel>
うまく動作しそうですが、ここまでのコードだと TextBox のテキストを変更する毎に ValueString のセッターは呼び出されていますが、出力用の TextBox は全然更新されません。
コードの値を画面に反映させるには、Bindings->Update(); を実行します。
ValueString のセッターの実装を値の変更があったときに Bindings->Update(); を実行するように変更すれば期待した動作になります。
void Binding1Page::ValueString(const hstring& value) { if (m_valueString != value) { m_valueString = value; Bindings->Update(); } }
データバインディングを使わない方法
今回の例であればデータバインディングを使わない方が簡単かもしれません。
- コードから TextBox にアクセスできるように x:Name 属性で名前を付けます。
- テキストが変更されたときに呼び出される TextChanged イベントのイベントハンドラを入力用の TextBox に追加します。
- TextChanged イベントが発生したら、InputTextBox のテキストを OutputTextBox にコピーします。
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBox x:Name="InputTextBox" TextChanged="InputTextBox_TextChanged" Header="String" /> <FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" Margin="8"/> <TextBox x:Name="OutputTextBox" IsReadOnly="True"/> </StackPanel>
void winrt::NavigationView1::implementation::Binding1Page::InputTextBox_TextChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::TextChangedEventArgs const& e) { OutputTextBox().Text(InputTextBox().Text()); }
参考
WinUI 3 with C++ 入門 - ビリヤードが好きなプログラマー
Microsoft インターフェイス定義言語 3.0 の概要 - Windows UWP applications | Microsoft Learn
WinUI - データバインディングできるモデルを追加する
C++ で WinUI 3 ライブラリを使う
データバインディングできるモデル (class) をプロジェクトに追加しようとしたとき、その方法がわからず苦労したので紹介します。
Visual Studio のソリューションエクスプローラを開き、プロジェクトを選択した状態で右クリックのポップアップメニューを表示します。
ポップアップメニューの追加 → モジュールを選択します。
新しい項目の追加ダイアログで、ビューモデル (C++/WinRT) を追加します。
私は、Page やユーザーコントロールと違い「 WinUI 3 のプロジェクトなのに (C++/WinRT) の項目を追加してよいのだろうか」と悩んでました。
参考
WinUI - ナビゲーションのウィンドウに設定ボタンを表示する
C++ で WinUI 3 ライブラリを使う
NavigationView の IsSettingsVisible 属性を True にすると設定ボタンを表示します。
<NavigationView IsSettingsVisible="True">
メニューを選択したときのイベントハンドラでは、IsSettingsSelected, IsSettingsInvoked 関数で設定ボタンが押されたか判断できます。
void winrt::NavigationView1::implementation::MainWindow::navi_SelectionChanged(winrt::Microsoft::UI::Xaml::Controls::NavigationView const& sender, winrt::Microsoft::UI::Xaml::Controls::NavigationViewSelectionChangedEventArgs const& args) { if (args.IsSettingsSelected()) { // 設定メニューを選択した } } void winrt::NavigationView1::implementation::MainWindow::navi_ItemInvoked(winrt::Microsoft::UI::Xaml::Controls::NavigationView const& sender, winrt::Microsoft::UI::Xaml::Controls::NavigationViewItemInvokedEventArgs const& args) { if (args.IsSettingsInvoked()) { // 設定メニューを選択した } }
参考
WinUI - 新しい UserControl を追加する
WinUI - メニューを選択したときのイベントを実装する
C++ で WinUI 3 ライブラリを使う
NavigationVeiw のメニューを選択したときのイベントを処理する方法を紹介します。
NavigationView のメニューを選択すると ItemInvoked イベントが発生します。また選択しているメニューが変化したときは SelectionChanged イベントも発生します。
<NavigationView x:Name="navi" ItemInvoked="navi_ItemInvoked" SelectionChanged="navi_SelectionChanged" IsSettingsVisible="False"> <NavigationView.MenuItems> <NavigationViewItem Content="Page A" Tag="PageA" /> <NavigationViewItem Content="Page B" Tag="PageB" SelectsOnInvoked="False" > <NavigationViewItem.MenuItems> <NavigationViewItem Icon="Home" Content="Page B1" Tag="PageB1" /> <NavigationViewItem Icon="World" Content="Page B2" Tag="PageB2" /> </NavigationViewItem.MenuItems> </NavigationViewItem> </NavigationView.MenuItems> <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="4"> <TextBox x:Name="ItemInvokedText" Header="ItemInvoked" /> <TextBox x:Name="SelectionChangedText" Header="SelectionChanged" /> </StackPanel> </NavigationView>
どのメニューを選択しても同じイベントハンドラが呼び出されるので、Tag 等を設定してメニューを識別できるようにします。
void winrt::NavigationView1::implementation::MainWindow::navi_SelectionChanged(winrt::Microsoft::UI::Xaml::Controls::NavigationView const& sender, winrt::Microsoft::UI::Xaml::Controls::NavigationViewSelectionChangedEventArgs const& args) { if (auto tag = args.SelectedItemContainer().Tag().try_as<hstring>(); tag) { SelectionChangedText().Text(*tag); } } void winrt::NavigationView1::implementation::MainWindow::navi_ItemInvoked(winrt::Microsoft::UI::Xaml::Controls::NavigationView const& sender, winrt::Microsoft::UI::Xaml::Controls::NavigationViewItemInvokedEventArgs const& args) { if (auto tag = args.InvokedItemContainer().Tag().try_as<hstring>(); tag) { ItemInvokedText().Text(*tag); } }
参考
WinUI - ToggleSwitch のラベルをボタンの左側にする
C++ で WinUI 3 ライブラリを使う
ToggleSwitch のラベルをボタンの左側にするには FlowDirection に RightToLeft を設定します。
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="4"> <ToggleSwitch BorderBrush="Red" BorderThickness="1"/> <ToggleSwitch BorderBrush="Blue" BorderThickness="1" FlowDirection="RightToLeft" /> </StackPanel>
ToggleSwitch の領域がわかるように枠に色をつけています。
参考
WinUI 3 with C++ 入門 - ビリヤードが好きなプログラマー
ToggleButton クラス (Microsoft.UI.Xaml.Controls.Primitives) - Windows App SDK | Microsoft Learn
WinUI - 新しい Page を追加する
C++ で WinUI 3 ライブラリを使う
新しい Page をプロジェクトに追加しようとしたとき、その方法がわからず苦労したので紹介します。
Visual Studio のソリューションエクスプローラを開き、プロジェクトを選択した状態で右クリックのポップアップメニューを表示します。
ポップアップメニューの追加 → モジュールを選択します。
新しい項目の追加ダイアログで、空白のページ (WinUI 3) を追加します。
私は、空白のページ (C++/WinRT) を追加して「うまくいかない」と悩んでました。