为啥我的隐式 ContextMenu 样式不会覆盖 TextBox 上下文菜单样式?
Posted
技术标签:
【中文标题】为啥我的隐式 ContextMenu 样式不会覆盖 TextBox 上下文菜单样式?【英文标题】:Why my implicit ContextMenu Style doesn't override the TextBox context menu style?为什么我的隐式 ContextMenu 样式不会覆盖 TextBox 上下文菜单样式? 【发布时间】:2016-04-08 13:47:45 【问题描述】:我对 ContextMenu
有这种隐含的风格,我从 this site 那里得到:
<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>
然后我尝试在这里使用它,以便它同时应用于TextBox
的默认ContextMenu
和我为Button
添加的ContextMenu
。
</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
。
我觉得这应该是相当直接和简单的,为什么我的隐式风格没有应用到TextBox
的默认ContextMenu
,我做错了什么?
==更新==
目前我不确定答案,但我认为这里的问题是TextBox
和ContextMenu
的设计存在一些缺陷,我希望有更多知识的人能够确认。
使用 Snoop 我可以看到 ContextMenu
不是您所期望的对象,而是一个 EditorContextMenu
对象,它是内部的,因此您无法设置它的样式。他们为什么使用它?我不知道。
作为一种解决方法,我创建了一个默认上下文菜单并使用它。如果您向TextBox
添加上下文菜单,它会正确地采用隐式样式。
既然你知道默认ContextMenu
有的item,而item又基本使用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
将采用隐式样式。
【问题讨论】:
您的资源字典在哪里?在某处有像<Style TargetType="ContextMenu" BasedOn="StaticResource x:Type ContextMenu" />
这样的行,您只需更改 basedon 值即可将您的新样式引用为全局默认值。
@ChrisW。 - 对不起,我不明白。正如您在我的代码中看到的,我在应用程序资源中定义了 ContextMenu
的隐式样式:<Style TargetType="ContextMenu">
。我尝试将它添加到不同的级别,但在任何级别中都不起作用。
您找到解决问题的方法了吗?我有同样的问题:(
@mgarant - 没有人回答为什么会这样,从使用 snoop 来看,它似乎在设计中存在一些缺陷,但我找到了解决方法,我将其作为更新发布。
【参考方案1】:
对我来说,这看起来像是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>
我说的是错误,因为默认 ContextMenu
的 ScrollBar
实现确实正确应用了隐式样式。
坏消息是,在 TextBox
的当前实现中,您无法达到内部创建的默认 ContextMenu
并且未应用隐式样式。
【讨论】:
以上是关于为啥我的隐式 ContextMenu 样式不会覆盖 TextBox 上下文菜单样式?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Hibernate 会为 @ManyToOne 关联的隐式连接生成 CROSS JOIN?