Foredecker

Jibe!

Posts Tagged ‘wpf

WPF: Styling the Expander Control

with one comment

I recently needed to style a WPF expander control for a prototype project. Our team’s UX designer came up with a nice visual design.    So, I began to build my prototype and match his visual style (mocked up in Visio) in WPF.   I thought it would be easy…   it was, once I spent all morning  figuring out how to do it…

Here is screen shot of the prototype – it is pretty close to the mock up.

ScreenShot1

I started off with the Expander Control Template example on MSDN.  This wasn’t a bad start – it showed me how to change expander toggle button – nice.

But, I also needed to change the font used in the expander header.  That wasn’t shown in the example.   Ok, no problem – just use a setter in the style like this – right?

    <Style x:Key="MainViewExpander" TargetType="Expander">
        <Setter Property="FontSize" Value="18" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Expander">

Example1Bzzzztttt… wrong.   That set the font appearance in the Expander header like I wanted, but all the controls in the Expander content property inherited the font appearance.  That’s not what I wanted at all.

I needed to style the appearance of the Expander header and its contents separately.

So, i dug around in MSDN a little more.  The key information turned out to be in the documentation for the Headered Content Control class.

This is an important class as it defines the base functionality for controls including the Expander, Group Box and TabItem controls – all very commonly used controls.

They key to getting the look I wanted is using Data Templates.   I’ve seen this used in many MSDN examples and WPF blog posts but until now, I never really understood them.

In short, data templates are like control templates but instead of defining how WPF structures the visual tree for a control, data template defines how WPF displays data.

We’ll, you might say “Duh Richard!”  That’s what the docs say….  Ya, Duh! For sure…  I didn’t grok exactly what this meant until now: they key for me was finally understanding that the data template is bound to the data it is intended to display.

All data template intentions are bound to a property on an object instance.   You can refer to this binding by using the simple most binding which is “{Binding}”.  You can see how this is used below.

The key is that all Headered Content Controls have a data template for the header content.    So, setting the font appearance for the Expander header was a simple matter of adding this setter to my style


        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <TextBlock
                        Text="{Binding}"
                        FontSize="18"
                        FontWeight="Bold"
                        Foreground="White"
                        />
                </DataTemplate>
            </Setter.Value>
        </Setter>

This worked great!  As you can see in the screen shot at the beginning of the post, I set the text appearance to the white, 18 size bold text I wanted.   Most importantly, the appearance of the content was not affected by the header’s appearance.

Now, in one case, I do want to determine the font appearance of all the children in an Expander’s content property.   As you can seen in the screen shot, the commands are in a larger, bold font style.

To accomplish this, I simply created a style derived from the main expander style -like this:

    <Style x:Key="MainViewExpanderCommands"
           BasedOn="{StaticResource MainViewExpander}"
           TargetType="Expander"
           >
        <Setter Property="FontSize" Value="20" />
        <Setter Property="FontWeight" Value="Bold" />
    </Style>

Then, in the MainView.xaml file, I used the derived style like this:

        <Expander
            Header="Commands"
            Grid.Column="0"
            IsExpanded="True"
            Style="{StaticResource                   MainViewExpanderCommands}"
            >
            <StackPanel>
                <Label Content="Open"    Foreground="#FFB4B4A9" />
                <Label Content="Save"    Foreground="#FFB4B4A9"/>
                <Label Content="Save As" Foreground="#FFB4B4A9"/>
                <Label Content="Close"   Foreground="#FFB4B4A9"/>
            </StackPanel>
        </Expander>

This worked fine – mostly.  If you look, you will see that each of the labels has the foreground set to a grey color.   I tried to do this in the derived style, bit alas, this didn’t work because the the intervening stack panel (which is the content for the expander) does not have the foreground property, so it is not inherited.

Here is the source code download

This code builds with the release candidate version of Visual Studio 2010.   It does not need any additional components (like NUnit).

Installing and building the project is easy:   Just download and run the installer.   It will put the project on your desktop.  Note, it doesn’t really ‘install’ anything – there are no shortcuts and it doesn’t write to the registry.  You can move the sample directory wherever you would like, or just delete it when you are done.  No un-installing is needed.   The contents are licensed with the Microsoft Public License.

Credit – I’d like to thank Josh Smith for his excellent examples of using the MVVM design pattern.   All my new WPF projects are MVVM and I learned the ropes from his blog posts.  The MVVM classes in this example are inspired by Josh’s MSDN article here.   I’m still a noob with both WPF and MVVM – the learning curve is very steep, and long.  But Josh’s blog and articles have been hugely helpful (as have many others…)

Advertisements

Written by foredecker

March 28, 2010 at 5:48 pm

Posted in Coding

Tagged with ,

BizzySpinner 2 – A WPF Spinning Busy State Indicator (with source)

with 4 comments

I’ve made a key improvement to my BizzySpinner control form my last post.  It now starts and stop smoothly instead of abruptly.  This is quite visually attractive.  But it was somewhat non-trivial to do.

In my first example, I had a single simple animation that was either running or stopped.  The animation behavior is now like this:

BizzySpinnerAcceleration

I tried to do this all in XAML but it proved quite complex to handle in in that language.   This was complicated as there are two key dependency properties to deal with that leads to several states: The Spin property controls the actually spinning state.   And the IsEnabled property inherited from UIElement.  Both must work correctly together.

This gave me two significant problems:

  1. The XAML was really complicated and difficult to read.  I don’t know about you, but to me, XAML is very verbose and difficult to read.  Syntax highlighting helps, but only to a degree.   There is also no such thing as a XAML debugger so a lot of trial and error is involved with large amounts of XAML.   Kaxaml is really helpful, but this was hard even with that tool.
  2. I couldn’t get a fully correct state machine in XAML: No matter what I tried I could toggle the IsEnabled and Spin dependency properties and cause the spinner to “jump” unattractively.

BizzySpinnerAcceleration-Page2 So, I simply used C# code to do this.   This approach was much simpler.   It was easy to implement the two cooperating state machines needed to implement all cases.   The spinning state machine is shown to the right.   It has four states and six transitions – not at all difficult to implement.

This state machine is implemented in the ControlSpinning() function on line 432 in BizzySpinner.xaml.cs.

The transitions caused by the SpinUp and SpinDown values are straight forward and implemented using a couple of switch statements

The transitions labeled Acceleration Complete and Deceleration Complete are more interesting:  they are implemented using the Completed event on the DoubleAnimation using to spin the control.

For example when the spinner goes form the Not Spinning state to the Accelerating state, the code simply attaches a function to the animation’s Completed event like this:

Code Snippet
  1. spinAnimation.From = SpinAngle;
  2. spinAnimation.To = (SpinAngle + (OneRotation / 8));
  3. spinAnimation.Duration = new Duration(TimeSpan.FromSeconds(SpinRate/4));
  4. spinAnimation.DecelerationRatio = 0.0;
  5. spinAnimation.AccelerationRatio = 1.0;
  6. spinAnimation.Completed += SpinContinuously;
  7. theSpinState = SpinState.Accelerating;
  8. this.BeginAnimation(SpinAngleProperty, spinAnimation);

This is the code that causes the spinner to accelerate to its normal spinning rate.  The key line is #7.   When the spinner has finished accelerating the SpinContinuously member is called.  It looks like this:

Code Snippet
  1. void SpinContinuously(object sender, EventArgs e)
  2. {
  3. spinAnimation.Completed -= SpinContinuously;
  4. spinAnimation.From = SpinAngle;
  5. spinAnimation.To = SpinAngle + OneRotation;
  6. spinAnimation.Duration = new Duration(TimeSpan.FromSeconds(SpinRate));
  7. spinAnimation.DecelerationRatio = 0.0;
  8. spinAnimation.AccelerationRatio = 0.0;
  9. spinAnimation.RepeatBehavior = RepeatBehavior.Forever;
  10. theSpinState = SpinState.Running;
  11. this.BeginAnimation(SpinAngleProperty, spinAnimation);
  12. }

Note line 3 which disconnects the SpinContinuously from the animations Completed event.   This is super important as we don’t want this called again because the next state transition when the animation completes is a different one.

The other important state machine is the one that handles the IsEnabled and Spin dependency properties.   This is what sends the SpinUp and SpinDown commands to the spinning state machine.

BizzySpinnerAcceleration-Page3

This is simpler than it looks:  there are only four important transitions – the ones in green where the SpinUp and SpinDown commands are sent to the spinning state machine.

This is all handled easily in the IsEnabledChanged event handler like this:

Code Snippet
  1. private void IsEnabledChangedHandler(Object sender, DependencyPropertyChangedEventArgs e)
  2. {
  3. if ( !(bool)e.NewValue)
  4. {
  5. //
  6. // Going enabled
  7. //
  8. Background = BackgroundBrushSave;
  9. LeaderBrush = LeaderBrushSave;
  10. TailBrush = TailBrushSave;
  11. // The control is enabled, turn on spinning if the Spin property is ture
  12. ControlSpinning(Spin ? SpinCommand.SpinUp : SpinCommand.SpinDown);
  13. } else {
  14. //
  15. // Going disabled
  16. //
  17. if (theSpinState == SpinState.NotSpinning)
  18. {
  19. SetDisabledBrushes();
  20. }
  21. else
  22. {
  23. ControlSpinning(SpinCommand.SpinDown);
  24. }
  25. }
  26. }

The resulting animation is very pleasing – the control spins up and down smoothly.   Its rotational rate can also be controlled dynamically.

You will need NUnit 2.5.3 (download) as I’ve added assertions. The code builds for C# 3.0 and .NET 3.5 using the client profile.  As with the last post, the project is for the Visual Studio 2010 BETA which you can download for free..

Installing and building the project is easy:   Just download and run the installer.   It will put the project on your desktop.  Note, it doesn’t really ‘install’ anything – there are no shortcuts and it doesn’t write to the registry.  You can move the sample directory wherever you would like, or just delete it when you are done.  No un-installing is needed.   Also note that the contents are licensed with the Microsoft Public License.

Here is the source code download

Written by foredecker

January 11, 2010 at 3:49 am

Posted in Coding

Tagged with ,