6

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

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

新的 GMail 按钮

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

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

问题!

Q1。与其复制整个样式并仅更改这两个属性,不如通过某种类型的继承来完成。左右按钮的位置基于现有样式,但它使这两个视觉变化。创建新样式时我已经尝试过 BasedOn 属性,但无法编辑所需的属性。

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

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

即按钮如下一个蓝色按钮和一个灰色/正常按钮

谷歌按钮 2

我的风格

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

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

4

3 回答 3

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

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

在此处输入图像描述

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

在此处输入图像描述

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

缺点是它是附属财产,因此更难被发现。记录您的主题将在这方面有所帮助。

因此,要回答您的具体问题:

Q1。是的,看我上面的回答。您可以在本地覆盖或定义覆盖该属性的新样式。

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

Q3。我会说这在代码中是可能的,但作为控制消费者使用并不直观。我认为你最好定义额外的附加属性 - 例如。ExtendedProperties.HoverBackground, ExtendedProperties.PressedBackground- 并以完全相同的方式使用模板中的那些。然后,当您的控件处于各种状态时,您的控件的使用者可以更好地控制使用的画笔。我过去曾这样做过,但使用了更通用的属性名称(SecondaryBackground, TernaryBackground),因此我可以在其他上下文中重用这些属性。同样,记录您的主题很有帮助。

于 2011-08-16T07:48:26.547 回答
2

Q1:据我所知没有

Q2:我会说样式是要走的路,您当然可以创建自己的类,该类派生自按钮并根据左、中、右选择右角半径。

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

总之。在这种情况下,我可能很想将背景渐变和角半径放在周围的堆栈面板上。按钮对文本是透明的。这样您就不必处理单个按钮上的角半径。

编辑:为上面的 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),我们使用参数为 0.95 的转换器 SolidColorBrushToGradientConverter(这决定了“低”颜色应该比“高”颜色更暗多少)。

完整风格:

<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>
于 2011-08-15T21:08:24.123 回答
1

我喜欢这个挑战的样子!

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

Q2:是的,您在样式/模板方面走在了正确的轨道上

Q3:修改您的模板以包含OpacityMask具有您想要的渐变的模板。然后,您可以在此遮罩后面放置一个元素,或者让被遮罩的元素本身采用背景颜色。完整示例如下所示:

在此处输入图像描述

<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>
于 2011-08-16T07:37:22.917 回答