Skip to content

Proposal: Improve the XAML styling system to target template attributes and states #279

@kikisaints

Description

@kikisaints

Proposal: Improve the XAML styling system to target template attributes and states

Summary

Provide a simplier styling system within XAML that enables a user to customize animations, template properties and attributes, and visual states without the need to re-template or copy numerous resources to a dictionary.

Rationale

Styling or customizing XAML controls today, outside of properties on a control, is very dependent on the developer's knowledge of the control's template and our framework. Although having a deep knowledge of our system isn't a bad thing, it can lead to some unwanted workarounds when it comes to customization and branding our controls.

There are two major sticking points that developers can run into when customizing our controls:

  • Desire to change visuals during a state (colors, attributes, ContentPresenter properties, etc.)
  • Need to alter default template attributes (spacing, corner radius, fixed widths, etc.)

To do either of these the above customization, a developer would need to maintain ResourceDictionary(ies) with hundreds of resources (if changing colors and/or control properties), or take complete ownership of the control through re-templating (if changing attributes).

Having numerous resources to maintain during design changes is a lot of developer cost and overhead, while the re-templating option means a higher risk of breaking high contrast as well as tedious, file diff changes when we roll out new updates to our default control templates.

With so many ways to customize a control, it's almost impossible for us to enable a property for each permutation that a developer could need. Therefore, it would make more sense to simply improve our current styling system to enable more versatility.

Functional Requirements

# Feature Priority
1 Easily access control template properties, components and attributes within a style Must
2 Customize controls based on the template markup, not through resource keys or APIs Must
3 Gracefully fallback or throw exceptions when template attribute markup has changed Must
4 Anything not specified in the (non-template) style will default to their generic.xaml definition(s) Must
5 Can target any visual state within a style Must
6 Animations can be added, removed, or changed within a style Must
7 Does not remove or make obsolete today's method of styling, instead offering a lighter weight alternative Must
8 Intellisense supports styling system Should

Important Notes

Let's look at common styling scenarios and how we might be able to change the way we can style them.

Styling a Control Locally

Styling a control is simplified and it's easy to target the named parts within that control's template.

<RadioButton Content="RadioButton">
    <RadioButton.Style>
        <Style TargetType="RadioButton">
            <Setter Path="Background" Value="White"/>
            <Setter TargetName="CheckOuterEllipse" Property="Fill" Value="Green"/>
            <Setter TargetName="CheckOuterEllipse" Property="Width" Value="18"/>
       </Style>
   </RadioButton.Style>
</RadioButton>

Changing Properties on Visual States

When a developer wants properties to changed only within a state, they can target that state directly without needing to specify the entire VisalState tree/manager.

<RadioButton Content="RadioButton">
    <RadioButton.Style>
        <Style TargetType="RadioButton">
            <VisualState x:Name="PointerOver">
                <Setter Path="Background" Value="White"/>       
            </VisualState>

            <Setter TargetName="CheckOuterEllipse" Property="Fill" Value="Green"/>
            <Setter TargetName="CheckOuterEllipse" Property="Width" Value="18"/>          
        </Style>
    </RadioButton.Style>
</RadioButton>

Since the style defined above has no states for Pressed and Disabled, the button control in this case will use it's generic.xaml definitions for those states, and the PointerOver definition specified above for hover.

The more states defined in the style, the less is being defaulted to in generic.xaml.

Styles at a Page-Level

This is the same behavior today and I am calling it out as something we will maintain with this new styling system as well.

Just as you can define styles and templates at a page-level, this new styling system can also be defined there.

<Page.Resources>
    <Style TargetType="RadioButton">
        <Setter Path="Background" Value="White"/>
        <Setter TargetName="CheckOuterEllipse" Property="Fill" Value="Green"/>
        <Setter TargetName="CheckOuterEllipse" Property="Width" Value="18"/>         
    </Style>
</Page.Resources>

Specifying Animations

Animations specified using the improved styling method.

<Style TargetType="Button">
    <VisualState x:Name="PointerOver">
        <Storyboard>
            <PointerUpThemeAnimation Storyboard.TargetName="ContentPresenter" />
        </Storyboard>
    </VisualState>

    <Setter Path="Background" Value="White"/>
    <Setter TargetName="ContentPresenter" Property="CornerRadius" Value="4"/>
</Style>

Styling in Code-Behind

This new styling method would also modify how we style controls from code-behind. Although similar to what can be done today, there would need to be a few changes to account for visual states and targeting template attributes.

//Create style and add a PointerOver state using a previously defined storyboard and setters
Style style = new Style(typeof(AppBarButton));
style.Setters.Add(new Setter(AppBarButton.ContentViewbox.Height, "14"));
style.VisualStates.Add(new VisualState("PointerOver")
    { Setters = mySetterBaseCollection, Storyboard = myStoryboard });

//Add that style to container (or page) resources
rootGrid.Resources.Add(typeof (AppBarButton), style);

Intellisense Support

Intellisense will also scan and pick up the named parts within a control's template and display them in a Setter's property option dropdown. This would remove the need to dig through generic.xaml and would greatly increase the speed of app development and productivity.

image

Using this Path attribute will also allow "dotting" into the named element within that template and accessing it's properties directly.

image

Open Questions/Issues

  • In this model, when we do change our default template attributes, the styles relying on those x:Key names will no longer be valid.
    Even if we do fallback gracefully and intellisense detects which attributes don't exist anymore, it could still be a bit tedious for the developer to update those attribute names individually.
  • This includes a major change to our VisualStates and how they're architected.
    VisualStates as they are today were built to help immensely with VisualStudio's tooling features, something we would not want to regress if we were to modify how and where VisualStates can be defined.
  • How would this work or use XAML direct?

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions