Blog Archives

How To Create and Use a Custom Command in WPF

  I've got to be honest and admit up front that initially I wasn't completely sold on the idea that WPF Commands are the great leap forward that they are sometimes billed as. OK, so I get that they can reduce the repetition of event handling code, but even their most ardent supporters aren't going to be able to claim that the required code is particularly intuitive. I will agree though that the availability of CanExecute is useful sometimes when multiple controls are bound to a Command. As a WinForms developer moving to WPF, I still find that in many cases I'm happy to use event handlers and, where necessary, a utility method or two.

So, having got that caveat out of the way, let's look at a basic situation where we want a range of different user actions to trigger the same task.

I've decided to use the scenario that I covered in this blog. It's maybe not the most realistic scenario, but it does give an opportunity to use a wide range of user inputs and gestures to move the image across the screen. It also allows the use of the CanExecute event.

In the earlier blog, the user had to click on an image to make it move from left to right across a Canvas. This can soon become tiresome, so by means of a Command we'll give her several other options. In order to make the use of CanExecute realistic, we will say that once the image reaches the far right edge of the Canvas, it must stay there. So the image move will be allowed to happen (i.e. CanExecute) as long as it hasn't reached the right hand edge of the canvas.

Creating the User Interface
The following XAML will create an updated version of the Canvas-and-Image UI used previously. This version includes various controls that will be bound to the Command. 

<Window x:Class="Window3"

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

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

 

   Title="Using A Command" Height="300" Width="300">

 

 

    <Grid x:Name="MainGrid">

    <Grid.RowDefinitions>

      <RowDefinition Height="Auto" />

      <RowDefinition Height="218*" />

      <RowDefinition Height="Auto" />

    </Grid.RowDefinitions>

 

 

      <!–  Canvas in the main middle section –>

    <Canvas x:Name="MainCanvas" Grid.Row="1">

      <Image x:Name="MoveableImage" Width="55" Source="questionmark2.jpg"

 

        Canvas.Left="0" Canvas.Top="0" />

    </Canvas>

 

    <!– Menu at the top –>

    <Menu Grid.Row="0" Margin="3">

      <MenuItem Header="Move It" Margin="5"

 

               ></MenuItem>

    </Menu>

 

    <ToolBar Grid.Row="2" >

      <Button Content="Move It"

              />

    </ToolBar>

 

      <Button Grid.Row="1" VerticalAlignment="Bottom"

             HorizontalAlignment="Right" Width="100"

             Height="33" Content="Move It" Margin="0,0,4,2"

              />

  </Grid>

</Window>

 (If you've looked at the markup closely and think that the order of elements is haphazard, you would be wrong.   Try moving the MenuItem to what seems a more logical position above the Canvas and you will get an Error when the code is completed.  As you will see, the code behind for the MenuItem will eventually have a link to the Image.   So therefore the Image must be created first in the top down XAML file, before the MenuItem can know about it .  I often think that it's these little potential Gotchas that make the WinForms-to-WPF learning curve so difficult.)

This is what it should look like:

  

Creating the Command
Let's start by creating a new Command Class called MoveItCommand. It will have a single property named MoveIt, a backing field named _move it and a parameterless constructor. The property and field are of type RoutedUICommand, but apart from that are standard and really don't need any additional explanation: 

    Private Shared _moveit As RoutedUICommand

    Public Shared ReadOnly Property MoveIt() As RoutedUICommand

        Get

            Return _moveit

        End Get

    End Property

 The constructor is used to create the various keyboard and mouse gestures that can be used to move the image. For the purposes of demonstration, I've gone totally overboard on these and have included four variations in the final version. But for clarity, I am only showing one in the code snippet below:

     Shared Sub New()

        '  Add keyboard and mouse gestures

        Dim UserInputs As New InputGestureCollection()

        UserInputs.Add(New KeyGesture(Key.M, ModifierKeys.Alt))

 

         '  Assign these gestures to the _moveit field (and thereby to the MoveIt property)

        _moveit = New RoutedUICommand("Move Element", "Move", GetType(MoveItCommand), UserInputs)

 

    End Sub 

  • The constructor begins by creating a new empty collection of mouse gestures.
  • The second line creates and stores a gesture which takes the Alt and M keys as the combination to be used to fire this command. Notice the order of the keys used as arguments – effectively it is "M & Alt" which isn't the way we usually think of the combination. If you switch the order and place the Modifier key first, you will get a runtime error. Trust me, I've been there, done that!
  • Finally, a new RoutedCommand is created, containing descriptive text, its name, owner type (which is this custom command class – MoveItCommand), and the collection of Gestures that will work with this command.

 Note that all the members of this class are Shared, ensuring that only instance of the Command will be in use in the application when it runs.

Additional Gestures
If you are happy with how the Class and its Constructor works, I'll now add in those other gestures I mentioned. These include F12 on its own, a mouse click on its own and finally a combination of mouse click and key press. Here's the revised code:  

    Shared Sub New()

        '  Add keyboard and mouse gestures

        Dim UserInputs As New InputGestureCollection()

        UserInputs.Add(New KeyGesture(Key.M, ModifierKeys.Alt))

        UserInputs.Add(New KeyGesture(Key.F12, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.RightClick, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.LeftClick, ModifierKeys.Shift))

         '  Assign these gestures to the _moveit field (and thereby to the MoveIt property)

        _moveit = New RoutedUICommand("Move Element", "Move", GetType(MoveItCommand), UserInputs)

    End Sub

 

  • The first gesture is Alt and M, which I've already covered.
  • The second gesture is the F12 key on its own. Note that you must include Modifiers.None. if you don't, you will get a runtime error.
  • The third gesture will fire the command when the mouse is right clicked anywhere in the Window. Because of the way that WPF and RoutedCommands work, this effectively means that you can still click on the Image to make it move. If you wanted to limit the mouse click to the Image only, you could harness the power of Bubbling and Tunneling events to trap the mouse button press at the Image level. I haven't done that in this example.
  • The final gesture takes a combination of both the mouse left button click together with holding down the Shift key. Quite awkward to use and only included to demonstrate the range of available gestures.

 

Command Bindings
Having created the custom Command Class, if we want to use it in the XAML – and we do – then it is necessary to create an XML namespace mapping. Without this link, the XAML markup would have no idea about the existence of the class and its property. The syntax is fairly simple: 

   xmlns:local="clr-namespace:GetValue" 

The alias of 'local' is used as the key which is mapped to the namespace in which the MoveItCommand class resides. The namespace is the same as the name of the project and in this case the project is named 'GetValue'.

Given this namespace mapping, we can now set up the CommandBinding in the XAML pane:

 

  <Window.CommandBindings>

    <CommandBinding Command="local:MoveItCommand.MoveIt"

                   Executed="CommandBinding_Executed"

                   CanExecute="CommandBinding_CanExecute"/>

  </Window.CommandBindings>

 

As you see from the snippet above, you create a new CommandBinding inside a CommandBindings collection. The Command property of the CommandBinding points to that MoveIt property created in the MoveItCommand class.

The Executed and CanExecute properties point to two methods which we will create next in the code-behind.

Executing The CommandBinding The task of the CommandBinding_Executed method is to carry out whatever tasks we want to have actioned when the command is invoked. In this demonstration project, this task is simply to shunt the image a few units to the right. So the code is as follows:  

    Private Sub CommandBinding_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)

        '  Move the image 5 units to the right

        Dim LeftPos As Double = MoveableImage.GetValue(Canvas.LeftProperty)

        MoveableImage.SetValue(Canvas.LeftProperty, LeftPos + 5)

 

    End Sub 

(If you are not sure about how the GetValue and SetValue functions work, you can read up on it in my blog here).

CanExecute
CanExecute is an optional feature which can be quite useful. In this demonstration, we will only allow the Command to work (and therefore only allow the Image to continue moving to the right) if it hasn't reached the right hand edge of the Canvas. More subtly, if CanExecute becomes set to False, any controls that are bound to the Command will automatically be disabled. I will add Command Bindings to the buttons, etc shortly.

First, here is the code for the CanExecute method: 

    Private Sub CommandBinding_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs)

        '  Only allow execution of the command if the image

        '  has not yet reached the right hand edge.

        e.CanExecute = MoveableImage.GetValue(Canvas.LeftProperty) < (MainCanvas.ActualWidth – MoveableImage.Width)

 

    End Sub 

It tests the current position of the Image to see if it has reached the right hand edge of the Canvas. CanExecute will be set to True if there is still space to the right side of the Image, otherwise it will be set to False.

The complete code behind for the Window and the Custom Command class together now looks like this: 

Partial Public Class Window3

 

    Private Sub CommandBinding_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)

        '  Move the image 5 units to the right

        Dim LeftPos As Double = MoveableImage.GetValue(Canvas.LeftProperty)

        MoveableImage.SetValue(Canvas.LeftProperty, LeftPos + 5)

 

    End Sub

 

    Private Sub CommandBinding_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs)

        '  Only allow execution of the command if the image

        '  has not yet reached the right hand edge.

        e.CanExecute = MoveableImage.GetValue(Canvas.LeftProperty) < (MainCanvas.ActualWidth – MoveableImage.Width)

 

    End Sub

End Class

 

Public Class MoveItCommand

 

    Private Shared _moveit As RoutedUICommand

    Public Shared ReadOnly Property MoveIt() As RoutedUICommand

        Get

            Return _moveit

        End Get

    End Property

 

    Shared Sub New()

        '  Add keyboard and mouse gestures

        Dim UserInputs As New InputGestureCollection()

        UserInputs.Add(New KeyGesture(Key.M, ModifierKeys.Alt))

        UserInputs.Add(New KeyGesture(Key.F12, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.RightClick, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.LeftClick, ModifierKeys.Shift))

         '  Assign these gestures to the _moveit field (and thereby to the MoveIt property)

        _moveit = New RoutedUICommand("Move Element", "Move", GetType(MoveItCommand), UserInputs)

    End Sub

 

End Class 

At this point, you can run the project and use any of those four gestures to move the Image. Once the Image hits the right hand edge, it will stop moving. The menu item, the two buttons don't play any part in the action yet – that is, clicking them will have no effect and they won't become disabled when the Image reaches the right hand limit.

Binding the Command to the Controls
All that remains to do is to bind this custom command to the menu item and the two buttons. Now things really do become simple and you can begin to see the potential benefit of using a Command. The following piece of markup is added to the MenuItem, the Button in the Toolbar and the Button in the Canvas:  

        Command="local:MoveItCommand.MoveIt" 

With those bindings in place, you can move the Image by any of the seven methods included – the four input gestures and by clicking on any of the three controls. Note now that once the Image hits the right hand edge, those three controls all become disabled.

So, there is a fairly simple example of using your own Command. You can see the final version of the XAML markup here, and also the code behind here.

WPF ListView – Formatting Rows

 By the end of the previous blog item on the topic of the WPF ListView, the ListView looked like this:

In this blog I want to look at ways of changing the look of the rows. You may, for example, want to assign a background color to the rows. And based on what we have done so far, you might be tempted to edit the DataTemplates used for the cells. Maybe you would think about adding a Border with a Background fill and placing this around the TextBlock for each cell:  

  <DataTemplate x:Key="IDCellTemplate2">

      <Border Background="LightGreen">

      <TextBlock Foreground="MediumBlue"

                FontFamily="Calibri"

         Text="{Binding Path=ProductID}" />

      </Border>

    </DataTemplate> 

I won't waste the space showing the markup for all three templates here, but if you did try this you will get a disappointing result:

Pretty ugly, I think you'll agree. Playing around with the Margin or Padding won't get you any further either.

The answer is to use the ItemContainerStyle property of the ListView. This opens up a number of choices for you. Firstly, if you would like to still have a gap between each of the columns – but an even gap – then you can take the following approach:  

      <ListView.ItemContainerStyle>

        <Style TargetType="ListViewItem">

          <Setter Property="HorizontalContentAlignment" Value="Stretch" />

        </Style>

      </ListView.ItemContainerStyle> 

This will produce the following output, which is an improvement:

If you want the light green to spread across the whole row, then you still use the ItemContainerStyle, but this time you set the Background property of the ListViewItem:  

      <ListView.ItemContainerStyle>

        <Style TargetType="ListViewItem">

            <Setter Property="Background" Value="LightGreen" />

        </Style>

      </ListView.ItemContainerStyle> 

Now you get the result you are probably looking for.

 

If all that whiteness in the background is a bit too much for you, then of course you can blend another shade of green into the mix by setting the ListView's Background property. Using SeaGreen, for example, will produce this:

 

 

The final markup for the ListView looks like this:  

    <ListView Name="ProductsListView"

         ItemsSource="{Binding}"

           Margin="5,25"

              Background="SeaGreen">

      <ListView.ItemContainerStyle>

        <Style TargetType="ListViewItem">

            <Setter Property="Background" Value="LightGreen" />

        </Style>

      </ListView.ItemContainerStyle>

 

      <ListView.View>

        <GridView>

          <!– Product ID –>

          <GridViewColumn

           HeaderTemplate="{StaticResource IDColHeader}"

          CellTemplate="{StaticResource IDCellTemplate}">

          </GridViewColumn>

          <!– Product Name –>

          <GridViewColumn

           HeaderTemplate="{StaticResource NameColHeader}"

           CellTemplate="{StaticResource NameCellTemplate}">

          </GridViewColumn>

          <!– Pack Size –>

          <GridViewColumn

           HeaderTemplate="{StaticResource PackageColHeader}"

           CellTemplate="{StaticResource PackCellTemplate}">

          </GridViewColumn>

        </GridView>

      </ListView.View>

    </ListView> 

The markup for the DataTemplates and Styles is the same as that shown in the previous blog, all of which I placed in the Application.xaml file (App.xaml for C# projects).

There are some other row formatting options I want to cover, but I will leave those for another day.

WPF ListView: DataBinding and Column Headers

 A recent question on VBCity came from a developer who was searching for a way of changing the background color, font and border of the Column Headers in a ListView. This has always been a particularly tough call in Windows Forms. (A DataGridView, on the other hand, does have this feature, so that might often be an easy workaround.)

But, as usual when questions of difficult layout come up, my mind turns to WPF as a possible solution. Usually I would say 'an easy solution', but in this case honesty forces me to say that I've always found the WPF ListView to be one of its less user-friendly elements. Windows Forms certainly makes it easier to create data by hand, with the ability to add sub-items via the properties window. WPF effectively forces you to use data binding as the means of populating the ListView – although the data source can be a relatively simple collection of objects.   So I thought it might be useful to blog how to create a relatively uncomplicated ListView in WPF – one with a differently colored Column Headers,of course.

There is quite a lot of repetition of the graphical characteristics of the various sub-elements of a WPF ListView – the Column Headers and the content of the columns themselves, for example. So, it is always worth spending the time to create Styles or Templates which can be reused. Not only does this save time in the long run, it reduces the amount of markup used to create the actual ListView itself in the Window which usually makes it a little easier to read.

The first version isn't going to be too ambitious and will look like this:

  

Of course I could have added lots of other graphics junk in there, but I want to try and keep things as straightforward as possible. By the time we've worked our way through the Styles, Templates and XML Data Provider, I think that will be quite enough for one session. In a later blog, I plan to expand on this version.

The Basic ListView
The first step is to drag an empty ListView from the Toolbox and drop it on the XAML pane. Note that I say XAML pane, not the Design pane. As in this case, it is often easier to start with an empty element rather than have to edit the default property settings that you will see in the XAML if you drop it on to the Design pane.

In order to display the content, a WPF ListView requires the use of a View. This has the same role as the View property in the Windows Forms ListView, i.e. it controls the manner in which the data is laid out. The similarities end there, however. At the time of writing, the WPF ListView only has one choice of view – the GridView. In its basic form, this is similar to the WinForms ListView Details View, (except of course that WPF isn't limited in the way you can package and display the data).

As you can see from the following screenshot, even if you wanted to choose a value for the View property, this is something that has to be created in XAML. Future versions of WPF may perhaps increase this choice, but WPF already gives you the freedom to use the equivalent of Large Icon, Small Icon, List, etc, if that is the look you want, so maybe not.

  

The markup to create the View is simple: 

      <ListView >

         <ListView.View>

             <GridView>

 

             </GridView>

         </ListView.View>

       </ListView>

 There will be very little to see in the Design Pane yet. The next step is to add some columns. Add three columns to the GridView of the ListView:

                <GridView>

            <GridViewColumn Width="85">

            </GridViewColumn>

            <GridViewColumn></GridViewColumn>

            <GridViewColumn></GridViewColumn>

        </GridView>

 If you check the Design pane now, you will see a fine outline of the Column Headers. There is a divider after the first column, because I have set a Width value on it. The other two columns are not visible because they currently have a zero width.

  

Create a Reusable Style
Each of the three Column Headers will have some properties that are set to the same values as each other. Therefore it makes sense to set those property values in one place and assign them to each Column Header. The alternative approach would be to repeat virtually the same XAML three times, which clearly isn't a sensible way forward. WPF Styles is the technique which makes this very easy to implement.

In this simple example I am going to use a TextBlock to hold the text of the Column Header. The following markup, which is placed in the Application.xaml file (for VB) or App.xaml file (for C#) creates a Style for a TextBlock:  

    <Style x:Key="ColHeaderText" TargetType="TextBlock">

      <Setter Property="Width" Value="110" />

      <Setter Property="Height" Value="29" />

      <Setter Property="FontSize" Value="14" />

      <Setter Property="FontWeight" Value="Bold" />

      <Setter Property="Background" Value="Yellow" />

      <Setter Property="Foreground" Value="Navy" />

    </Style>

 If you're not familiar with the concept of Styles, all it does is set values on an assortment of TextBlock properties. The Style is assigned a key – in this case 'ColHeaderText'. This Style can now be applied to any TextBlock in the application.

The markup for the ListView can now be extended to include a Header that contains a TextBlock:  

 <GridViewColumn Width="85">

             <GridViewColumn.Header>

              <TextBlock Text=" Product ID"

              Style="{StaticResource ColHeaderText}"  />

            </GridViewColumn.Header>

          </GridViewColumn>

 The TextBlock created by this markup has Text content of 'Product ID' and its color, font, etc are all set by the values stored in the Style. You can see the result in the Design pane screenshot below.

 

  

In the final version, I have added similar markup for the remaining two columns. I have left them out of the snippet above for brevity.

Populating with Data
As I mentioned earlier, you need to use some form of data binding to populate the ListView. In this example I am using a simple XML file. You can view this file here. The markup that creates the link between the application and the XML file is straightforward:

 

    <XmlDataProvider x:Key="ProductList" Source="Products.xml" />

 

It uses an XMLDataProvider which I've keyed as 'ProductList' so that it can be referred to elsewhere in the project. The source data comes from the Products.xml file, which is located with the other project files in Solution Explorer.

Now that we have a data source, the ListView's ItemsSource property can be set to point to it. The markup is a bit tortuous, but not as complex as it might first appear. Essentially, the ItemsSource property is assigned a Binding and that Binding links to the ProductsList data provider. From the ProductsList, XPath syntax is used to point directly to the Product elements in the XML file. XPath isn't always the easiest syntax to use, but in the case of our simple XML file this is relatively straightforward.  

ItemsSource="{Binding Source={StaticResource ProductList}, XPath=Products/Product}" 

Next, the various columns can be bound to the appropriate fields. The GridViewColumn has a DisplayMemberBinding property which , as its name implies, sets the Binding to be used for the content of the column. Once again, XPath is used for this.  

   DisplayMemberBinding="{Binding XPath=ProductID}" 

In the above snippet, DisplayMemberPath is assigned a Binding which links back to the ProductID element of the Products.xml file. (Note: If the field in the data source is an XML Attribute, as opposed to an Element, it is necessary to add a leading @ symbol to the XPath name.)

For this basic ListView, that's all that is needed. You can view the Application.xaml file here, and the markup for the Window here. In a future blog, I'll improve this basic version and look at some other functionality, but if you haven't previously dealt with WPF ListViews I hope this will get you started.

How To Create a Splash Screen with WPF

One small but sometimes useful new feature that was bundled with Service Pack 1 (SP1) of Visual Studio 2008 is the Splash Screen feature of WPF. This enables you to create an image file which is then used as splash while the main application spins up and loads.

Probably the main selling point of this feature is its ease of use. Here are the steps involved:

 Create or select a suitable image file

  1. Add it to the project in the Solution Explorer.
  2. Left-click on the file name in Solution Explorer to select it
  3. Set its Build Action to Splash Screen.

 I chose to use a simple image with some text, but you can be as artistic as you like

At its simplest, that's really all there is to it. If you run the project now, you will see the splash screen appear, followed by the main startup form of the application. If you have a fast machine and a small project, the length of time the splash screen appears may be very short.

This isn't necessarily a problem. After all, the idea is usually to reassure the user that something is happening in the background. If the boot up time is short, then maybe you don't need a splash screen at all.

Of course, you may not know the speed of systems on which your application is going to be installed. Or you may have an opening message that you particularly want to display in any event. In that case, you need some way of controlling how long the splash screen stays on view. And as you probably guessed, this feature is also available.

Remembering that the splash screen is shown at the very start of the application life cycle, it makes sense for the code to be placed in the Application.xaml.vb file. Specifically, in the Application_Startup event.

There are three elements to the code required.

  • The first step is to create a variable for the required splash screen.
  • The second step is to turn off the AutoClose option of the splash screen (this being the option that you will have seen if you followed the steps first described above).
  • Finally, you use the Close method of the splash screen, but set a TimeSpan for the delay before the splash screen totally disappears.

 That expression 'totally disappears' bears a little more explanation. What actually happens is that the splash screen fades from an Opacity level of 1 (fully opaque) to a level of 0 (completely transparent) over the period set by the TimeSpan.

Here is the code:

     Private Sub Application_Startup(ByVal sender As Object, ByVal e As System.Windows.StartupEventArgs) Handles Me.Startup

        Dim splash As New System.Windows.SplashScreen("LoadingScreen.png")

        splash.Show(False)

        splash.Close(New TimeSpan(0, 0, 6))

  

  End Sub

 The above snippet displays the splash screen for 6 seconds.

In most cases, the splash screen works best if you just use it in those cases where you genuinely have, or expect, a delay in the startup. One problem otherwise is that you have to start fiddling with the timing and possibly the opacity of the display of the startup Window so that it synchs with the splash screen. If you don't, then the startup Window may appear (fully opaque) while the splash screen is still gently fading out. This generally doesn't look right. But for a straightforward "Don't worry, this application is working" message to users, the WPF Splash Screen is great.

If you haven't yet installed SP1, you can access it here.

 

 

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.

How To Play Sounds and Music in WPF

Introduction 

This is a re-post of an item that I had on my old blog.   As it seems to continue to get a lot of hits (when the site is up) I thought I would transfer it over here to the new site.

  It is more of a "here's how" approach than a "why it works that way" one.  Also I will be creating everything in code, not markup.  There are several more approaches and advanced techniques available via XAML that I won't be covering here.

PART 1  :  The SoundPlayer

1. Hard Coded File Path

  Starting with possibly the least realistic scenario, you can use the SoundPlayer to play a .wav file by pointing directly to a file via a hard coded path. 

 Code Copy

Imports System.Media

    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
        Dim player As New SoundPlayer("C:Temp4WAY.WAV")
        player.Play()
    End Sub

 

     Note the Imports statement at the top. 

    This wave file will play asynchronously (I do wish someone would come up with an easier to type word than that!  -  it's nearly as bad as 'concatenation' )   But I digress….

    If it's a particularly large wav file, you can help things out by inserting a Load statement after the initialization and before the Play fires.  This will tee the player up to play without any kind of pause while it cranks up the file.   This is something well worth bearing in mind as we users have become more and more impatient of delays.

2.  Use a Content File

   One particularly useful approach which gets around all the potential problems of hard coding file paths is to add the file to your Solution Explorer items.  You can do this with the standard "Add Existing Item" selection from the IDE menu, adding the wav file directly to the list of files for the Project. 

  This now brings me to Frustration #1.  First, here's valid code to play the sound using a SoundPlayer object:

Code Copy
    Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button6.Click

        Dim player As New SoundPlayer
        player = New SoundPlayer("SND0103.WAV")
        player.Play()

    End Sub

   Now, before we move on, let's check the Solution Explorer:

     

    As you can see the file name is correctly entered and that file is sitting in the Solution Explorer's list of files that (you would think) it knows about.

   The key to fixing this problem is the 'Copy to Output Directory' Property.  By default, this is set to 'Do Not Copy'. 

 

 You need to change this to 'Copy Always' and you'll be back on track.

3. Audio File as Resource (Frustration #2)

   I wasn't going to include this option because it has generally caused me more trouble than it's worth (especially bearing in mind that there are several relatively trouble-free alternatives).  However, you can add a wav file as a Project Resource as normal and then play that Resource via a Stream.

   The syntax is:

Code Copy
  Private Sub btnResource_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnResource.Click
        Dim player As New SoundPlayer(My.Resources.canon)
        player.Play()

  End Sub

 

   Even with small, well tested files I often found that using this approach I would get a lot of unacceptable sound distortion (actually additional unrelated sounds to that expected).   I have tried it on several PCs,  each with different sound card configurations and the problems occur on them all.

5.  Other SoundPlayer Features

   In addition to the standard Play method, the SoundPlayer has a PlaySync method – in case you should rather unusually want the system to be locked until the sound has finished playing. 

  And finally if you want a sound to be repeatedly played, you can use the PlayLooping method to drive people mad; sanity can be restored by inserting the Stop method!

PART 2:  MediaPlayer and Media Element

    As you've already seen, the  SoundPlayer has some limitations.  It only understands wav format files and you can generally only play one wav file at a time (unless you start to get quite fancy with multithreading).  In many cases, the MediaPlayer or MediaElement will be a better option, but there is a good reason why I included the discussion of the SoundPlayer above.  In some situations on particular systems you will have  problems getting the  two Media components to work properly.  So it's always useful to know how to use the lighter weight SoundPlayer if you run into these problems in the future.

6.  But I Want to Play MP3 Files!

  -  or WMA or MID, etc. for that matter.  And this is a job for either the MediaPlayer or MediaElement.  Keeping to my approach of doing all the work in code, I'm therefore going to home in on the MediaPlayer.  (If you have a need to create a media object in XAML and have it play, pause, stop, trigger, etc, only in XAML then the MediaElement will be your tool of choice.)  

   Before abandoning the MediaElement approach altogether, I should point out that  you can create the MediaElement in XAML and then access it's properties and methods in code.   However, there is no advantage that I know of and in fact, the syntax is slightly more verbose and it also caused Frustration #4 – the fact that the audio file will only play once and then needs to be 'reset' by calling its Stop method; the Stop method apparently has the effect of returning the playhead to the beginning of the file.

  So let's look at the MediaPlayer in WPF.  It has an Open method which loads the file in the form of a URI.  This was Frustration #5 for me, as it took me a while to get past the problem of the "Value of Type 'String' cannot be converted to 'System.URI.'" error.  It took a lot more  than a quick search to finally discover that I needed simply to add the URIKind argument to the Open method; so the considerable amount of time I spent trying to do casts was wasted.   But maybe that's something I should have known. 

  It also has Play, Pause and Stop methods, plus the more sophisticated Balance and Volume properties.

  There is another small Gotcha involved in using this control.  Take a look at the following code snippet:

Code Copy

Dim mplayer As New MediaPlayer
mplayer.Open(New Uri("Godfather.mp3", UriKind.Relative))
mplayer.Play()

     It's hard to see anything wrong with that code snippet, but if you do try it you'll find that the audio will play for a short period of time and then – for no apparent reason – stop.   This is Frustration #6.

   As I understand it, what happens here is that the new MediaPlayer object is created, passed a URI for its source and then instructed to play.  Play begins but at that point seemingly the MediaPlayer closes itself, saying  "I've set the audio away, so my work here is done".  The end result is that at some unpredictable point in time the closed MediaPlayer is released from memory and will be disposed.  And of course as soon as it no longer exists there is no means for the sound to continue to be played.  So it stops in mid note!

   Anyway, having dragged you  laboriously through that explanation, you'll probably already have realised that the fix is simple.  All you need do is instantiate the MediaPlayer outside the scope of the click event so that the MediaPlayer will continue to exist throughout the lifetime of that Window or Page.  In other words, like this:

Code Copy
Class Window1
    Dim mplayer As MediaPlayer

  Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click

        mplayer = New MediaPlayer
        mplayer.Open(New Uri("Godfather.mp3", UriKind.Relative))
        mplayer.Play()

    End Sub

End Class

   As with the SoundPlayer sample, the mp3 file has been added to the project files in Solution Explorer,  its Build property set to Content and its Copy To Output Director set to Copy Always.

  If the file is extra large and you get a pause before it actually begins to play then you can of course move the mediaplayer instantiation and Open code to, for instance, the Window Loaded event or some other place where the delay won't be so obvious.  It will then be teed up ready to go as soon as the Play method is invoked.

7.  Summary

   There isn't much else to add.  I think you now have the tools you need to  play sound and music files in WPF.  By the way, that does also mean both sounds and music at the same time, because you can have multiple instances of MediaPlayers running concurrently (or MediaPlayer(s) and a SoundPlayer running together).   Therefore  you can have almost any combination of sound files running, subject to the limitations of your system, that is.    

   I hope you will find this information useful in your future projects if you need to include sound and want to avoid the frustration of the learning curve.

  

   

WP Like Button Plugin by Free WordPress Templates