web-dev-qa-db-ja.com

WPF:ContextMenuを動的に生成する方法

複数の項目があるListBox(SelectionMode = Extended)があり、コンテキストメニュー機能を追加したいと思います。問題は、いくつかの条件に基づいてコンテキストメニューを動的に作成する方法です。例えば。 1つの項目のみが選択されている場合は通常のコンテキストメニューを表示し、複数の項目が選択されている場合は他のコンテキストメニュー(たとえば、いくつかの新しい項目が追加されている)を表示したいと思います。また、選択した項目の中に、プロパティが設定されているものが少なくとも1つある場合は、第3の種類のコンテキストメニューを作成したいと思います。など...これらのような複数の条件が存在する可能性があります。

基本的に、ユーザーがマウスボタンを右クリックした直後で、メニューが実際に表示される直前に、コンテキストメニューを動的に生成する必要があります。これは可能ですか?

14
matori82

私の質問に対する答えを見つけました。それはContextMenuOpeningイベントです。基本的に、このイベントを処理し、現在のアプリケーションの状態に応じてメニューを調整する必要があります。詳細はこちら: https://msdn.Microsoft.com/en-us/library/Bb613568(v = vs.100).aspx

7
matori82

これは古い質問だと思います。 ContextMenuクラスはItemsSourceプロパティを介したバインディングをサポートしているため、MVVMシナリオでのOPの元の問題を解決する非常に簡単な答えがあるようです。

それが誰かがこれに遭遇するのを助けることを願っています。

[〜#〜] xaml [〜#〜]

      <ContextMenu ItemsSource="{Binding Path=ItemList, UpdateSourceTrigger=PropertyChanged}">
      </ContextMenu>

ViewModelでは、現在のアプリケーションの状態に応じて「ItemList」プロパティを動的に変更できます。

12
nepdev

特定のシナリオに基づいて使用する定義済みのコンテキストメニューのセットがある場合は、いつでもコンテキストメニューをリソースとして作成できます。

<Window.Resources>
    <ContextMenu x:Key="Menu1">
        <MenuItem>Item1</MenuItem>
    </ContextMenu>
    <ContextMenu x:Key="Menu2">
        <MenuItem>Item1</MenuItem>
        <MenuItem>Item2</MenuItem>
    </ContextMenu>
</Window.Resources>

次に、ListBoxにデータトリガーを作成して、ContextMenuを使用するように設定します。以下の方法ではなく、ビューモデルのプロパティにバインドするか、このためのコードを背後にバインドすることをお勧めしますxamlで非常に複雑になります。ここでの実装は、項目が1つだけ選択されているかどうかを確認し、その場合はMenu1に切り替えます。

<ListBox x:Name="mylist" SelectionMode="Multiple" ContextMenu="{StaticResource Menu2}" >
    <ListBox.Style>
        <Style TargetType="{x:Type ListBox}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=SelectedItems.Count, RelativeSource={RelativeSource Self}}" Value="1" >
                    <Setter Property="ContextMenu" Value="{StaticResource ResourceKey=Menu1}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ListBox.Style>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=DisplayName}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

表示するコンテキストメニューの選択がビューにのみ関係する場合は、コードビハインドで処理することをお勧めします。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        // Hook up any events that might influence which menu to show
        mylist.SelectionChanged += listSelectionChanged;
        InitializeComponent();
    }

    private void listSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var listBox = sender as ListBox;
        if (listBox == null)
            return; // Or throw something, hard

        ContextMenu menuToUse;
        // Logic for selecting which menu to use goes here

        listBox.ContextMenu = menuToUse;
    }
}

ViewModelがどのメニューを表示するかに関心がある場合(そのようには聞こえませんが、完全なコンテキストを知らないとわかりにくい)、ViewModelでどのContextMenuを決定できるようにするいくつかのプロパティを公開できます。表示します。個々のブール値プロパティではなく、常に1つのブール値のみが真であることを確認するクラスを作成することをお勧めします。

public class MyViewModel : INotifyPropertyChanged
{

    public MyViewModel()
    {
        SelectedItems = new ObservableCollection<string>();
        SelectedItems.CollectionChanged += SelectedItemsChanged;
    }

    private void SelectedItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        // Logic to see which ShowMenuX property to set to true goes here
    }

    public ObservableCollection<string> SelectedItems { get; set; }

    private bool _showMenu1 = false;
    public bool ShowMenu1
    {
        get { return _showMenu1; }
        set
        {
            _showMenu1 = value;
            RaisePropertyChanged("ShowMenu1");
        }
    }

    // INotifyPropertyChanged implementation goes here
}
4
Peter Karlsson