Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
205 views
in Technique[技术] by (71.8m points)

c# - WPF Styles/Template inheritance

I'm trying to learn WPF at the moment, and looking into making the default .Net control look different through the use of style. Using C# as my preferred language although all the code below is WPF markup.

I setup gmail today with the new theme, (see image below) and thus set my self the challenge, could be done in WPF.

New GMail buttons

What I have managed to achieve is to create the middle button Spam through the use of a style with a control template and triggers.

The right and left buttons are very similar but with only 2 differences. They have a corner radius of 1 and margin of 15 on the left or right sides, whilst the middle button has them both set to 0.

Questions!

Q1. Rather than copying the entire style and changing just those 2 attributes, can it be done via some type of inheritance. Where by the right and left buttons are based on an existing style but it makes those 2 visual changes. I have already tried the BasedOn property when creating a new style but was unable to edit the attributes needed.

Q2. Are styles the right way to address this problem in WPF. In WinForms you would look to create a custom control, which has a visible property linked to an enum, i.e. you click on the button and the style options maybe Left, Middle, Right.

Q3. The most difficult question til last. Is it possible to make it, so if a button has my style applied to it. Then when you set its background colour to say blue. Then the button maintains the gradients but instead of them been off-white they are now a shade of blue. i.e. the background linear gradient brush is based on, rather than overwrites the background colour that has been applied to the button. Or do these need to have separate styles defined. I personally cannot see without some type of code behind that this could be achieved, that is making gradient brushes from a single brush in WPF markup.

i.e. buttons as below a blue button and a grey/normal button

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. If you spot any beginner mistakes in the WPF above, feel free to point them out to me.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I've done this in the past by defining an attached property called ExtendedProperties.CornerRadius. I can then set it in my style:

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

And use it from within the template:

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

I might then override it locally in the same way I override any other property:

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

In my case, this gives me (obviously, my theme is dark):

enter image description here

And just by tweaking a couple of my theme's attached properties, I created this effect:

enter image description here

The advantage of this approach is that there's no need to subclass Button. You can also use that same attached property to define corner radii for other controls (such as TextBox). And you're not limited to just corner radius, of course. You could define attached properties for all sorts of things specific to your theme that aren't present on base controls.

The disadvantage is that it's an attached property and thus harder to discover. Documenting your theme will help in this respect.

So, to answer your specific questions:

Q1. Yes, see my answer above. You can either override locally or define new styles that override the property.

Q2. It's a gray area. In my opinion, if it's purely visual (not behavioural) then styles are the way to go. Of course, if it gets out of hand you may instead wish to subclass all the built-in controls and add your specific properties. But that makes your theme harder to re-use and your application more onerous to develop (because you need to use your control set rather than the standard ones).

Q3. I'd say it's possible in code, but not intuitive to use as a control consumer. I think you'd be better off defining extra attached properties - eg. ExtendedProperties.HoverBackground, ExtendedProperties.PressedBackground - and using those from your template in exactly the same way. Then consumers of your control then have more control over the brushes used when your control is in its various states. I've done this in the past but have used more generic property names (SecondaryBackground, TernaryBackground) so I can reuse those properties in other contexts. Again, documenting your theme is helpful.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...