用于两个触发器的模板属性设置器

时间:2019-03-17 09:55:28

标签: wpf triggers controltemplate

WPF。考虑以下带有模板的样式,以及它如何影响MenuItem上的填充。 (这些是与填充相关的行,是从http://materialdesigninxaml.net/的github中MaterialDesignTheme.Menu.xaml的{​​{1}}中的样式中提取的。)

MaterialDesignThemes.Wpf

很容易覆盖默认的填充线

<Style TargetType="{x:Type MenuItem}" x:Key="MaterialDesignMenuItem" BasedOn="{x:Null}">
    <Setter Property="Padding" Value="24 0"></Setter>
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type MenuItem}">
                <ControlTemplate.Resources>
                </ControlTemplate.Resources>
                <Grid ClipToBounds="True">
                </Grid>
                <ControlTemplate.Triggers>
                    <!--#region Roles Triggers -->
                    <Trigger Property="Role" Value="TopLevelHeader">
                        <Setter Property="Padding" Value="16 0"/>
                    </Trigger>
                    <Trigger Property="Role" Value="TopLevelItem">
                        <Setter Property="Padding" Value="16 0"/>
                    </Trigger>
                    <!--#endregion-->
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

像这样:

<Setter Property="Padding" Value="24 0"></Setter>

我一直无法找到的是如何定义两个TopLevel ...触发器,以便可以覆盖它们的公共填充值 <Style TargetType="MenuItem" BasedOn="{StaticResource MaterialDesignMenuItem}"> <Setter Property="Padding" Value="4 0"></Setter> </Style>

在实际的代码中,这些触发器的作用更大,所以我不想完全覆盖触发器。我必须重复其余的触发代码。

我想让原始样式为这些样式指定一个默认值,我可以覆盖它。像这样:

"16 0"

然后覆盖:

<Style TargetType="{x:Type MenuItem}" x:Key="MaterialDesignMenuItem" BasedOn="{x:Null}">
    <Setter Property="Padding" Value="24 0"></Setter>
    <Setter Property="TopPadding" Value="16 0"></Setter>
...

但是,当然,MenuItem上没有“ TopPadding”属性,因此无法编译。

如何在原始样式中创建此新属性,在模板中(在两个触发器中)引用它,然后以自定义样式覆盖它?

可能相关的答案:
Bind to resource keys on each item
Bind to attached property

最坏的情况是,我可以将MenuItem子类化,并制作一个仅适用于该子类的模板(添加“ TopPadding”属性)。我不想这样做,因为它是复杂库的一部分。我想要一个可以与任何MenuItem一起使用的模板。我认为这意味着任何附加属性都必须是可选的(MenuItem可能没有此属性。)我不确定模板如何引用附加属性,也不确定如何设置默认属性。

重要提示:我不希望使用 <Style TargetType="MenuItem" BasedOn="{StaticResource MaterialDesignMenuItem}"> <Setter Property="Padding" Value="4 0"></Setter> <Setter TopPadding="Padding" Value="4 0"></Setter> </Style> 属性的骇客;我想要一种可扩展的方法,以某种方式添加适当的属性或资源。


更新

我刚发现this answer;我没有意识到不必在原始类中定义附加属性-可以在不继承MenuItem的情况下定义附加属性。
我不清楚是否可以设置 default 值,以防丢失此附加属性,但是我现在正在尝试这样做。

1 个答案:

答案 0 :(得分:0)

不工作,尝试使用this answer中的想法:

在该库的c#类中定义附加属性:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace MaterialDesignThemes.Wpf
{
    public static class ThemeAssist
    {
        // ...

        #region TopPadding AttachedProperty
        public static DependencyProperty TopPaddingProperty =
            DependencyProperty.RegisterAttached("TopPadding",
                typeof(Thickness),
                typeof(ThemeAssist),
                new PropertyMetadata(new Thickness(12, 0, 12, 0)));
        public static void SetTopPadding(DependencyObject obj, Thickness value)
        {
            obj.SetValue(TopPaddingProperty, value);
        }
        public static Brush GetTopPadding(DependencyObject obj)
        {
            return (Brush)obj.GetValue(TopPaddingProperty);
        }
        #endregion
    }
}

尝试在模板中使用该附加属性:

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf"
                    xmlns:converters="clr-namespace:MaterialDesignThemes.Wpf.Converters">

    <!-- ... -->

    <Style TargetType="{x:Type MenuItem}" x:Key="MaterialDesignMenuItem" BasedOn="{x:Null}">
        <Setter Property="Padding" Value="24 0"/>
        <Setter Property="OverridesDefaultStyle" Value="True"/>
        <Setter Property="wpf:ThemeAssist.TopPadding" Value="16 0 16 0"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type MenuItem}">
                    <ControlTemplate.Resources>
                    </ControlTemplate.Resources>
                    <Grid ClipToBounds="True">
                    </Grid>
                    <ControlTemplate.Triggers>
                        <!--#region Roles Triggers -->
                        <Trigger Property="Role" Value="TopLevelHeader">
                            <!-- <Setter Property="Padding" Value="16 0"/> -->
                            <Setter Property="Padding" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ThemeAssist.TopPadding)}"/>
                        </Trigger>
                        <Trigger Property="Role" Value="TopLevelItem">
                            <!--<Setter Property="Padding" Value="16 0"/>-->
                            <Setter Property="Padding" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ThemeAssist.TopPadding)}"/>
                        </Trigger>
                        <!--#endregion-->
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>


用法(在不同的程序集中):

    <StackPanel DockPanel.Dock="Top">
        <StackPanel.Resources>
            <Style TargetType="MenuItem" BasedOn="{StaticResource MaterialDesignMenuItem}">
                <Setter Property="Padding" Value="24 0 24 0"/>
                <!--<Setter Property="wpf:ThemeAssist.TopPadding" Value="12 0 12 0"/>-->
                <Setter Property="Background" Value="Aqua"></Setter>
            </Style>
        </StackPanel.Resources>
        <Menu x:Name="MainMenu" IsMainMenu="True" Visibility="Collapsed">
            <MenuItem Header="_File">

错误结果:TopLevelHeader / TopLevelItem MenuItem填充始终为(0,0,0,0)。附加属性定义(12, ..)的默认值或样式设置器<Setter Property="wpf:ThemeAssist.TopPadding" Value="16 0 16 0"/>的值都无效。

重要的是,最终样式的<Setter Property="Padding" Value="24 0 24 0"/>确实被触发器覆盖;似乎触发器未找到附加属性,因此已将Padding重置为0。


如果我在触发器的填充中恢复了原始的"16 0"值,则菜单项会获得该填充。


我还尝试将TopPadding添加到最终样式中,但是没有成功找到在视觉设计器中不会抱怨的语法。 (我仍然运行了该程序,但仍然没有效果。)我尝试的语法:

<UserControl x:Class="GISWPF.Dialogs"
         xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
         ...
>
    ...
            <Style TargetType="MenuItem" BasedOn="{StaticResource MaterialDesignMenuItem}">
                <Setter Property="wpf:ThemeAssist.TopPadding" Value="12 0 12 0"/>
            </Style>

提供设计时工具提示:

'' is not a valid value for the 'MaterialDesignThemes.Wpf.ThemeAssist.TopPadding' property on a Setter.

奇怪的是我说Value="12 0 12 0",但是错误消息显示了空白值''。无法将字符串应用于厚度转换器?