为什么我的隐式ContextMenu样式不会覆盖TextBox上下文菜单样式?

Dzy*_*ann 5 c# wpf xaml

ContextMenu这个网站上获取了这种隐式样式:

<Application.Resources>
        <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
        <SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />

        <Style TargetType="ContextMenu">
            <Setter Property="SnapsToDevicePixels" Value="True"/>
            <Setter Property="OverridesDefaultStyle" Value="True"/>
            <Setter Property="Grid.IsSharedSizeScope" Value="true"/>
            <Setter Property="HasDropShadow" Value="True"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ContextMenu">
                        <Border 
          Name="Border"
          Background="{StaticResource WindowBackgroundBrush}"
          BorderBrush="{StaticResource SolidBorderBrush}"
          BorderThickness="1" >
                            <StackPanel IsItemsHost="True"
                      KeyboardNavigation.DirectionalNavigation="Cycle"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasDropShadow" Value="true">
                                <Setter TargetName="Border" Property="Padding" Value="0,3,0,3"/>
                                <Setter TargetName="Border" Property="CornerRadius" Value="4"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Application.Resources>
Run Code Online (Sandbox Code Playgroud)

然后,我努力让武功既应用在默认情况下,在这里使用ContextMenuTextBoxContextMenu增加了我的我Button

</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <TextBox Height="30" Width="200">Test</TextBox>
    <Button Grid.Row="1" Width="200" Height="30" Content="Test2">
        <Button.ContextMenu>
            <ContextMenu>
                <MenuItem>Test</MenuItem>
                <MenuItem>Test2</MenuItem>
            </ContextMenu>
        </Button.ContextMenu>
    </Button>
</Grid>
Run Code Online (Sandbox Code Playgroud)

样式套用到Button,但套用在上TextBox

我觉得这应该是相当简单明了,为什么不是我得到应用于默认的隐式样式ContextMenuTextBox,我究竟做错了什么?

==更新==

我不知道此刻肯定的答案,但我觉得这里的问题是的设计缺陷的一些TextBoxContextMenu,我希望有人更了解可以证实。使用Snoop,我可以看到这ContextMenu不是您期望的EditorContextMenu对象,而是一个内部对象,因此您无法设置其样式。他们为什么使用它?我不知道。

解决方法是,创建一个默认的上下文菜单并使用它。如果您将上下文菜单添加到中TextBox,它将正确采用隐式样式。既然您知道默认项所ContextMenu具有的项目,并且这些项目又基本上使用了ApplicationCommands,则它非常简单:

<ContextMenu x:Key="DefaultContextMenu">
        <MenuItem Command="ApplicationCommands.Copy" />
        <MenuItem Command="ApplicationCommands.Cut" />
        <MenuItem Command="ApplicationCommands.Paste" />        
    </ContextMenu>
Run Code Online (Sandbox Code Playgroud)

然后在您的TextBoxStyle中执行以下操作:

<Style x:Key="MyTextBoxStyle" TargetType="TextBox">
    <Setter Property="ContextMenu" Value="{StaticResource DefaultContextMenu}" />
Run Code Online (Sandbox Code Playgroud)

这样,您TextBox的默认值ContextMenu将采用隐式样式。

Rek*_*ino 0

对我来说,这看起来像是TextBox实现中的一个错误,其特点是它ContextMenu不属于 VisualTree。

我会尽力解释一下。

  • 默认值ContextMenu将由TextBox 实现创建并显示,即在方法中 OnContextMenuOpening(ContextMenuEventArgs e)。要查看它,您可以在自定义中重写此方法TextBox并省略调用base.
  • ContextMenu显式设置的 相比,默认值的ContextMenu处理方式不同(不通过 处理ContextMenuService)。
  • 仅当未显式设置该 属性(XAML 或代码隐藏)时ContextMenu,才会创建默认值。TextBoxTextBox.ContextMenu
  • ContextMenu不在 VisualTree 中,与 VisualTree 的唯一连接是属性PlacementTarget(例如,如果您ContextMenu在代码后面创建并打开 a 且未设置 a, PlacementTarget则不会应用隐式样式)。
  • TextBox实现中ContextMenu.PlacementTarget被设置,但该值来自一些内部缓存/字典。我无法调试它并肯定地说,但我认为这里缓存的值是错误的。
    您可以做一些小测试来查看缓存的值是否正确。重写OnContextMenuOpening(ContextMenuEventArgs e)自定义 函数TextBox,并在调用基本函数之前进行修改 TextBox(例如每次设置另一个函数Margin)。默认ContextMenu连不开!如果您在base调用的地方自行实例化并显示ContextMenu,则Margin修改不会干扰并且打开ContextMenu将具有您在资源中设置的隐式样式。

要测试的片段

public class MyStyledTextBox: TextBox
{
    protected override void OnContextMenuOpening(ContextMenuEventArgs e)
    {
        //this.Margin = new Thickness(0, 20, 0, 0);

        var uiScope = e.Source as TextBox;

        var ctxm = new ContextMenu();

        MenuItem menuItem;
        menuItem = new MenuItem();
        menuItem.Header = "CutCustom";
        menuItem.CommandTarget = this;
        menuItem.Command = ApplicationCommands.Cut;
        ctxm.Items.Add(menuItem);

        ctxm.PlacementTarget = uiScope;

        ctxm.IsOpen = true;

        //base.OnContextMenuOpening(e);
    }
}

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="ContextMenu" >
            <Setter Property="SnapsToDevicePixels" Value="True"/>
            <Setter Property="OverridesDefaultStyle" Value="True"/>
            <Setter Property="Grid.IsSharedSizeScope" Value="true"/>
            <Setter Property="HasDropShadow" Value="True"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ContextMenu">
                        <Border  Name="Border_custom" Background="Chocolate" BorderBrush="Coral" BorderThickness="1" >
                            <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasDropShadow" Value="true">
                                <Setter TargetName="Border_custom" Property="Padding" Value="0,3,0,3"/>
                                <Setter TargetName="Border_custom" Property="CornerRadius" Value="4"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </StackPanel.Resources>

    <local:MyStyledTextBox Height="30" Width="200" Text="Test">
        <!--<TextBox.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Test"/>
                <MenuItem Header="Test2"/>
            </ContextMenu>
        </TextBox.ContextMenu>-->
    </local:MyStyledTextBox>
</StackPanel>
Run Code Online (Sandbox Code Playgroud)

我说的是错误,因为ScrollBar默认的实现确实ContextMenu正确应用了隐式样式

坏消息是,在当前的实现中,TextBox您无法达到内部创建的默认值ContextMenu并且未应用隐式样式。