简体   繁体   English

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

[英]Can i define a standard ContextMenu for WPF CustomControls?

Because i'm making a bunch of WPF CustomControls and want use a standard ContextMenu for These controls, i wish to know, if i can define that ContextMenu as a resource. 因为我要制作一堆WPF CustomControl,并且想为这些控件使用标准的ContextMenu,所以我想知道是否可以将ContextMenu定义为资源。

And how has the style of such a ContextMenu to be defined? 以及如何定义这种ContextMenu的样式? If it is possible, i could overwrite the control's Contextmenu with something like that: 如果有可能,我可以用类似以下的方法覆盖控件的Contextmenu:

ContextMenu="{StaticResource standardcontextmenu}"

Thanks in advance! 提前致谢!

Define this in a XAML resource dictionary that's merged into App.xaml, so it's available throughout your application. 在合并到App.xaml的XAML资源字典中定义它,因此在整个应用程序中都可用。 It's easy to define a context menu as a resource, but if it's a resource, you need to do extra work to let it know what its context is. 将上下文菜单定义为资源很容易,但是如果它是资源,则需要做一些额外的工作来让它知道其上下文是什么。 With most WPF controls you'd do a RelativeSource AncestorType binding, but the contextmenu is not in the VisualTree so that doesn't work. 对于大多数WPF控件,您需要进行RelativeSource AncestorType绑定,但是contextmenu不在VisualTree中,因此无法使用。

It says here that ContextMenu.PlacementTarget will be set to the context menu's owner when the menu opens, but the watch window on my desktop says they're just kidding about that. 它说在这里,当菜单打开时,ContextMenu.PlacementTarget将被设置为上下文菜单的所有者 ,但是我桌面上的监视窗口说他们只是在开玩笑。 If you define a context menu like this, its DataContext is this instance of local:Bar : 如果您定义这样的上下文菜单,则其DataContext就是local:Bar以下实例:

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

...but that doesn't work when the context menu is a resource. ...但是当上下文菜单是资源时这不起作用。 In that case, you need to set the DataContext yourself. 在这种情况下,您需要自己设置DataContext That turns out not to be too awfully painful. 事实证明,这并不会太痛苦。 We'll do it in the ContextMenuOpening event on the custom control. 我们将在自定义控件的ContextMenuOpening事件中进行此操作。 We'll define two custom controls. 我们将定义两个自定义控件。 In one we'll set up ContextMenuOpening via an EventSetter in the Style , and in the other we'll handle ContextMenuOpening with a lambda in the constructor. 在其中一个中,我们将通过StyleEventSetter设置ContextMenuOpening ,在另一个中,我们将在构造ContextMenuOpening使用lambda处理ContextMenuOpening I like the EventSetter version because while it's a little more work, you can toss that handler on absolutely anything you can put a Style on. 我喜欢EventSetter版本,因为尽管它需要做更多的工作,但是您可以将它放置在任何可以放置Style东西上。 You could write an attached property/behavior to set a handler like that as well, which would be even easier to use. 您也可以编写一个附加的属性/行为来设置类似的处理程序,这将更加易于使用。

Themes/Generic.xaml 主题/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>

Themes/Generic.xaml.cs 主题/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 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 MainWindow.xaml

<StackPanel Orientation="Vertical">
    <local:Foo />
    <local:Bar />
</StackPanel>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM