1

我曾经创建了一种样式来修复 WPF 中菜单项的错误外观。它主要是关于未对齐的菜单文本。它在左上角太远并且没有使用适当的间距。

我发现它确实可以在窗口菜单中工作,但不能TextBox在我现在测试的上下文菜单中工作。所以问题是,为什么文本框中的上下文菜单不考虑这种样式?

更新:我发现TextBox使用它自己的菜单项类,一个私有嵌套类TextEditorContextMenu.EditorContextMenu和它自己的菜单项,嵌套类EditorMenuItem。两者都来自ContextMenuand MenuItem,resp。因此,如果它们是我设置样式的类的子类,那么为什么我的样式也不适用于它们呢?

我唯一能做的就是复制

<ControlTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type MenuItem}, ResourceId=SubmenuItemTemplateKey}" TargetType="{x:Type MenuItem}">

从 PresentationFramework.Aero 的资源到我的样式文件。但这很明显让我的菜单看起来像 Windows 7,这在 Windows 8 或 10(或 XP)上可能不会出现。但重新定义风格键至少会影响EditorMenuItem's 的外观。为什么?

如果EditorMenuItem没有自己的风格(我找不到),为什么不使用我为基类提供的任何风格呢?它怎么知道使用我的样式,而只使用默认样式,而其他所有上下文菜单都已替换且无法访问该样式?

下面是 XAML 代码,它存储在 MenuStyles.xaml 中并包含在ResourceDictionary来自 App.xaml 中。

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <!-- Expression Blend 4 created this (and a lot more) from some system theme on Windows 7 -->
    <Style TargetType="{x:Type MenuItem}">
        <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
        <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
        <Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=SubmenuItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
        <Style.Triggers>
            <Trigger Property="Role" Value="TopLevelHeader">
                <Setter Property="Padding" Value="7,2,8,3"/>
                <Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=TopLevelHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
            </Trigger>
            <Trigger Property="Role" Value="TopLevelItem">
                <Setter Property="Padding" Value="7,2,8,3"/>
                <Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=TopLevelItemTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
            </Trigger>
            <Trigger Property="Role" Value="SubmenuHeader">
                <Setter Property="Padding" Value="5,4,2,3"/>
                <!-- Changed from 2,3,2,3 -->
                <Setter Property="Template" Value="{DynamicResource {ComponentResourceKey ResourceId=SubmenuHeaderTemplateKey, TypeInTargetAssembly={x:Type MenuItem}}}"/>
            </Trigger>
            <Trigger Property="Role" Value="SubmenuItem">
                <Setter Property="Padding" Value="5,4,2,3"/>
                <!-- Changed from 2,3,2,3 -->
            </Trigger>
        </Style.Triggers>
    </Style>

    <!-- Expression Blend 4 created this from some system theme on Windows 7 -->
    <!-- Edited like in: http://devlicio.us/blogs/christopher_bennage/archive/2008/06/19/styling-separators-in-wpf.aspx -->
    <!-- Decreased in height to be more platform standard -->
    <Style x:Key="{x:Static MenuItem.SeparatorStyleKey}" TargetType="{x:Type Separator}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Separator}">
                    <Grid Margin="0,3,0,2" SnapsToDevicePixels="true">
                        <!-- Changed from 0,6,0,4 -->
                        <Rectangle Fill="#E0E0E0" Height="1" Margin="30,0,1,1"/>
                        <Rectangle Fill="White" Height="1" Margin="30,1,1,0"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
4

1 回答 1

3

您的问题在于没有清楚地了解 WPF 中的样式是如何工作的。有两种不同的处理方式。

第一种是主题风格。EveryFrameworkElement和在初始化时使用属性FrameworkContentElement解析它自己的。具有主题资源的资源字典的位置由 指定。StyleDefaultStyleKeyThemeInfoAttribute

第二种是非主题风格。可以通过指定Style元素的属性来显式设置此样式。或者它也可以在初始化时隐式解决。非主题风格的sSetter优先于Setter主题风格的 s。

当您通过将非主题Style添加到ResourceDictionary应用程序或没有键的元素中来制作非主题时,它会隐式使用,并且只有没有显式设置属性的目标类型的实例Style才会受到影响,而不是派生类型。此行为在FrameworkElement.GetRawValue方法(第 1887 行)中定义:

internal void GetRawValue(DependencyProperty dp, PropertyMetadata metadata, ref EffectiveValueEntry entry)
{
    // ...

    if (dp != StyleProperty)
    {
        if (StyleHelper.GetValueFromStyleOrTemplate(new FrameworkObject(this, null), dp, ref entry))
        {
            return;
        }
    }
    else
    {
        object source;
        object implicitValue = FrameworkElement.FindImplicitStyleResource(this, this.GetType(), out source);
        if (implicitValue != DependencyProperty.UnsetValue)
        {
            // This style has been fetched from resources
            HasImplicitStyleFromResources = true;

            entry.BaseValueSourceInternal = BaseValueSourceInternal.ImplicitReference;
            entry.Value = implicitValue;
            return;
        }
    }

    // ...
}

所以你Style没有被应用,因为它只是为MenuItem班级设计的,因为它不是一个主题Style。而且您有两种方法可以更改'sStyle中的项目,它们都有缺点。TextBoxContextMenu

第一种方法是Style为所有TextBoxes 添加一个并在其中设置你的ContextMenu. 但是,MenuItems如果分别使用文本服务框架和拼写检查,您将失去重新转换和拼写。

<Style TargetType="{x:Type TextBox}">
    <Setter Property="ContextMenu">
        <Setter.Value>
            <ContextMenu>
                <MenuItem Command="ApplicationCommands.Copy" />
                <MenuItem Command="ApplicationCommands.Cut" />
                <MenuItem Command="ApplicationCommands.Paste" />
            </ContextMenu>
        </Setter.Value>
    </Setter>
</Style>

第二种方法是在启动时使用反射StyleTextEditorContextMenu.EditorMenuItem类创建一个。但仅当您使用拼写检查和文本服务框架时才应使用此方法。

// Inside the Application.OnStartup method
Style menuItemStyle = TryFindResource(typeof(MenuItem)) as Style;
if (menuItemStyle != null)
{
    Assembly menuItemAssembly = typeof(MenuItem).Assembly;
    Type editorMenuType = menuItemAssembly.GetType("System.Windows.Documents.TextEditorContextMenu+EditorMenuItem", false);
    if (editorMenuType != null)
    {
        Resources.Add(editorMenuType, menuItemStyle);
    }

    Type reconversionMenuType = menuItemAssembly.GetType("System.Windows.Documents.TextEditorContextMenu+ReconversionMenuItem", false);
    if (reconversionMenuType != null)
    {
        Resources.Add(reconversionMenuType, menuItemStyle);
    }
}
于 2016-01-24T11:12:14.177 回答