これは、App.xamlにマージされたXAMLリソース辞書に定義されているため、アプリケーション全体で使用できます。コンテキストメニューをリソースとして定義するのは簡単ですが、リソースの場合は、そのコンテキストが何であるかを知らせるために余分な作業を行う必要があります。ほとんどのWPFコントロールではRelativeSource AncestorType
バインディングを実行しますが、contextmenuはVisualTreeにはないので動作しません。
It says here that ContextMenu.PlacementTarget will be set to the context menu's ownerメニューが表示されたときにデスクトップにウォッチウィンドウが表示されたら、それについて冗談を言うだけです。
<local:Bar >
<local:Bar.ContextMenu>
<ContextMenu>
<MenuItem
Header="{Binding ArbitraryProperty}"
/>
</ContextMenu>
</local:Bar.ContextMenu>
</local:Bar>
...しかし、コンテキストメニューがリソースであるときには動作しません:あなたはこのようなコンテキストメニューを定義する場合、そのDataContext
はlocal:Bar
のこのインスタンスです。その場合はDataContext
を自分で設定する必要があります。それはあまりにもひどく苦痛ではないことが分かります。これはカスタムコントロールのContextMenuOpening
イベントで行います。 2つのカスタムコントロールを定義します。 1つは、をStyle
にEventSetter
で設定し、もう1つでは、ContextMenuOpening
をコンストラクタにラムダで処理します。私はEventSetter
バージョンが好きです。もう少し作業している間は、Style
を置くことができる絶対に何かにハンドラを投げることができるからです。そのようなハンドラを設定するために、接続プロパティ/動作を記述することもできます。これはさらに使いやすくなります。
テーマ/ Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SharedContextMenuTest"
x:Class="SharedContextMenuTest.Themes.Generic"
>
<ContextMenu x:Key="SharedContextMenu">
<MenuItem Header="{Binding ArbitraryProperty}" />
</ContextMenu>
<Style TargetType="{x:Type local:Foo}">
<!-- IMPORTANT -->
<Setter Property="ContextMenu" Value="{StaticResource SharedContextMenu}" />
<EventSetter Event="ContextMenuOpening" Handler="FooBar_ContextMenuOpening" />
<!-- !IMPORTANT -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Foo}">
<Border
Background="GhostWhite"
BorderBrush="DodgerBlue"
BorderThickness="1"
Margin="1"
>
<Label
Content="{TemplateBinding ArbitraryProperty}"
Padding="20"
/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:Bar}">
<!-- IMPORTANT -->
<!-- Bar sets up the ContextMenuOpening handler in its constructor -->
<Setter Property="ContextMenu" Value="{StaticResource SharedContextMenu}" />
<!-- !IMPORTANT -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Bar}">
<Border
Background="GhostWhite"
BorderBrush="ForestGreen"
BorderThickness="1"
Margin="1"
>
<Label
Content="{TemplateBinding ArbitraryProperty}"
Padding="20"
/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
テーマ/ Generic.xaml.cs
namespace SharedContextMenuTest.Themes
{
public partial class Generic
{
private void FooBar_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
(e.Source as FrameworkElement).ContextMenu.DataContext = e.Source;
}
}
}
MyCustomControls.cs
namespace SharedContextMenuTest
{
public class Foo : Control
{
public static readonly DependencyProperty ArbitraryPropertyProperty =
DependencyProperty.Register("ArbitraryProperty", typeof(String), typeof(Foo),
new PropertyMetadata(nameof(Foo)));
}
public class Bar : Control
{
public Bar()
{
// Foo has an EventSetter in its Style; here we illustrate a quicker way.
ContextMenuOpening += (s, e) => ContextMenu.DataContext = this;
}
public static readonly DependencyProperty ArbitraryPropertyProperty =
DependencyProperty.Register("ArbitraryProperty", typeof(String), typeof(Bar),
new PropertyMetadata(nameof(Bar)));
}
}
MainWindow.xaml
<StackPanel Orientation="Vertical">
<local:Foo />
<local:Bar />
</StackPanel>
こんにちはEd!私はあなたが正しいことを理解したかどうかは分かりません。それは私が各CustomControlのためにそれを定義しなければならないという意味ですか? –
ああ、ちょっとしたことがあります。同じカスタムメニューをたくさん用意しておけば、独自のリソースとして定義することもできます。それに少し戻って、私は私の電話atmにいる。 –
@PatrickPirzerよ、私は今目覚めています。キャッチがあります:共有メニューを定義するのは簡単です。しかし、メニュー項目に何かをバインドするコントロールに何かをバインドすることは絶対的な悪夢です。 WPFの数少ない部分のうちの1つで、ひどくひどく台無しになったものです。 WPFは実際には、コンテキストメニューがそのコンテキストが何であるかを知るのを防ぐのが本当に難しいです。 –