Blog Archives

WPF: How to Create a Reversing ProgressBar

 

 

In some of my earlier blogs – here, here and here, I looked at some rather colorful, non-rectangular versions of the ProgressBar. Although you probably wouldn't want to use some of those versions outside of a game environment, I hope it did show you how you can create and tweak non-rectangular versions. In this blog, I want to examine a less bizarre variation and along the way hopefully show you some more useful WPF basics.

Reversing the Default ProgressBar
The default ProgressBar is rectangular, has slightly rounded corners, a Gray Background, Linear Gradient Green Foreground and, most importantly, it traverses from left to right. I was wondering about how to change this and have a ProgressBar that traversed from right to left – a kind of a countdown effect instead of the usual fill-increasing effect. Because I was in ControlTemplate mode, I confess that I initially overlooked the obvious solution – total simplicity in WPF – and created a whole new template that did the job. I'll show you the obvious WPF solution shortly, but first let's just look at how this can be done as a ControlTemplate task.

ControlTemplate
I set about this by using a combination of Expression Blend and Visual Studio. I know I could have done it all in Blend using the Edit Template tools, but I came up this WPF route the hard way, from back in the early days when there were no tools and you had to become a XAML-head to get anywhere with this new-fangled thing called WPF. So I find that I still prefer to create my ControlTemplates in XAML in Visual Studio and use Blend when I want to do anything fancy with graphics or animation.

If you find it easier to do it all in Blend, I have no problem with that, but here's my way. First create a ControlTemplate, Key it as 'ReverseBasicPB' and set its TargetType to ProgressBar. The visual elements of the template will all be held inside a Grid. 

    <ControlTemplate x:Key="ReverseBasicPB" TargetType="{x:Type ProgressBar}">

      <Grid>

 

      </Grid>

    </ControlTemplate> 

As you will know from the earlier blogs, the ProgressBar requires two named parts – PART_Track and PART_Indicator. In this version, PART_Track will consist of a Rectangle inside a Border and PART_Indicator will be a Rectangle with a reasonable copy of the Green Linear Brush.
(As a side note, it is possible to dig into the WPF namespaces and get hold of the ProgressBarGlassyHighlight and ProgressBarTopHighlight Brushes, but I was content to go with my approximation.)

Here is the markup for the PART_Track: 

       <Border Name="PART_Track" BorderBrush="DarkGray"

               BorderThickness="2" CornerRadius="3" >

          <Rectangle Fill="LightGray"

               RadiusX="3" RadiusY="3"/>

        </Border> 

Note that to achieve the rounded corner effect, Border uses CornerRadius and Rectangle uses RadiusX and RadiusY.

The markup for the Indicator is just as simple: 

          <Rectangle Name="PART_Indicator" HorizontalAlignment="Right"

                    RadiusX="3" RadiusY="3" 

                    Margin="2,2,2,2">

          <Rectangle.Fill>

            <LinearGradientBrush EndPoint="0.5,0" StartPoint="0.5,1">

              <GradientStop Color="#FF5FBB23" Offset="0.525"/>

              <GradientStop Color="White"/>

              <GradientStop Color="#FF63BC28"/>

              <GradientStop Color="#FF95EDA5" Offset="0.542"/>

            </LinearGradientBrush>

          </Rectangle.Fill>

        </Rectangle> 

The key property here is the HorizontalAlignment. By setting it to Right we ensure that the ProgressBar Indicator will start at the right hand side and stretch out towards the left.

Demo
The following markup in a Window will create two ProgressBars, one default style and the other using this ControlTemplate: 

    <Grid>

    <Grid.RowDefinitions>

      <RowDefinition Height="87*" />

      <RowDefinition Height="175*" />

    </Grid.RowDefinitions>

 

    <ProgressBar x:Name="PBDefault" Width="150" Height="33">

 

    </ProgressBar>

    <ProgressBar Name="MyPB" Width="150" Height="33"

          Template="{StaticResource ReverseBasicPB}" Margin="22"

          Grid.Row="1"

          VerticalAlignment="Top" />

  </Grid> 

Add in the animation code-behind used in previous examples: 

    Private Sub ReverseBasic_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim a As New DoubleAnimation

        a.From = 0

        a.To = 100

        a.Duration = New TimeSpan(0, 0, 6)

 

        MyPB.BeginAnimation(ProgressBar.ValueProperty, a)

 

        a.From = 0

        a.To = 100

        a.Duration = New Duration(TimeSpan.Parse("0:0:6"))

 

        PBDefault.BeginAnimation(ProgressBar.ValueProperty, a)

    End Sub 

When you run this, completed ProgressBars are reasonably close in looks, the only major difference being that the templated one at the bottom runs right to left.

 

Although the creation of the ControlTemplate was only a few minutes work and opens the way for further visual changes (which I'll probably blog about later), if you just want the default ProgressBar reversed, the simple way is as follows:

Add a new ProgressBar to the Window, but this time include a LayoutTransform to rotate it 180 degrees. 

    <ProgressBar x:Name="PBDefaultReversed"

                 Width="150" Height="33"

                 Grid.Row="2" >

      <ProgressBar.LayoutTransform>

        <RotateTransform Angle="180" />

      </ProgressBar.LayoutTransform>

    </ProgressBar> 

(You'll also need to add the extra row to the Grid if you are following along and creating this sample yourself:) 

    <Grid.RowDefinitions>

      <RowDefinition Height="87*" />

      <RowDefinition Height="85*" />

      <RowDefinition Height="90*" />

    </Grid.RowDefinitions> 

And don't forget to animate it in the code-behind.

The final result, with the default ProgressBar at the top, the templated one in the middle and the transformed one at the bottom, looks like this:

I chose not to use a gradient for the background Gray, but of course you can implement this if you prefer.

WPF: Changing ProgressBar Appearance As Values Change

 

Introduction
In this earlier blog, I looked at how to create a non-rectangular ProgressBar. As the next step, I want to look at how you can change properties dynamically as the current Value of the ProgressBar changes.

I'll start with the easy (and fairly realistic) scenario where you want to tone down the ProgressBar once it has reached its maximum value.

Currently the ProgressBar coloring looks like this:

If you want to apply changes to the look of the ProgressBar as it is running, you can use a DataTrigger in XAML or you can simply use the ValueChanged event of the ProgressBar in the code-behind. I say "simply", but you do in fact have to understand how to dig into the ControlTemplate and drill down in order to make the code-behind approach work.

There's a trade-off between these two approaches – DataTrigger or code-behind. DataTrigger offers a slightly more concise syntax, but only as long as your requirements are very basic. Once you step beyond the absolute basics, the XAML becomes quite complex. And as you are no doubt more familiar with VB, what's the point in struggling just to prove the point that it can be created in XAML? Sometimes, common sense has to win over technical ego.

Overall, in most cases, code-behind is the easiest answer. But just to be sure you know what a DataTrigger is and how it works, let's run through it.

DataTrigger
A DataTrigger is fired (and as a result some predefined change happens) when a value is reached. In the scenario we are looking at here, a DataTrigger could be set to fire when the Value property of a ProgressBar reaches its maximum. So in the case of the ProgressBar defined below that would of course be when it reaches 100.  

    <ProgressBar x:Name="CurvyPB" Width="300" Height="60"

       Template="{StaticResource PBCurvy}"

       Foreground="{StaticResource BlueGreenRed}"

       Minimum="0" Maximum="100" /> 

The 'predefined change' might be to alter the color of the outside edge of the control and also to dim its Opacity, so that it can be more easily ignored now that its work is done.

The DataTrigger sits in a Triggers collection in the ControlTemplate for the ProgressBar. The syntax is as follows: 

      <ControlTemplate.Triggers>

        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Value}"

                    Value="100">

          <Setter TargetName="PART_Track" Property="Stroke" Value="DarkGray" />

          <Setter TargetName="PART_Track" Property="Opacity" Value="0.3" />

        </DataTrigger>

      </ControlTemplate.Triggers> 

(Note that this is only one section of the complete ControlTemplate.)

The DataTrigger Binding looks awkward and is one of those rather peculiar constructs that makes XAML hard to decipher (and create) sometimes. Essentially it translates to :
"Keep checking the Value property of the ProgressBar instance that is using this ControlTemplate. If and when it reaches a Value of 100, fire the trigger."

The Setters both target the Path named 'PART_Track'. The first changes the Stroke to DarkGray and the second one turns down the Opacity of that Path (which effectively reduces the Opacity of the templated ProgressBar).

Here is the full XAML for the Window which contains the ControlTemplate and the ProgressBar instance: 

<Window x:Class="CurvyWithTriggers"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:converter="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Luna"  

   Title="Curved ProgressBar" Height="200" Width="400">

  <Window.Resources>

    <ControlTemplate x:Key="PBCurvy" TargetType="{x:Type ProgressBar}">

      <Grid>

        <Path x:Name="PART_Track"

             Stroke="{StaticResource BlueGreenRed}"

             StrokeThickness="5"

             Data="F1 M46.802502,0.50000018 C59.803562,0.50000006 71.553123,3.7052743 79.942001,

              8.9014616 C88.330879,3.7052746 100.08044,0.5 113.0815,0.50000018 C125.92575,

              0.5 137.54851,3.6284194 145.9305,8.6908474 C154.3125,3.6284194 165.93524,

              0.50000006 178.7795,0.50000018 C204.35167,0.5 225.082,12.900593 225.082,

              28.1975 C225.082,43.494408 204.35167,55.895 178.7795,55.895 C165.93524,

              55.895 154.3125,52.766582 145.9305,47.704151 C137.54851,52.766582 125.92575,

              55.895 113.0815,55.895 C100.08044,55.895 88.330879,52.689728 79.942001,

              47.493538 C71.553123,52.689728 59.803562,55.895 46.802502,55.895 21.230335,

              55.895 0.5,43.494408 0.5,28.1975 0.5,12.900593 21.230335,0.5 46.802502,

              0.50000018 z"

             Stretch="Fill">

          <Path.Fill>

            <MultiBinding>

              <MultiBinding.Converter>

                <converter:ProgressBarBrushConverter />

 

              </MultiBinding.Converter>

              <Binding Path="Foreground" RelativeSource="{RelativeSource TemplatedParent}" />

              <Binding Path="IsIndeterminate" RelativeSource="{RelativeSource TemplatedParent}" />

              <Binding Path="ActualWidth" ElementName="PART_Indicator" />

              <Binding Path="ActualHeight" ElementName="PART_Indicator" />

              <Binding Path="ActualWidth" ElementName="PART_Track" />

 

            </MultiBinding>

          </Path.Fill>

        </Path>

        <Decorator x:Name="PART_Indicator" />

       </Grid>

 

      <ControlTemplate.Triggers>

        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Value}"

                    Value="100">

          <Setter TargetName="PART_Track" Property="Stroke" Value="DarkGray" />

          <Setter TargetName="PART_Track" Property="Opacity" Value="0.3" />

        </DataTrigger>

      </ControlTemplate.Triggers>

    </ControlTemplate>

 

  </Window.Resources>

  <Grid>

    <ProgressBar x:Name="CurvyPB" Width="300" Height="60"

       Template="{StaticResource PBCurvy}"

       Foreground="{StaticResource BlueGreenRed}"

       Minimum="0" Maximum="100" />

 

  </Grid>

</Window> 

This markup in the Application.xaml file creates the LinearGradientBrush: 

    <Application.Resources>

    <LinearGradientBrush x:Key="BlueGreenRed"

       EndPoint="1,0.5" StartPoint="0,0.5">

      <GradientStop Color="#FF2D3ADD" Offset="0"/>

      <GradientStop Color="#FFF13E14" Offset="1"/>

      <GradientStop Color="#FF9775D8" Offset="0.192"/>

      <GradientStop Color="#FF3F893B" Offset="0.481"/>

      <GradientStop Color="#FF2B9518" Offset="0.625"/>

      <GradientStop Color="#FECC7638" Offset="0.812"/>

    </LinearGradientBrush>

  </Application.Resources> 

The code-behind to animate the ProgressBar: 

Imports System.Windows.Media.Animation

 

Partial Public Class CurvyWithTriggers

 

    Private Sub CurvyWithTriggers_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim a As New DoubleAnimation

        a.From = 0

        a.To = 100

        a.Duration = New TimeSpan(0, 0, 8)

 

        CurvyPB.BeginAnimation(ProgressBar.ValueProperty, a)

    End Sub

End Class 

Apologies if you already have all that code and markup from the previous blog. Personally I hate it when someone says "I've used the same code as in my earlier blog", which means I then have to go and find that blog, dig through it and find the missing bits before I can test out what I'm working on now. So I much prefer to repeat it and make life easier for you, at the expense of a slightly longer blog entry.

After the ProgressBar completes its mission, it hits that Value of 100 and the look changes to this:

So if you're sat there saying to yourself that there's nothing there that you couldn't have easily done in code-behind, I almost agree with you. Certainly the Opacity change would be easy: 

        If progressBar1.Value = 100 Then

            progressBar1.Opacity = 0.3

        End If 

But what about changing the value of the Stroke property though? The ProgressBar doesn't have a Stroke property. That's tucked away inside the ControlTemplate and is a property of the Path named PART_Track. To get to that, we will need a way to access the ControlTemplate and then drill down into the Path.

Before we look at the code-behind approach, I just want to mention a problem with DataTriggers, especially as they relate to ProgressBars. The only arithmetic operator available to you in the DataTrigger in XAML is the equals operator. For fairly obvious reasons, the less-than and greater-then operator symbols have the potential to cause problems in a language that uses them as element delimiters.

The reason that this is a particular problem with the ProgressBar is that the algorithm that breaks the ProgressBar movement into its time chunks will rarely, if ever, space them across whole numbers. That is to say if the Minimum Value is 0 and the Maximum Value is 100 and the duration is 10 seconds, you might reasonably suppose that each new block will appear at intervals of 0, 10, 20, 30, etc. However it doesn't work that way and the values are more likely to be something like :
0
0.252538
0.299015
0.613981
: etc
: ending with
99.769844
99.890324
100

This makes it impossible for you to set a DataTrigger on a value that you can be certain will be matched exactly, except for the starting and ending values of 0 and 100. In theory, you could tweak the size settings of the PART_Track and PART_Indicator to ensure whole number partitions (these being the key factors in the breakdown), but none of my experimenting with this approach worked. An alternative approach, which I haven't tried, would be to create a ValueConverter which would then allow you to set value parameters, such as < 20 or > 40 and so on. However, I didn't really see any gain in going that route, as I can use the ValueChanged event of the ProgressBar directly, together with a bit of WPF delving, as we will now see.

FindResource and FindName
When you need to get at a ControlTemplate (or other Resource, for that matter), you can use the FindResource method of the FrameworkElement class. Before, I get into that, I am going to make two changes to the markup in the Window.
The first change is to add a Name property to the Window, placing this inside the opening tag of the Window class markup:  

 x:Name="CurvyValueChangedWindow" 

This is necessary in order for the Window to be accessed from the code-behind.

The second change is purely decorative – changing the Stroke property of the PART_Track path from the LinearGradientBrush to plain Yellow.  

        <Path x:Name="PART_Track"

             Stroke="Yellow" 

 The ProgressBar now looks like this at startup:

To locate that ControlTemplate, I use the following code, which I have placed in the ValueChanged event of the ProgressBar: 

        Dim ct As New ControlTemplate()

        Try

            ct = CType(CurvyValueChangedWindow.FindResource("PBCurvy"), ControlTemplate)

 

        Catch Ex As ResourceReferenceKeyNotFoundException

            '  Design time message

            Console.WriteLine("CT Not found")

            '  Quit gracefully if not found

            Exit Sub

        End Try 

The third line does all the work and you will see now why I added a name to the WPF Window. This Name is used to identify the container in which the Resource named PBCurvy (i.e. the ControlTemplate) should be found.
Note also that if due to a typo or other error the Resource can't be found, then no further action will be taken. At this stage, the Catch is superfluous. It is however important to avoid the application crashing as we move on to the next step.

FindName
So now we have got ourselves a reference to the ControlTemplate, but we still need to drill down into the Path named PART_Track which is a sub-element of that template. To do this, you can use the following code, placing it immediately below the code used to find the ControlTemplate resource:  

        Dim p As New Path

        p = CType(ct.FindName("PART_Track", PBCurvy), Path)

 

        If IsNothing(p) Then

            Console.WriteLine("Path Not found")

            Exit Sub

        End If 

This time the FindName function searches through that ControlTemplate (now referenced as 'ct') in order to find the named Path.
Once again, the test for IsNothing isn't necessary at this stage, because we have not yet tried to do anything with the Path variable 'p'. As before though, when we add further code, this checkpoint – and the Exit Sub if the Path isn't found – are important.

Changing The values
We've now reached the point where we can manipulate values of that Path according to the current values of the ProgressBar. This code placed immediately below the previous snippet will change the color and thickness of the Stroke: 

        Select Case PBCurvy.Value

            Case Is < 25

                p.Stroke = New SolidColorBrush(Colors.Yellow)

                p.StrokeThickness = 5

            Case 25 To 49

                p.Stroke = New SolidColorBrush(Colors.Green)

                p.StrokeThickness = 7

            Case 50 To 75

                p.Stroke = New SolidColorBrush(Colors.Orange)

                p.StrokeThickness = 9

            Case 76 To 99

                p.Stroke = New SolidColorBrush(Colors.Red)

                p.StrokeThickness = 11

            Case Else

                p.Stroke = New SolidColorBrush(Colors.DarkGray)

        End Select 

When you run the application, the colors will change as things progress:  

 

 I have chosen to make those particular changes for demo purposes, but you are of course not limited to those. The key take away points in this blog are the availability of the DataTrigger and the use of the very helpful FindResource and FindName methods when you want to trigger a change via the code-behind.

WPF: How To Animate a Gradient Brush

 

In the previous blog item in this series, I created a simple Animation in Visual Basic Code. This used a DoubleAnimation to animate the thickness of the gradient border. That example doesn't even begin to scratch the surface of what you can achieve with WPF Animation and – as you have seen demonstrated in the previous items – how you can include these animations in Windows Forms.

In this part, we will look at a slightly more complex animation. Or at least one that seems complex at first, but a quick analysis reveals that it is really rather simple and easily created by writing the XAML by hand.

In order to see the animation effect, I recommend you download the project attached to this blog item and run it. When you click the Button if there is no text in the TextBox, two animations will run. The first is the resize border one that was covered in the previous blog. The second animation changes the position of each of the colors in the gradient to give the impression of movement.

If you are new to WPF, you perhaps won't be too comfortable with the idea of hand-writing the XAML. You can of course use a tool, such as Expression Blend, to create the XAML for you. A time-limited trial version of Expression Blend is available, so if you decide that the hand-written route isn't for you, then you can certainly use Blend instead. (That said, there is something of a learning curve involved in getting to grips with Blend too – especially if, like me, you have more of a Developer mindset than Designer).

One of the reasons I decided to add this part to the blog was that sometimes you need to start the animation running from some user action. In this case, the user action will be the clicking of a button on a totally unrelated UI – a Windows Form, as it happens (although of course the same approach works fine with an all-WPF application too.)

So, firstly, here is the XAML that goes in the WPF UserControl to create the moving gradient effect: 

(Note that there is a formatting problem with the blog editor.  The GradientStop isn't really a Devil's Head it is the numeral 6 in square square brackets – [ 6 ], but without the spaces.  )

    <Storyboard x:Key="MoveColors" >

 

      <DoubleAnimation By="0.2" Duration="0:0:1.3"

         AutoReverse="True" 

         Storyboard.TargetName="GradBorder"

               Storyboard.TargetProperty="BorderBrush.GradientStopsDevil.Offset"

                BeginTime="0:0:0.6" />

 

      <DoubleAnimation By="0.2" Duration="0:0:1.3"

                       AutoReverse="True"

              Storyboard.TargetName="GradBorder"

               Storyboard.TargetProperty="BorderBrush.GradientStops[5].Offset"

                BeginTime="0:0:0.6" />

 

      <DoubleAnimation By="0.2" Duration="0:0:1.3"

                       AutoReverse="True"

             Storyboard.TargetName="GradBorder"

               Storyboard.TargetProperty="BorderBrush.GradientStops[4].Offset"

                BeginTime="0:0:0.6" />

 

      <DoubleAnimation By="0.2" Duration="0:0:1.3"

                       AutoReverse="True"

         Storyboard.TargetName="GradBorder"

               Storyboard.TargetProperty="BorderBrush.GradientStops[3].Offset"

                BeginTime="0:0:0.6" />

 

      <DoubleAnimation By="0.2" Duration="0:0:1.3"

                       AutoReverse="True"

             Storyboard.TargetName="GradBorder"

               Storyboard.TargetProperty="BorderBrush.GradientStops[2].Offset"

                BeginTime="0:0:0.6" />

 

      <DoubleAnimation By="0.2" Duration="0:0:1.3"

                       AutoReverse="True"

            Storyboard.TargetName="GradBorder"

               Storyboard.TargetProperty="BorderBrush.GradientStops[1].Offset"

                BeginTime="0:0:0.6" />  

 

      <DoubleAnimation By="0.2" Duration="0:0:1.3"

                       AutoReverse="True"

             Storyboard.TargetName="GradBorder"

               Storyboard.TargetProperty="BorderBrush.GradientStops[0].Offset"

                BeginTime="0:0:0.6" />

 

    </Storyboard> 

That may look like a lot of typing, but there's a whole lot of repetition in there, which means you can copy and paste the majority of it, then just make minor tweaks. 

 <Storyboard x:Key="MoveColors" > 

The first line shown above simply creates a new Storyboard instance and assigns it a name, or Key, that we can use to reference it.

The next block creates a DoubleAnimation.  

      <DoubleAnimation By="0.2" Duration="0:0:1.3"

         AutoReverse="True" 

         Storyboard.TargetName="GradBorder"

         Storyboard.TargetProperty="BorderBrush.GradientStopsDevil.Offset"

         BeginTime="0:0:0.6" /> 

Most of us tend not to think of Types such as Double numeric values when we are thinking about animation. In our mind's eye we are more likely to picture the physical result of such a change – the size of an element or its position, its shape or its color, perhaps. But it is perfectly possible to change the value of a Double Type, taking a set amount of time to make the change, and then having this change be visible to a user by assigning it to a property of an element that takes type Double.

That's exactly what we are doing here – changing the value of the Double that is being used to set the position of a GradientStop in a LinearGradientBrush. Here's a more detailed breakdown of what's going on:

By changes the Double value by the amount stated – in this case by 0.2. In our demonstration animation this moves one of the colors that make up the gradient 0.2 along the gradient's linear scale.

Duration sets the length of time taken to change the value from its starting value to the end of the By value. In this case it is 1.3 seconds.

AutoReverse set to True will ensure that the value returns to its starting value by the end of the Duration. In our gradient example this will cause the color to move 0.2 out and then return the same 0.2 distance back.

Storyboard.TargetName uses the WPF Attached Property syntax to identify which element is the target of the animation.

Storyboard.TargetProperty in a similar way identifies the property of the target element which is to be animated. See the further explanation below about the identification of the property.

BeginTime is an optional pause feature. The animation will only begin after an initial pause of (in this example) 0.6 seconds.

The TargetProperty in each of these DoubleAnimations is the Offset value of a GradientStop. If you look at the XAML which creates the BrightGradient LinearGradientBrush, you will see that it contains seven colors, each of which has its position in the gradient assigned by its Offset value. All we are doing in this animation is shunting each of the colors along by a value of 0.2 and this creates the effect of the color movement.

The particular block of XAML shown above animates the last GradientStop, which has an index of 6 – the index being zero based. When you look at the six DoubleAnimations which follow that one, the only difference is that the Index has changed so that the next GradientStop is targeted. So by copying and pasting the first DoubleAnimation, pasting it six times and then changing the value of the Index, this complex looking animation is shown to be very basic in reality.

All that remains now is to wire up this Storyboard in the code-behind.  

    Public Sub MoveColors()

        Dim SB As Storyboard = CType(FindResource("MoveColors"), Storyboard)

        If SB IsNot Nothing Then

            SB.Begin()

        Else

            MessageBox.Show("Can't find storyboard")

        End If

    End Sub 

The FindResource method is very useful in these kind of situations in WPF. I used it in the original example to change the LinearGradientBrush on the Border element and now we can employ it to find the animation. It allows you to dig into the XAML, find the markup you need for a particular task and use it. In this cas, the Resource we are looking for is the Storyboard that has a Key of 'MoveColors' – the one we have just dissected above.

Over in the Windows Forms Form1 code, the Button click has only one additional line from where we left it at the end of the previous blog. This calls the MoveColors procedure shown above, which in turn runs the animation. The additional line is: 

           MovingGradientTextBox1.MoveColors() 

You can see the remaining unchanged Button Click event code in the sample project attached.

WP Like Button Plugin by Free WordPress Templates