WPF样式/模板继承

时间:2011-08-15 20:26:27

标签: c# wpf user-interface styles controltemplates

我正在尝试学习WPF,并试图通过使用样式使默认.Net控件看起来不同。使用C#作为我的首选语言,尽管以下所有代码都是WPF标记。

我今天使用新主题设置了gmail(见下图),从而让我自己成为挑战,可以在WPF中完成。

New GMail buttons

我设法实现的是通过使用带有控件模板和触发器的样式来创建中间按钮Spam

右侧和左侧按钮非常相似,但只有2个不同。它们的左侧或右侧的圆角半径为1,边距为15,而中间的按钮则将它们都设置为0。

的问题!

Q1。可以通过某种类型的继承来完成,而不是复制整个样式并只更改这两个属性。右侧和左侧按钮的位置基于现有样式,但它会进行2次视觉更改。我在创建新样式时已尝试过BasedOn属性,但无法编辑所需的属性。

Q2。样式是解决WPF中此问题的正确方法。在WinForms中,您将创建一个自定义控件,该控件具有链接到枚举的可见属性,即您单击按钮,样式选项可能是左,中,右。

Q3。最难解决的问题。是否可以制作它,所以如果一个按钮应用了我的风格。然后当您将其背景颜色设置为蓝色时。然后按钮保持渐变但不是它们是灰白色,而是现在是蓝色的阴影。即,背景线性渐变画笔基于而不是覆盖已经应用于按钮的背景颜色。或者这些需要定义单独的样式。我个人看不到没有某些类型的代码可以实现这一点,即在WPF标记中使用单个画笔制作渐变画笔。

即。按钮如下方的蓝色按钮和灰色/普通按钮

Google buttons 2

myStyle的

<Style x:Key="GoogleMiddleButton" TargetType="{x:Type Button}">
        <Setter Property="Background">
            <Setter.Value>
                <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                    <GradientStop Color="#F1F1F1" Offset="0"/>
                    <GradientStop Color="#F5F5F5" Offset="1"/>
                </LinearGradientBrush>
            </Setter.Value>
        </Setter>
        <Setter Property="Foreground" Value="#666666"/>
        <Setter Property="FontFamily" Value="Arial"/>
        <Setter Property="FontSize" Value="13"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border Name="dropShadowBorder"
                        BorderThickness="0,0,0,1"
                        CornerRadius="1"
                        >
                        <Border.BorderBrush>
                            <SolidColorBrush Color="#00000000"/>
                        </Border.BorderBrush>
                    <Border Name="border" 
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Padding="{TemplateBinding Padding}" 
                    CornerRadius="0" 
                    Background="{TemplateBinding Background}">
                        <Border.BorderBrush>
                            <SolidColorBrush Color="#D8D8D8"/>
                        </Border.BorderBrush>
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" TargetName="border">
                                <Setter.Value>
                                    <SolidColorBrush Color="#939393"/>
                                </Setter.Value>
                            </Setter>
                            <Setter Property="BorderBrush" TargetName="dropShadowBorder">
                                <Setter.Value>
                                    <SolidColorBrush Color="#EBEBEB"/>
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Foreground" Value="#333333"/>
            </Trigger>
            <Trigger Property="IsPressed" Value="True">
                <Setter Property="Background">
                    <Setter.Value>
                        <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                            <GradientStop Color="#F1F1F1" Offset="1"/>
                            <GradientStop Color="#F5F5F5" Offset="0"/>
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>

P.S。如果您发现上面的WPF中有任何初学者错误,请随时向我指出。

3 个答案:

答案 0 :(得分:8)

我过去通过定义名为ExtendedProperties.CornerRadius的附加属性来完成此操作。然后我可以按照我的风格设置它:

<Style TargetType="Button">
    <Setter Property="local:ExtendedProperties.CornerRadius" Value="0"/>
    ...

在模板中使用它:

<Border CornerRadius="{Binding Path=(local:ExtendedProperties.CornerRadius), RelativeSource={RelativeSource TemplatedParent}">

然后我可以在本地覆盖它,就像我覆盖任何其他属性一样:

<Button Content="Archive" local:ExtendedProperties.CornerRadius="5,0,0,5"/>
<Button Content="Span"/>
<Button Content="Delete" local:ExtendedProperties.CornerRadius="0,5,5,0"/>

就我而言,这给了我(显然,我的主题是黑暗的):

enter image description here

只是通过调整我的几个主题的附加属性,我创建了这个效果:

enter image description here

这种方法的优点是不需要子类Button。您还可以使用相同的附加属性为其他控件(例如TextBox)定义角半径。当然,你并不仅限于角落半径。您可以为基本控件中不存在的主题特定的各种事物定义附加属性。

缺点是它是附属物,因此难以发现。记录您的主题将有助于这方面。

所以,回答你的具体问题:

Q1。是的,请参阅上面的答案。您可以在本地覆盖或定义覆盖该属性的新样式。

Q2。这是一个灰色地带。在我看来,如果它纯粹是视觉(不是行为),那么风格就是你要走的路。当然,如果失控,您可能希望继承所有内置控件并添加您的特定属性。但是这会让你的主题更难以重复使用,而你的应用程序更难以开发(因为你需要使用你的控制集而不是标准的控件集)。

Q3。我说它可以在代码中使用,但不能直观地用作控件使用者。我认为你最好定义额外的附加属性 - 例如。 ExtendedProperties.HoverBackgroundExtendedProperties.PressedBackground - 并以完全相同的方式使用模板中的内容。然后,当您的控件处于各种状态时,您的控件的使用者可以更好地控制所使用的画笔。我以前做过这个,但是使用了更多通用的属性名称(SecondaryBackgroundTernaryBackground),所以我可以在其他上下文中重用这些属性。再次,记录您的主题是有帮助的。

答案 1 :(得分:2)

Q1:据我所知,

Q2:我会说样式是要走的路,你当然可以创建自己的类,它来自按钮,并根据左边,中间,右边选择右边的半径。

问题3:应该可以使用自定义值转换器和您自己的风格。

在结论中。在这种情况下,我可能想要将背景渐变和角半径放在周围的堆叠面板上。按钮对文本是透明的。然后你就不必处理各个按钮上的角半径了。

编辑:添加了代码&amp; Q3的风格回答如上。到OP;我不确定这正是你所追求的,但也许这里有一些让你感兴趣的东西。

我解释你的方式是你想要将按钮背景设置为某种颜色,但它应该呈现为基于该颜色的线性渐变。其他海报提到了不透明面具,这根本不是一个坏主意。我想我会展示如何使用自定义值转换器。

我的想法是创建一个自定义值转换,将纯色画笔转换为线性渐变画笔。然后我使用此转换器将按钮背景颜色从纯色画笔转换为线性渐变画笔。

以下是自定义值转换器:

class SolidColorBrushToGradientConverter : IValueConverter
{
    const float DefaultLowColorScale = 0.95F;

    public object Convert (object value, Type targetType, object parameter, CultureInfo culture)
    {
        var solidColorBrush = value as SolidColorBrush;

        if (!targetType.IsAssignableFrom (typeof (LinearGradientBrush)) || solidColorBrush == null)
        {
            return Binding.DoNothing;
        }

        var lowColorScale = ParseParameterAsDouble (parameter);

        var highColor = solidColorBrush.Color;
        var lowColor = Color.Multiply (highColor, lowColorScale);
        lowColor.A = highColor.A;

        return new LinearGradientBrush (
            highColor,
            lowColor,
            new Point (0, 0),
            new Point (0, 1)
            );
    }

    static float ParseParameterAsDouble (object parameter)
    {
        if (parameter is float)
        {
            return (float)parameter;
        }
        else if (parameter is string)
        {
            float result;
            return float.TryParse(
                (string) parameter, 
                NumberStyles.Float, 
                CultureInfo.InvariantCulture, 
                out result
                        )
                        ? result
                        : DefaultLowColorScale
                ;
        }
        else
        {
            return DefaultLowColorScale;
        }
    }

    public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Binding.DoNothing;
    }
}

然后我以我从你那里复制的样式引用它(基本上相同,但我重新组织了一下),重要的行部分是这样的:

Background="{Binding Path=Background,Mode=OneWay,RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource SolidColorBrushToGradientConverter}, ConverterParameter=0.95}"

这意味着我们绑定到模板化的父背景(即Button.Background),我们使用转换器SolidColorBrushToGradientConverter和参数0.95(这决定了“低”颜色应该与“高”颜色相比多深)

完整的风格:

<local:SolidColorBrushToGradientConverter x:Key="SolidColorBrushToGradientConverter" />

<Style x:Key="GoogleMiddleButton" TargetType="{x:Type Button}">
    <Setter Property="Background" Value="#F5F5F5" />
    <Setter Property="Foreground" Value="#666666"/>
    <Setter Property="FontFamily" Value="Arial"/>
    <Setter Property="FontSize" Value="13"/>
    <Setter Property="FontWeight" Value="Bold"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border 
                    Name="dropShadowBorder"                        
                    BorderThickness="0,0,0,1"                        
                    CornerRadius="1"
                    >
                    <Border.BorderBrush>
                        <SolidColorBrush Color="#00000000"/>
                    </Border.BorderBrush>
                    <Border Name="border"                     
                            BorderThickness="{TemplateBinding BorderThickness}"                    
                            Padding="{TemplateBinding Padding}"                     
                            CornerRadius="0"                     
                            Background="{Binding Path=Background,Mode=OneWay,RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource SolidColorBrushToGradientConverter}, ConverterParameter=0.95}"
                            >
                        <Border.BorderBrush>
                            <SolidColorBrush Color="#D8D8D8"/>
                        </Border.BorderBrush>
                        <ContentPresenter 
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"                                   
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                            />
                    </Border>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="BorderBrush" TargetName="border">
                            <Setter.Value>
                                <SolidColorBrush Color="#939393"/>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="BorderBrush" TargetName="dropShadowBorder">
                            <Setter.Value>
                                <SolidColorBrush Color="#EBEBEB"/>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="IsPressed" Value="True">
                        <Setter Property="Background" Value="#4A8FF7" />
                        <Setter Property="Foreground" Value="#F5F5F5" />
                        <Setter Property="BorderBrush" TargetName="border">
                            <Setter.Value>
                                <SolidColorBrush Color="#5185D8"/>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

答案 2 :(得分:1)

我喜欢这个挑战的样子!

Q1:其他评论/答案是正确的,模板无法修改或继承。但是,有一些方法可以将值传递到模板中以修改其外观。一种简单(但稍微有些hacky)的方法是使用Tag CornerRadius属性将边框enter code here传递到模板中。更好的方法可能是子类按钮添加“位置”属性。

Q2:是的,你在风格/模板的正确轨道上

问题3:修改模板以包含具有所需渐变的OpacityMask。然后,您可以在此蒙版后面放置一个元素,或者让蒙版元素自身采用背景颜色。完整示例如下所示:

enter image description here

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
  <Window.Resources>
    <Style x:Key="GoogleButton" TargetType="{x:Type Button}">      
      <Setter Property="Background" Value="White"/>
      <Setter Property="Foreground" Value="#666666"/>
      <Setter Property="Tag">
        <Setter.Value>
          <CornerRadius>0</CornerRadius>
        </Setter.Value>
      </Setter>
      <Setter Property="FontFamily" Value="Arial"/>
      <Setter Property="FontSize" Value="13"/>
      <Setter Property="FontWeight" Value="Bold"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="Button">
            <Border Name="dropShadowBorder"
                    BorderThickness="0,0,0,1"
                    CornerRadius="1"
                    BorderBrush="Transparent"
                    Background="White">
              <Grid>
                <Border Name="backgroundFill" 
                        BorderBrush="Red"
                        Background="{TemplateBinding Background}"
                        CornerRadius="{TemplateBinding Tag}">
                  <Border.OpacityMask>
                    <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                      <GradientStop Color="#FF000000" Offset="0"/>
                      <GradientStop Color="#00000000" Offset="1"/>
                    </LinearGradientBrush>
                  </Border.OpacityMask>
                </Border>
                <Border Name="border" 
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Padding="{TemplateBinding Padding}" 
                    CornerRadius="{TemplateBinding Tag}" 
                    Background="Transparent">
                  <Border.BorderBrush>
                    <SolidColorBrush Color="#D8D8D8"/>
                  </Border.BorderBrush>
                  <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
              </Grid>
            </Border>            
            <ControlTemplate.Triggers>
              <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="BorderBrush" TargetName="border">
                  <Setter.Value>
                    <SolidColorBrush Color="#939393"/>
                  </Setter.Value>
                </Setter>
                <Setter Property="BorderBrush" TargetName="dropShadowBorder">
                  <Setter.Value>
                    <SolidColorBrush Color="#EBEBEB"/>
                  </Setter.Value>
                </Setter>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Foreground" Value="#333333"/>
        </Trigger>
        <Trigger Property="IsPressed" Value="True">
          <Setter Property="Background">
            <Setter.Value>
              <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                <GradientStop Color="#F1F1F1" Offset="1"/>
                <GradientStop Color="#F5F5F5" Offset="0"/>
              </LinearGradientBrush>
            </Setter.Value>
          </Setter>
        </Trigger>
      </Style.Triggers>
    </Style>
  </Window.Resources>
  <Grid>
    <StackPanel Orientation="Horizontal"
                VerticalAlignment="Top">
      <Button Style="{StaticResource GoogleButton}" Content="Archive">
        <Button.Tag>
          <CornerRadius>2,0,0,2</CornerRadius>
        </Button.Tag>
      </Button>
      <Button Style="{StaticResource GoogleButton}" Content="Spam"
              Background="LightBlue"/>
      <Button Style="{StaticResource GoogleButton}" Content="Delete">
        <Button.Tag>
          <CornerRadius>0,2,2,0</CornerRadius>
        </Button.Tag>
      </Button>
    </StackPanel>
  </Grid>
</Window>
相关问题