メインメニューなどはあらかじめ定義された機能を呼び出すので、XAMLで静的に定義しても良いですが、最近使ったファイル(Recent)など動的に作っているメニュー項目がどのように作っているか調べてみました。
ファイル名:MainWindow.xaml
<Window
x:Class="DynamicMenu01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DynamicMenu01"
mc:Ignorable="d"
Title="タイトル"
Height="450"
Width="800"
xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBox Text="{Binding TextBoxText.Value}">
<TextBox.ContextMenu>
<ContextMenu ItemsSource="{Binding ContextMenuItems}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Header}"/>
<Setter Property="Command" Value="{Binding Path=MyCommand}"/>
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Grid>
</Window>
ファイル名:MenuItemViewModel.cs
using System.ComponentModel;
using Reactive.Bindings;
public class MenuItemViewModel : INotifyPropertyChanged
{
public string Header { get; set; } = "";
public ReactiveCommand<EventArgs> MyCommand { get; set; } = new();
// INotifyPropertyChanged
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string name) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
ファイル名:MainWindowViewModel.cs
using System.Diagnostics;
using System.ComponentModel;
using Reactive.Bindings;
namespace DynamicMenu01;
public class MainWindowViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string name) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
/**************************************************************************
* プロパティ
**************************************************************************/
public ReactiveCollection<MenuItemViewModel> ContextMenuItems { get; set; } = [];
public ReactiveProperty<string> TextBoxText { get; private set; } = new("");
public MainWindowViewModel()
{
PropertyChanged += (s, e) => {};
var mi1 = new MenuItemViewModel
{
Header = "ファイル",
};
mi1.MyCommand.Subscribe(e=>
{
TextBoxText.Value="ファイル";
});
ContextMenuItems.AddOnScheduler(mi1);
var mi2 = new MenuItemViewModel
{
Header = "編集",
};
mi2.MyCommand.Subscribe(e=>
{
TextBoxText.Value="編集";
});
ContextMenuItems.AddOnScheduler(mi2);
}
}
テキストボックスで右クリックコンテキストメニューが表示されます。
項目を選択するとテキストボックスに文字がセットされます。
コンテキストメニューの「ファイル」や「編集」などの項目は、MainWindowViewModelのContextMenuItemsコレクションにコンストラクタで動的に追加しています。
ContextMenuItemsはMainWindow.xamlのContextMenuのItemsSourceにバインドしています。
メニューの表示名にあたるHeaderや項目をクリックしたさい呼び出されるCommandはContextMenu.ItemContainerStyle内でバインドしています。
このあたりよく理解できていないのですが、MenuItem.ItemTemplateを使う方法では上手く動作されることが出来ませんでした。メニューの表示まではたどり着けましたが、Commandが反応しませんでした。
コメント