【问题标题】:How to create a default style for a common base class如何为通用基类创建默认样式
【发布时间】:2016-11-26 04:41:03
【问题描述】:

目标

我有一个针对ToggleButton 的自定义控件,它也可以与Button 一起使用。所以我想为这两种类型使用一个通用的默认ControlTemplate

我尝试的策略是在模板中设置TargetType="{x:Type ButtonBase}",如果在ButtonToggleButton 上显式设置,则效果很好。

隐式

自定义控件库

在项目根目录下 Themes 文件夹中名为 Generic.xaml 的资源字典中...

<Style TargetType="{x:Type ButtonBase}">
    <Setter Property="Template">
        <Setter.Value>
            <!--Modified Control Template-->
            <ControlTemplate TargetType="{x:Type ButtonBase}">

在控件的类中,我使用FrameworkElement.DefaultStyleKey在其静态构造函数中设置用户控件类型的元数据...

static ContentToggle()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(ContentToggle),
       new FrameworkPropertyMetadata(typeof(ButtonBase)));
}

使用 WPF 应用项目

App.xaml...

<Application x:Class="Spec.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>

            <!--Get a reference to the window to establish View Context-->
            <RelativeSource x:Key="View" Mode="FindAncestor" 
                        AncestorType="{x:Type Window}" />

            <ResourceDictionary.MergedDictionaries>

                <!--Local Style-->
                <ResourceDictionary Source="pack://application:,,,/ButtonStyle.xaml" />

            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>
    </Application.Resources>
</Application>

MainWindow.xaml...

<Window x:Class="Spec.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:b="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <b:ContentToggle Name="Toggle" Height="30" 
                         Content="{Binding options, RelativeSource={StaticResource View}}"
                         />
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public List<string> options { get; set; }
    public bool initialState { get; set; }

    public MainWindow ()
    {
        options = new List<string> { "Checked", "UnChecked" };

        initialState = false;

        InitializeComponent();
    }
}

还有一个名为 ButtonStyle.xaml 的文件定义了自定义控件要使用的画笔。它通过 App.xaml 中的合并字典在应用的根目录上公开。

结果

ContentToggle instance 的模板为 null,样式控件没有视觉效果(当我窥探该控件时,它没有子元素)。

我的理解是自动ButtonBase 样式/模板将用于我的控制。我错过了什么?

显式

如果在控件上显式声明了样式/模板,则自定义控件将按预期工作。以下适用于样式目标设置为ButtonBase...

使用 WPF 应用项目

在 App.xaml 中...

<Application x:Class="Spec.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>

            <!--Get a reference to the window to establish View Context-->
            <RelativeSource x:Key="View" Mode="FindAncestor" 
                        AncestorType="{x:Type Window}" />

            <ResourceDictionary.MergedDictionaries>

                <!--custom control-->
                <ResourceDictionary Source="pack://application:,,,/ContentToggleButton;component/Themes/Generic.xaml" />

                <!--Local Style-->
                <ResourceDictionary Source="pack://application:,,,/ButtonStyle.xaml" />

            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>
    </Application.Resources>
</Application>

在 MainWindow.xaml...

<Window x:Class="Spec.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:b="clr-namespace:ContentToggleButton;assembly=ContentToggleButton"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <b:ContentToggle Name="Toggle" Height="30" 
                         Content="{Binding options, RelativeSource={StaticResource View}}"
                         Style="{DynamicResource LocalButtonStyle}"
                         />
    </Grid>
</Window>

在 ButtonStyle.xaml...

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

    <!--Custom Button backgrounds-->
    <LinearGradientBrush x:Key="Button.Static.Background" 
                             EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF2C0606" Offset="1"/>
        <GradientStop Color="#E6ADAD"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Button.MouseOver.Background" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF2C0606" Offset="1"/>
        <GradientStop  Color="#FFF2F2"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Button.MouseOver.Checked.Background" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF2C0606" Offset="1"/>
        <GradientStop  Color="#F2FFF3"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="Button.Checked.Background" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF2C0606" Offset="1"/>
        <GradientStop x:Name="GradientStop" Color="#ADE6B1"/>
    </LinearGradientBrush>

    <!--Establish the style colours-->
    <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070" />
    <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1" />
    <SolidColorBrush x:Key="Button.Pressed.Background" Color="#C4F6CE" />
    <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B" />
    <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4" />
    <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5" />
    <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383" />

    <!--Custom Style-->
    <Style x:Key="LocalButtonStyle" TargetType="{x:Type ButtonBase}" BasedOn="{StaticResource ButtonStyle}">
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalContentAlignment" Value="Center" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Background" 
                    Value="{StaticResource Button.Static.Background}"/>
        <Setter Property="BorderBrush" 
                    Value="{StaticResource Button.Static.Border}"/>
    </Style>

</ResourceDictionary>

解决方案结构(两种情况通用)

【问题讨论】:

  • 你在哪里使用ContentToggle控制?
  • 您的ContentToggle 控件派生自ButtonBase ?
  • @AnjumSKhan:是的,基类是ToggleButton
  • 这将对您有所帮助:stackoverflow.com/questions/32956005/…
  • @AnjumSKhan:感谢您的链接,不幸的是它对我没有帮助。从我添加的图片中可以看出,样式已经在 /Themes/Generic.xaml 中,所以我已经有了推荐的结构。与链接案例和我的不同之处在于它们在控件库中创建子类并导出它们。我在消费应用程序中导出基类和子类,因此没有可以放置在 Generic.xaml 文件中的资源。

标签: c# wpf xaml custom-controls


【解决方案1】:

我不完全知道为什么,但是对于 ButtonBase 主题搜索根本不起作用(可能与它是抽象的有关)。即使您尝试使用例如 FindResource 或类似方法为 ButtonBase 找到样式 - 即使您在 Generic.xaml 中自己定义了它,您也不会找到任何东西(对于像 Button 这样的其他控件,您会找到样式)。

现在,为了解决您的特定问题,我认为最简单的方法是为 ContentTogger 定义样式,但为 ButtonBase 定义模板(您可以将其移出单独的资源并重用):

<Style TargetType="{x:Type c:ContentToggle}">
    <Setter Property="Template">
        <Setter.Value>
            <!-- move this template out to separate resource -->
            <ControlTemplate TargetType="{x:Type ButtonBase}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

【讨论】:

  • 在第1点中,“当前主题资源字典”是什么意思?
  • 默认控件的样式取决于当前主题(主题基本上只是一个资源字典,如您的 Generic.xaml) - msdn.microsoft.com/en-us/library/aa970773(v=vs.110).aspx
  • 抱歉,我不清楚:“current 主题资源字典”是什么意思?我明白它们是什么,我只是不明白你所说的当前主题是什么意思:据我所知,当前主题是在引用的自定义控件 dll 中的 generic.xaml 中设置的主题,因为其中没有样式控件、窗口或应用程序资源字典所以,我问的是您所说的 current 样式覆盖了这个?
  • 我认为这个答案很好地解释了它 - stackoverflow.com/a/1232333/5311735。注意“如果在主题字典中找不到样式,则在 Generic.xaml 中查找”。
  • 是的,这就是我的观点。在上面的第一点中,您是说样式优先级存在问题,而据我所知,我的意思是没有竞争优先级。所以,这就是为什么当你说“它不起作用,因为 Button 已经在当前主题中定义了样式。”时,我问你当前主题是什么意思。
【解决方案2】:

在一开始阅读您的问题介绍后,我建议以下方法。这只是一个给出基本想法的示例。

Generic.xaml

<Style x:Key="CommonStyle" TargetType="{x:Type Button}">
    <Setter Property="Background" Value="Yellow"/>
    <Setter Property="FontSize" Value="25"/>
</Style>

<Style TargetType="{x:Type local:CustomButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomButton}">
                <Border Background="{TemplateBinding Background}" CornerRadius="10"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Button Style="{StaticResource CommonStyle}" Content="{TemplateBinding Content}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

CustomButton.cs

public class CustomButton : ToggleButton
{
    static CustomButton()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
    }           
}

看看这是否解决了你的问题,否则我会尝试改进这个答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    • 2023-03-08
    • 1970-01-01
    • 2012-05-09
    • 1970-01-01
    • 1970-01-01
    • 2019-01-31
    相关资源
    最近更新 更多