我可以为WPF CustomControls定义标准的ContextMenu吗?

时间:2016-05-10 15:25:54

标签: c# wpf

因为我正在制作一堆WPF CustomControls并希望对这些控件使用标准的ContextMenu,我想知道,如果我可以将ContextMenu定义为资源。

如何定义这样一个ContextMenu的风格? 如果有可能,我可以用这样的东西覆盖控件的Contextmenu:

ContextMenu="{StaticResource standardcontextmenu}"

提前致谢!

1 个答案:

答案 0 :(得分:2)

在合并到App.xaml的XAML资源字典中定义它,因此它可以在整个应用程序中使用。将上下文菜单定义为资源很容易,但如果它是资源,则需要做额外的工作以让它知道它的上下文是什么。对于大多数WPF控件,您可以执行RelativeSource AncestorType绑定,但上下文菜单不在VisualTree中,因此不起作用。

菜单打开时

It says here that ContextMenu.PlacementTarget will be set to the context menu's owner,但桌面上的监视窗口显示他们只是在开玩笑。如果您定义这样的上下文菜单,则其DataContext就是local:Bar的实例:

<local:Bar >
    <local:Bar.ContextMenu>
        <ContextMenu>
            <MenuItem 
                Header="{Binding ArbitraryProperty}" 
                />
        </ContextMenu>
    </local:Bar.ContextMenu>
</local:Bar>

...但是当上下文菜单是资源时,这不起作用。在这种情况下,您需要自己设置DataContext。结果证明不是太痛苦。我们将在自定义控件的ContextMenuOpening事件中执行此操作。我们将定义两个自定义控件。在一个方面,我们将通过ContextMenuOpening中的EventSetter设置Style,另一方面我们将在构造函数中使用lambda处理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>