如何在不覆盖派生样式的情况下,将样式应用于自定义控件项中的 WPF?

4
我正在基于通用Windows搜索栏 (我的版本),在wpf中构建一个自定义控件搜索框。在Windows实现中,按钮的背景和前景颜色会根据其是否被按下而改变。为此,我使用了一个样式和触发器来改变按钮的颜色。
由于我正在使用MaterialDesign,我的自定义控件包含一个按钮,该按钮使用派生/默认的Material Design按钮。当将样式应用于此按钮时,Material Design样式会被覆盖以取代默认的wpf按钮。如何在保持基本的Material Design样式的情况下应用样式?
由于我的问题仅与将样式应用于按钮有关,因此我省略了搜索框样式,并创建了一个示例来说明我的问题,其中只包含一个按钮。 Generic.xaml-CustomButton
 <Style TargetType="{x:Type local:CustomButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomButton}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <!-- Normally the below button would be inside a grid with additional buttons, labels and content presenter. I have removed these for simplicity-->
                        <Button Name="MaterialButton"
                                Content="CustomButton">
                            <Button.Style>
                                <Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type  Button}}">
                                    <Style.Triggers>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter Property="Foreground" Value="Red"/>
                                        </Trigger>
                                    </Style.Triggers>
                                </Style>
                            </Button.Style>
                        </Button>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

App.xaml - (Material Design在资源字典中。)

 <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>

我尝试过的方法

我尝试使用建议的BasedOn属性这里这里,但这并没有解决我的问题。

根据"Material Design theme is lost" FAQ中基于材料风格派生的结果会导致以下错误: 异常:找不到名为“MaterialDesignFlatButton”的资源。资源名称区分大小写。

Generic.Xaml 尝试更改 CustomButton

<!--Same custon control as before--->
<Button.Style>
    <Style TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignFlatButton}">
        <!--same trigger-->
1个回答

7

看起来您没有安装正确的依赖项。右键单击使用该样式的项目,选择NuGet包管理器。搜索MaterialDesignThemes包并安装它。如果已经安装,则重新安装。这应该可以解决问题。

您其余的做法是正确的。

或者从GitHub存储库中复制原始样式(快照)和所有引用,并将其粘贴到您的App.xaml中。

如果已安装所有依赖项(或已复制原始样式),则使用以下Style将MaterialDesign默认Button样式应用于您的CustomButton

<Style TargetType="local:CustomButton" 
       BasedOn="{StaticResource MaterialDesignFlatButton}" />

更新

现在您已经编辑了问题并提供了必要的细节,我可以告诉您出了什么问题。

您不只是扩展了Button,还覆盖了扩展Button的默认Style。这样做需要在Generic.xaml文件中定义一个默认的Style。由于默认资源的解析方式,该文件不允许合并字典。只允许对Generic.xaml中相同ResourceDictionary中的资源进行引用。

为了解决这个问题,您需要明确地重写默认的Style。覆盖的Style必须在MaterialDesignFlatButton资源的范围内,否则引用将无法解析并导致您之前发布的错误消息。以下示例在App.xaml 中定义了覆盖 Style:

  <Style TargetType="CustomButton" 
         BasedOn="{StaticResource MaterialDesignFlatButton}">
    <Setter Property="OverridesDefaultStyle" Value="True" />
  </Style>
</ResourceDictionary>

备注

由于您显然不感兴趣默认的Style,因此应将此默认Style中的详细信息(例如ControlTemplate)合并到重写Style中。 然后可以从Generic.xaml中删除默认的Style。接下来,您还需要从自定义控件(例如CustomButton)的static构造函数中删除以下行:

DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), 
  new FrameworkPropertyMetadata(typeof(CustomButton)));

在将现已过时的 CustomButton 的默认样式合并到覆盖样式 Style 中后,新的覆盖样式 Style 可能看起来像这样:

App.xaml

<Style TargetType="local:CustomButton"
       BasedOn="{StaticResource MaterialDesignFlatButton}">
  <Setter Property="OverridesDefaultStyle" Value="True" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:CustomButton}">
        <Border Background="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}">
          <ContentPresenter />
        </Border>

        <ControlTemplate.Triggers>
          <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Foreground" Value="Red" />
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

感谢您建议使用原始样式,如果找不到简单的解决方案,我会使用它。MaterialDesignThemes似乎已经正确安装并且在未应用样式时按预期工作。问题出现在将样式应用于自定义控件中的项目时 - 该项目默认为wpf主题。我可以覆盖自定义控件外部的项目,并且样式和Material Design主题都按预期应用。 - DaemonFire
我不明白。您发布了一个错误消息,指出样式“MaterialDesignFlatButton”无法解析。这就是为什么我写信告诉您应该查找并安装MaterialDesignThemes包的原因。因此,除了您的“CustomButton”之外,所有样式都会隐式地应用于整个应用程序?确保您正在使用正确的“Style”。它必须是<Style TargetType="local:CustomButton" BasedOn="{StaticResource MaterialDesignFlatButton}" />。尝试这个样式。我会更新我的答案。 - BionicCode
抱歉造成混淆,明确的覆盖是我尝试解决问题的例子。我已经编辑了我的答案以帮助澄清这一点。Material Design样式是整个应用程序范围内的,包括我自定义控件中的项目。当我将样式应用于自定义控件内的项目(在本例中为“MaterialButton”)时,它会失去其材料设计主题,而变为默认的wpf。我正在尝试在应用样式的同时保持Material Design主题。这可以在页面或窗口等自定义控件之外完成,但这个问题只出现在自定义控件中。 - DaemonFire
你最新的更新为黑暗中的问题带来了一些启示。我不知道你正在尝试在_Generic.xaml_中合并这个资源。我怎么会知道呢?现在你提到了这个,我立刻理解了你的问题。我已经更新了我的答案来解决这个新情况。_Generic.xaml_不允许合并字典作为引用。所有引用的资源必须在_Generic.xaml_的ResourceDictionary中定义。一直以来,我都期望你在_App.xaml_中定义Style。请尝试我的答案,并让我知道是否解决了你的问题。 - BionicCode
我还意识到你的CustomButtonControltemplate相当无意义。没有必要在按钮内部放置一个按钮。因此,我删除了这个多余的内部Button。通常您会在模板中添加一个简单的ContentPresenter。这个ContentPresenter将自动绑定到ContentControl.Content属性以呈现内容值。请检查并尝试我的最新更新。 - BionicCode
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接