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

时间:2016-01-04 21:01:58

标签: c# wpf xaml

我从this site获取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>

然后我尝试在此处使用它,因此它同时适用于ContextMenu的默认TextBox和我为ContextMenu添加的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>

该样式应用于Button,但未应用于TextBox

我觉得这应该是相当简单和简单的,为什么我的隐式样式不适用于ContextMenu的默认TextBox,我做错了什么?

==更新==

我目前还不确定答案,但我认为这里的问题是TextBoxContextMenu的设计中的一些缺陷,我希望有更多知识渊博的人能够证实。 使用Snoop我可以看到ContextMenu不是你期望的对象,而是EditorContextMenu对象,它是内部的,所以你不能设置它的样式。他们为什么要这样用?我不知道。

作为一种解决方法,我创建了一个默认的上下文菜单并使用它。如果向TextBox添加上下文菜单,则它会正确地采用隐式样式。 既然您知道默认ContextMenu具有的项目,并且项目依次使用ApplicationCommands,则非常简单:

<ContextMenu x:Key="DefaultContextMenu">
        <MenuItem Command="ApplicationCommands.Copy" />
        <MenuItem Command="ApplicationCommands.Cut" />
        <MenuItem Command="ApplicationCommands.Paste" />        
    </ContextMenu>

然后在TextBoxStyle中执行:

<Style x:Key="MyTextBoxStyle" TargetType="TextBox">
    <Setter Property="ContextMenu" Value="{StaticResource DefaultContextMenu}" />

有了这个,TextBox的默认ContextMenu将采用隐式格式。

1 个答案:

答案 0 :(得分:0)

对我来说,这似乎是TextBox实现中的一个错误,其独特之处在于ContextMenu不属于VisualTree。

我会尽力解释。

  • 将创建默认ContextMenu并由TextBox显示 实现,即方法 OnContextMenuOpening(ContextMenuEventArgs e)。要看到它,你可以 在自定义TextBox中重写此方法,并省略base.调用。
  • 与显式设置的ContextMenu相比,默认ContextMenu为 (不通过ContextMenuService处理)。
  • 只有在以下情况下,ContextMenu才会创建默认TextBox: 没有明确设置TextBox.ContextMenu属性(XAML或后面的代码)。
  • ContextMenu不在VisualTree中,并且是与 VisualTree是属性PlacementTarget(例如,如果要创建 并在后面的代码中打开ContextMenu,并且不要设置 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>

我说的是关于bug的问题,因为默认情况下ScrollBar的{​​{1}}实现确实正确地应用了隐式样式

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