【问题标题】:template property setter for use in two triggers用于两个触发器的模板属性设置器
【发布时间】:2019-03-17 09:55:28
【问题描述】:

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

<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>

像这样:

            <Style TargetType="MenuItem" BasedOn="{StaticResource MaterialDesignMenuItem}">
                <Setter Property="Padding" Value="4 0"></Setter>
            </Style>

我还没有找到,是如何定义两个 TopLevel... 触发器,以便可以覆盖它们的公共填充值 "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>
...

然后覆盖:

            <Style TargetType="MenuItem" BasedOn="{StaticResource MaterialDesignMenuItem}">
                <Setter Property="Padding" Value="4 0"></Setter>
                <Setter TopPadding="Padding" Value="4 0"></Setter>
            </Style>

当然,MenuItem 上没有“TopPadding”属性,所以这不会编译。

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

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

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

重要提示:我不想要使用 Tag 属性的 hack;我想要一种可扩展的方法,以某种方式添加适当的属性或资源。


更新

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

【问题讨论】:

    标签: wpf triggers controltemplate


    【解决方案1】:

    不工作的尝试,使用来自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, ..) 上的默认值和样式设置器 &lt;Setter Property="wpf:ThemeAssist.TopPadding" Value="16 0 16 0"/&gt; 上的值都没有任何效果。

    重要的是,最终样式的&lt;Setter Property="Padding" Value="24 0 24 0"/&gt; 确实会被触发器覆盖;似乎触发器没有找到附加属性,因此已将 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" 很奇怪,但错误消息显示一个空白值''。将字符串应用到厚度转换器失败?

    【讨论】:

      猜你喜欢
      • 2013-08-30
      • 1970-01-01
      • 1970-01-01
      • 2012-02-15
      • 2013-04-09
      • 1970-01-01
      • 1970-01-01
      • 2012-11-23
      • 1970-01-01
      相关资源
      最近更新 更多