Archive

Archive for the ‘WPF’ Category

Use the Right Container for ListBox Scrolling

December 15, 2009 4 comments

I learned something interesting about ListBox and scrolling content this week.  In hindsight it is obvious, but trust me: that’s the last thing you want to hear after two hours of beating your head on your keyboard trying to figure out why the stinking ListBox isn’t scrolling.  Read on for the gory details.

The Obvious

What I learned is this: if you have an Auto sized ListBox, meaning that it will stretch its Width and Height to match its contents, the ListBox will never produce a Scrollbar.  Even if the content stretches far off the window: in that case, it just so happens that the ListBox will also stretch far off the window.  Since the content is not larger than the ListBox itself, no Scroll bars.

Obvious, right?  Not so: I have recently had several frustrating experiences with the Auto settings for Width and Height.  Don’t get me wrong! I use them a lot, and I tell others to do the same because they greatly assist in laying out elements, especially in Grids.  The problem comes when they don’t do what I *think* they are going to do.  In the case of the ListBox, I was operating under the assumption that they would Stretch the ListBox to fit the available space in the container, and that is not how they behave.

The Solution

Realizing my error, I understand now that the scrolling behavior is controlled by the relationship of the size of the Content to the defined (as opposed to visible)size of the ListBox itself.  In my case, I had the ListBox as the last element of a StackPanel.  I needed the ListBox to fill the remaining space, and I needed the Content to scroll vertically. 

The Width of the ListBoxItems was set to the width of the ListBox, minus a little to allow for the Scrollbar, so that wasn’t an issue.  I wanted the Height to fill the remaining space, but I had no way to bind to the measurement and I wanted to avoid a lot of code behind hijinx.  As discussed above, setting it to Auto would actually prevent the desired behavior, so that option was out. 

In this particular case, the application is a fixed size, so I was able to set the size of the ListBox manually to fill the remaining space in the StackPanel, and lo and behold my Scrollbar appeared.  All was well, or so I thought…

The Case of the Changing StackPanel

In testing, we realized that the element above the ListBox (an Image control) should only be visible when it had an Image to show.  So naturally I wired up a ValueConverter and bound the Visibility to a boolean property in the ViewModel reflecting whether or not there was an Image to display.

This worked great for the Image, but an ugly thing occurred with the ListBox.  Since its Height was hardcoded, it shifted up in the StackPanel and no longer stretched to the bottom.  It look pretty strange just hanging there, Scrollbar happily in view, but a bunch of empty space beneath it.

спални комплекти

So just when I thought I had it licked, I’m back in the same boat with the same problem: I need my ListBox to automatically fill all the remaining space in the StackPanel.  Wait, what was that?  I want a child to fill all remaining space?  That sounds familiar…

Enter the DockPanel

Once I got back up from having the ton of bricks fall on my head, I realized that I was using the wrong container!  What I really needed in that scenario was a DockPanel.  I was just using StackPanel out of habit, I had no specific need for it, and the items within it would never be more than the Image and the ListBox.

So using Blend’s trusty “Change Layout Type” feature, I changed my StackPanel to a DockPanel.  I made sure the LastChildFill property was checked and docked the Image control to the Top and set the VerticalAlignment to Top.  I then did the same thing for the ListBox and set its Height property to Auto.  Running the application gave me exactly the result I was after in the first place: an automatically resizing ListBox restrained to it’s Parent container’s remaining available space.

Lesson Learned

So aside from learning a lesson about ListBox and scrolling, I also learned that I need to probably pay a little more attention to the container type I am using.  Grid and StackPanel have always been my defaults since they cover most scenarios, but I also need to remember we have other options.

Categories: WPF

Using Custom Validation Rules in WPF

October 13, 2009 7 comments

Anyone who has ever developed data driven applications of any sort has had to deal with validation.  Let’s face it, users make mistakes, even if they are developers!  Validation can be a real pain to implement, and contrary to all our HelloWorld style applications, MessageBox.Show is not the best way to inform our users that we have an issue with their keyboarding skills.

I remember being ecstatic when I learned about the WinForms ErrorProvider. This handy little approach finally gave us a nice way to alert the user of the problem without being too heavy handed in the UI.  Where it failed, though, was in customization.  First of all, it took a lot of code behind to manage the ErrorProvider: checking field values against business rules, updating the provider’s list of errors, or clearing it out when the status was OK ended up being a lot of code, especially when there were a lot of data fields. And then, beyond deciding whether or not the Error symbol would blink (I always hated the blinking exclamation point), there wasn’t a lot of pizzazz we could add.  Of course, that’s all changed with WPF.

Getting Started

Before I go any further, you can find most of the basics in these articles:

I recommend reading these articles. The CodeProject article even links to a Project with a WPF version of ErrorProvider.  I will be using a couple code samples from these articles.

I’m not going to harp too much on the details presented in these other articles.  Instead, I’m going to layout what I did to get this working and hopefully give you enough information to get started.

Using IDataErrorInfo

When you first start looking into validating data in WPF you will no doubt run into IDataErrorInfo.  This seems to be the jumping off point.  Implementing the interface is pretty straightforward.  Here is a typical example:

using System.ComponentModel;
namespace ValidationRulesPlay.ViewModels
{
    public class DataErrorInfoSample : ViewModelBase, IDataErrorInfo
    {
        private int _int1;
        private int _int2;

        public int Int1
        {
            get { return _int1; }
            set { _int1 = value; FirePropertyChangedEvent("Int1"); }
        }

        public int Int2
        {
            get { return _int2; }
            set { _int2 = value; FirePropertyChangedEvent("Int2"); }
        }

        #region IDataErrorInfo Members

        public string Error
        {
            get { return null; }
        }

        public string this[string columnName]
        {
            get
            {
                string result = null;

                switch (columnName)
                {
                    case "Int1":
                        if (Int1 > 9999999)
                            result = "Int1 number cannot be greater than 9999999.";
                        break;

                    case "Int2":
                        if (Int2 > 99)
                            result = "Int2 number cannot be greater than 99.";
                        break;


                    default:
                        break;
                }

                return result;
            }
        }

        #endregion
    }

}

[NOTE: the class above inherits from ViewModelBase, a class I use to implement INotifyPropertyChanged.]

And then you add a piece to your Binding syntax in XAML to activate it:

<TextBox
    x:Name="Int1TextBox"
    ValidatesOnDataError="True"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    MinWidth="50"
    Margin="0,0,6,0"/>

This works and will by default wrap the offending textbox with a red border.  It’s not a bad experience for simple examples, but as I see it there are a couple of drawbacks.

First, IDataErrorInfo validated the data after the property has been set. So if I type in a value that is out of range, the target property is still invalidly set with the bad value.  This means that I have to do extra work to correct the error.  In other words, this approach lets me put bad data in that I must then take out.  Wouldn’t it be better if it just didn’t go in in the first place?  Second, this interface uses a custom indexer, which frankly just feels like magic.  It’s a personal preference, and a fear of the unknown, but I just don’t like it.

My other real issue with it is that it ends up being a lot of repetitive code.  Look at the example above and you’ll see two integer values that are both tested to be within a certain range.  While the ranges are different, the action is the same.  Of course, you could create a method, or better yet an Extension Method, to handle this situation.  Maybe it’s because of the hard-coded nature of the validation messages, but it still doesn’t feel right to me.

Creating Custom Validation Rules

Wouldn’t it be better to have a reusable piece of code to handle these similar situations?  One with no real hard coding?  In .NET 3.5 SP1 we have just such an animal, the Custom Validation Rule.

ValidationRule is an abstract class that we can inherit from to create a custom validation rule class of our own.  I could certainly create a custom rule class for each property in my DAL, and perhaps sometimes a custom class for a particular field would make sense, but many times all I need is a generalized validation rule. 

I mentioned previously that IDataErrorInfo checks the validation after the property has been updated.  This means if we attempt to insert a non-integer field with a non-integer value, the validity checking never occurs: instead, WPF swallows the error.  Using a validation rule causes the validation to occur before the property is updated, so we can easily prevent such an occurrence and report it to the user.  It does still allow the bad data into the TextBox, but that’s not necessarily a bad thing.

For this example, we need a validation rule that will report when the user attempts to insert a non-integer character.  We also want to be able to specify a range of acceptable values.  Additionally, we are going to add a few fields to help customize the messaging experience.  Here is the complete class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;

namespace CustomValidationRules
{
    public class IntegerValidationRule : ValidationRule
    {
        private int _min = int.MinValue;
        private int _max = int.MaxValue;
        private string _fieldName = "Field";
        private string _customMessage = String.Empty;

        public int Min
        {
            get { return _min; }
            set { _min = value; }
        }

        public int Max
        {
            get { return _max; }
            set { _max = value; }
        }

        public string FieldName
        {
            get { return _fieldName; }
            set { _fieldName = value; }
        }

        public string CustomMessage
        {
            get { return _customMessage; }
            set { _customMessage = value; }
        }


        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            int num = 0;

            if (!int.TryParse(value.ToString(), out num))
                return new ValidationResult(false, String.Format("{0} must contain an integer value.", FieldName));

            if (num < Min || num > Max)
            {
                if (!String.IsNullOrEmpty(CustomMessage))
                    return new ValidationResult(false, CustomMessage);


                return new ValidationResult(false, String.Format("{0} must be between {1} and {2}.",
                                           FieldName, Min, Max));
            }

            return new ValidationResult(true, null);
        }
    }
}

The magic all happens in the overridden Validate method.  There are lots of additional ways you could customize this, but there should be enough in this example to get you started writing your own custom validation rules.

I put generalized rules like this in a separate project and namespace so I can reuse them in many projects.  For rules that are specific to a particular DAL or Application you can just as easily put them in those namespaces.

Consuming Custom Validation Rules

Now that we have an IntegerValidationRule class, let’s add it to our XAML so we can put it into action.  Unfortunately, there is no facility in Blend for doing so, so we’ll have to edit the XAML directly.  The first thing to do is add a namespace reference to the XAML pointing to the location of the custom validation rules.

We are going to add the validation to a TextBox.  Normally, I would use the shortened syntax to specify a binding, but in this case I’m going to use long hand because there is a lot of detail to add. Here is the completed sample:

<TextBox
    x:Name="Int1TextBox"
    Validation.ErrorTemplate="{StaticResource validationTemplate}"
    Style="{StaticResource textBoxInError}"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    MinWidth="50"
    Margin="0,0,6,0">
    <Binding
        Path="Int1"
        UpdateSourceTrigger="PropertyChanged"
        Mode="TwoWay">
        <Binding.ValidationRules>
            <ValidationRules:IntegerValidationRule
                Min="1"
                Max="9999999"
                FieldName="Int1" />
        </Binding.ValidationRules>
    </Binding>
</TextBox>

There are a number of things going on here.  First, notice we added the Validation.ErrorTemplate property.  This specifies a template to use when the error condition exists.  I’ll cover that shortly along with the Style property. Both of these are used to customize the display of the error condition.

Next, in the TextBox.Text property we’ve added a long hand version of Binding.  I use UpdateSourceTrigger to cue the validation check whenever the property changes.  If I use the default value the change will only occur once the element loses focus, and I prefer a more real time effect.

Within the Binding property, we’ve added a subproperty section called Binding.ValidationRules.  Inside this property we add references to the rules we wish to implement. This will create an instance of the custom validation rule with the specified property values.  Since I set default values in the class, I can leave off any property and the validation will still function. As you can see in the example above, this is where I can set the property values for this particular instance of my IntegerValidationRule.  In this case we are allowing the integer range to be between 1 and 9999999, and specifying the name we want the message to associate with any error.

That’s all we need to do to consume the custom rule.  Now we’ll take a look at the ControlTemplate and Style to see how we can control the display.

Styling a Custom Validation

I took both the ControlTemplate and the Style from the MSDN article How To Implement Binding Validation.  Here are the original values:

<ControlTemplate
    x:Key="validationTemplate">
    <DockPanel>
        <TextBlock
            Foreground="Red"
            FontSize="20"><Run
                Text="!" />
        </TextBlock>
        <AdornedElementPlaceholder />
    </DockPanel>
</ControlTemplate>
<Style
    x:Key="textBoxInError"
    TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger
            Property="Validation.HasError"
            Value="true">
            <Setter
                Property="ToolTip"
                Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={x:Static RelativeSource.Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

Insert these into the Windows.Resources section of the XAML.  If you’ve been coding along, you can run the sample now and it will work.  The only thing you really get is a red exclamation point to the left of the TextBox and a Tooltip if you hover over it.  This is OK, but I didn’t really feel it was enough to grab the user’s attention, so I’m going to change it just a little.

Before I do that, though, take a look at the ControlTemplate: there is something interesting here called an AdornedElementPlaceholder.  This nifty little element allows us to wrap the original ControlTemplate with additional elements.  In this sample, we are placing a TextBlock with the exclamation point just in front of our original control.  Feel free to experiment with this and add some code of your own around the offending TextBox.

Back to the Style: I want the TextBox to pop a little more when an error occurs.  Looking at the original Style code, we see that we have a Trigger that changes the Style when Validation.HasError is True.  In the default example, we are providing a ToolTip.  Here I’ve added some code to make the Background color of the TextBox Red and the Foreground White whenever an error occurs:

<Style
    x:Key="textBoxInError"
    TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger
            Property="Validation.HasError"
            Value="true">
            <Setter
                Property="ToolTip"
                Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={x:Static RelativeSource.Self}}" />
            <Setter
                Property="Background"
                Value="Red" />
            <Setter
                Property="Foreground"
                Value="White" />
        </Trigger>
    </Style.Triggers>
</Style>

Now the user should definitely notice when an error occurs!  Again, I recommend you experiment and see what else you can do to spice up your validation errors.

And another thing…

Another thing that would be very useful is to be able to bind to the state of validation.  In other words, I have a button that I only want enabled if there are no Validation errors.

In WinForms, I used to process through all the Controls and check to see if any of them had a non-blank entry in the errorProvider.  In WPF, what I would really like is to be able to bind IsEnabled to a “HasErrors” property somewhere.  Unfortunately I couldn’t make something so simple work.  I did, however, come up with a solution using a RoutedCommand.  Commands and commanding are a topic for another time, but here is the short version of what I did. 

I created a static RoutedCommand object in my Window class:

public static RoutedCommand ValidateEntryCommand = new RoutedCommand();

I then added the supporting CommandBinding to my XAML file, including CanExecute:

<Window.CommandBindings>
    <CommandBinding
        Command="{x:Static ValidationRulesPlay:Window1.ValidateEntryCommand}"
        Executed="CommandBinding_Executed"
        CanExecute="CommandBinding_CanExecute" />
</Window.CommandBindings>

In the CanExecute event handler, I call Validation.HasError(DependencyObject) for each TextBox I want to validate against and set the event args CanExecute property based on the results.

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    bool hasError = Validation.GetHasError(Int1TextBox) ||
                    Validation.GetHasError(Int2TextBox);

    e.CanExecute = !hasError;
}

Lastly, I set the Command property of the SaveButton to my Command name.

<Button
    x:Name="SaveButton"
    Content="Button"
    Command="{x:Static local:Window1.ValidateEntryCommand}"/>

Now, the button will be disabled if either of the TextBoxes have an error condition.

Conclusion

I hope this distills some of the basics for you and gets you on your way to writing your own custom validation rules.  As always, comments are encouraged.

You can download the full solution for this project, which includes some supporting code.

Categories: WPF, XAML

Swatting A 6 Month Old Bug

August 4, 2009 Comments off

I began working on our first major WPF project last December.  Previously I had done a lot of playing and learning, and written a few internal utilities, but this was our first release grade project.  Things were going well and the partners were very happy with the software development… that is until “the bug”.

The Back Story

The software is an image management tool that interfaces with our legacy AS/400 application.  In short, it allows images on a network server to be linked with data records in our legacy application.  It is a replacement for an existing application written in FoxPro, so you can imagine the Oohs and Ahhs at the new UI.  The project also fills in some serious deficiencies in functionality.

As one can probably surmise, one important piece of functionality when managing groups of images is the ability to add, remove, or rename images.  With File IO such a rudimentary function, I had let it lapse until the end of the project.  Since WPF was the focus of my development, I had lost focus on the project itself.  I spent a lot of time making the application sing with animations, transitions, Visual States, drag and drop with a Visual Brush, you name it I wanted to try it.

Near the end of February I began working with the final bits, which included deleting an Image and saving an updated list of images back to the server.  As soon as I tried to perform any such action on any image, however, I would receive an IOException: "The process cannot access the file because it is being used by another process."

I worked intermittently on the issue while I continued improving the UI.  I figured it had to be something simple, so it took a little while until I began devoting serious effort to solving the problem.

Working on the Problem

Since the project was winding down, I began working on the problem in earnest about the middle of March.  I was able to determine that it was my application causing the lock, so I did a little research and found plenty of indication that opening images has a tendency to lock the file until the application ends.  With the idea that my application was holding the resource I tried to find a way to close it out until the operation was complete.

Without all the gory details, here is a list of things I tried in varying degrees, all of which failed to solve my problem:

  • Setting the ListBox.Source to null.
  • Adding a wait time before attempting the move.
  • Issuing GC.Collect().
  • Moving the operation to a different thread.

All of these were to no avail.  Eventually I posted the problem to StackOverflow and received two responses in short order.  I tried both of the solutions, but neither one seemed to solve my problem.  I asked a few friends on Twitter and email, but no one was able to help beyond what I had already tried.  By the end of April I was ready to call it quits.

Unfortunately for the project, but fortunately for my sanity, other projects became more important.  This gave me an official hiatus from the project, but I would still return to it occasionally in my spare time, always with the same results and frustrations.

Early in July my hiatus was over: the total project (the image project is a subproject of a larger effort) deadline is set at September 1st.  Panic set in: I have three applications to deliver in that time, and one of them is the Image manager.  I put it off a little while while I wrote one of the other applications, but on July 18th I returned full time to my nemesis.  The good news was there was little left to do: the bad news was that I had already spent countless hours on one stupid bug that was make or break for the entire application.

The Solution

I had little choice but to start from the beginning.  I went back to the StackOverflow discussion because it seemed to hold the most promise.  I wrote a test application that just performed the action I wanted, deleting an Image that was in the application.  By using a combination of the two methods in that thread I was able to get the feature working.

The solution that worked was to create a Value Converter that accepted the path of an image and returned a BitmapImage. 

[ValueConversion(typeof(string), typeof(BitmapImage))]
public class PathToBitmapImage : IValueConverter
{
    public static BitmapImage ConvertToImage(string path)
    {
        if (!File.Exists(path))
            return null;

        BitmapImage bitmapImage = null;
        try
        {
            bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.StreamSource = new FileStream(path, FileMode.Open, FileAccess.Read);
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
            bitmapImage.EndInit();
            bitmapImage.StreamSource.Dispose();
        }
        catch (IOException ioex)
        {
        }
        return bitmapImage;
    }

    #region IValueConverter Members

    public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null || !(value is string))
            return null;

        var path = value as string;

        return ConvertToImage(path);
    }

    public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

I then bound the Image source to the path of the image using the converter. While this image (or rather a local copy of the image) was on the screen, I was able to delete the file from the server.  I had tried a version of this previously, but I assumed it wasn’t working because I was still getting the error. Now I had proof that this code works.

Applying the Solution

I was able to apply the solution to the original application after some refactoring.  It’s amazing how much my coding style has changed from the last 6 months experience with WPF.  I look at the application now and cringe, especially at the code behind.  Anyway, I was able to shoe horn my solution in and the images showed correctly.  Can you imagine the anguish I felt, however, when the first time I tried to delete an image I received the same error?

I was quite dismayed, until I realized that it was not the actual image code since that had been proven: there had to be something else causing the problem, but what?  I had already gone through all the code and every reference to an image with a fine toothed comb, several times, what more could there be?  I began the process over again, but I was not a happy camper.

And then it dawned on me.  I finally knew what the problem was: I had been bitten by Data Binding.

How Can I Blame Data Binding?

To be fair, the issue is not with Data Binding per se, but my own misunderstanding of the object I was binding.  The images were bound not once, or twice, but three times:

  • ImageListBox – each item is bound to a different image, effectively a FilmStrip control
  • CurrentImage – a larger Image control showing the selected image from ImageListBox, bound to ImageListBox.SelectedItem
  • DeleteImageDialogBox – a separate Window that displays the image and prompts the user to confirm deletion, the Image is bound to the path passed in from the parent Window

For both ImageListBox and DeleteImageDialogBox I had used the new Value Converter.  CurrentImage, however was a different story.

In binding CurrentImage to the SelectedItem property of the ListBox, I was expecting the property to represent the rendered image that was the result of the Value Converter.  This is completely wrong!  The SelectedItem property is still the file path string!  CurrentImage was still executing the default behavior and retrieving the file from the path resulting in a file lock.

The Resolution

Once I understood what CurrentImage was binding to, I knew what to do: apply the same Value Converter I was using for the list box items.  Sure enough, once I applied that Value Converter everything else fell in place.

In hindsight I feel this was an “Oh Duh” sort of problem.  So many times the solution to our most frustrating problems is right in front of us, and almost always the solution is fairly simple.  As I said to a friend recently – it’s amazing sometimes that we ever get anything right!

Now I have a new issue: via the Value Converter I’m going to the file system three times to show the same image.  I’ll have to address that at some point, and I have a few ideas.  I could store the BitmapImage copy, or a byte[], or a MemoryStream locally and reuse it.  For now, however, I’m going to leave well enough alone and get on to my next application.  Hopefully it won’t take six months to finish.

Categories: WPF

'Brush' type does not have a public TypeConverter class.

July 13, 2009 4 comments

I’ve been a big fan of Value Converters in WPF for some time now, but I was bitten today by something simple and thought it would be worth sharing.

Brush Converter

One of my favorite uses for Value Converters is to set colors of things based on object state.  In my current application, I have drawn a circle that I want to be filled in Red if the application is not connected to the database, and green if it is connected.  My binding engine has an “IsDatabaseConnected” boolean property that I can use for the binding, so what I need to do is bind the bool to a Brush. To do this, we create a simple converter:

using System.Windows.Data;
using System.Drawing;

namespace CamraSketchViewerWPF
{
    public class DatabaseConnectedColorConverter : IValueConverter
    {
        public Brush ConnectedBrush { get; set; }
        public Brush NotConnectedBrush { get; set; }

        #region IValueConverter Members

        public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is bool)
            {
                var isConnected = (bool) value;
                return isConnected ? ConnectedBrush : NotConnectedBrush;
            }

            return null;
        }

        public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new System.NotImplementedException();
        }

        #endregion
    }
}

In the XAML we need an instance of the Converter to bind to: I used Blend 3 to write the XAML, but whether you do it this way or write it by hand you should end up with something like this:

<local:DatabaseConnectedColorConverter x:Key="DatabaseConnectedColorConverter" />
 

This will create an instance of the Converter as a resource that can then be used for the actual binding:

<Ellipse Fill="{Binding IsDatabaseConnected, Converter={StaticResource DatabaseConnectedColorConverter}, Mode=Default}" Width="15" Height="15"/>
 

If you run the code like so, you will get a transparent circle.  What’s missing are the property value declarations for the Converter. In addition to declaring the instance in XAML, we can also set its properties in XAML like so:

<local:DatabaseConnectedColorConverter x:Key="DatabaseConnectedColorConverter" ConnectedBrush="{StaticResource GreenBrush}" NotConnectedBrush="Red" />
 

Note that for ConnectedBrush I have defined my own version of Green as a resource. Now, if you run this code, you’ll receive the same exception that bit me:

‘Brush’ type does not have a public TypeConverter class.

It’s always the simplest things…

I have run into this message and others like it, and it is always frustrating.  What it means is that the Type I am trying to descibe in XAML does not have TypeConverter behavior defined: this is what allows simple Strings in XAML to be properly transcribed into correct types at runtime.

In this case, the problem was a simple one but not an evident one.  Now I love code completion and refactoring tools as much as the next guy, so whenever Intellisense volunteers to help me out I am quick to accept the assistance.  In this case, however, it did more harm than good!

When I referenced the Brush in my Value Converter class code, I was prompted by Intellisense to resolve the missing namespace, which I did with the handy-dandy Shift-Alt-F10 keystoke.  Only I did it reflexively and did not actually read my options.  If you look at the code above, you will see that the Value Converter class is referencing the System.Drawing namespace instead of the System.Windows.Media namespace.  System.Drawing.Brush was not written for WPF and so does not have the TypeConverter code necessary for this to work.  The problem is easily corrected by changing the namespace appropriately.

Conclusion

I placed this in the “Read your options” file for future use. If you are interested in more TypeConverter details, check out this article by Rob Relyea at XAMLfied.

Categories: WPF

Processing Multiline Text in a WPF TextBlock

March 23, 2009 1 comment

So I’m sure this will be filed in the *yawn* category for you experienced WPF developers out there, but I ran into an interesting item on Friday that I wanted to share.  I was developing a custom DialogBox that displays a lot of text and an image from a CLR object passed in from the calling Window.  The text is fairly long, three short paragraphs, but includes several bits of variable data (properties on the CLR object).

I had several options for how to go about this:

  1. Build the Text dynamically in the code behind.
  2. Create several TextBlock items and string them together, probably using WrapPanel, and bind the variables to the CLR properties.
  3. Store the desired text in the TextBlock.Text property with place holders and use it in a String.Format statement to insert the property values.  (I could have also stored this string in an external file)

I’m sure there are other options, but I didn’t want to spend a lot of time on this.  Option 1 just seemed too blunt, not to mention that I couldn’t see in Blend what it would look like from a design perspective.  Option 2 would probably work, and from a pure XAML approach would be preferable, but I was in a hurry so I went with what I knew (or so I thought) and chose option 3.

I immediately liked this idea because I could really see the end result in Blend.  To handle the variable embedding, I planned on reading in the TextBlock.Text value in the constructor and storing it in a private variable.  Then I could read the values passed in and reset the .Text property:

private string messageFormat ;
private DeleteImageDialogBox()
{
    this.InitializeComponent();
    messageFormat = MessageTextBlock.Text;
}

public DeleteImageDialogBox(PictureInfo pic, string archiveDirectory)
    : this()
{
    LayoutRoot.DataContext = pic;

    string message = String.Format(messageFormat,
        pic.GetRecordNumber(),
        pic.GetCardNumber(),
        archiveDirectory);

    MessageTextBlock.Text = message;
}

Unfortunately, this did not work.  A little debugging soon showed me why: TextBlock.Text was an empty string. This was confusing because Blend shows the Text property as containing my text.

Apparently, because I had CRLFs in my Text, the actual text was no longer exposed by the Text property, but was now part of the Inlines property.  Inlines is of type InlineCollection, a collection of Inline objects.  Inline objects are used to manage a line of content. Inline is an abstract class, so the actual collection can contain multiple types. In my case, I had to test for two types: LineBreak and Run.  In order to retain my formatting, if I detected a LineBreak I inserted “\r\n”.  If I detected a Run object, then I could grab the text.

So if you aren’t confused yet, or even if you are, here is the code I used to extract my full text into a usable string variable:

StringBuilder s = new StringBuilder();
foreach (var line in MessageTextBlock.Inlines)
{
    if (line is LineBreak)
    {
        s.Append("\r\n");
    }
    else if (line is Run)
    {
        Run text = (Run)line;
        s.Append(text.Text);
    }
}
messageFormat = s.ToString();

The only formatting I am retaining here are the LineBreaks: if my text had Underlines, Bolds, etc., I would need to add more code above to retain them.  This may seem like a lot of effort, but if I can definitely see the value for text formatting and processing.  I expect to see this again when I get deeper into WPF printing.

Categories: WPF

More Fun with WPF ScrollViewer

February 17, 2009 6 comments

In yesterday’s post, Fun with WPF ScrollViewer, I discussed how to detect when just the scroll bar was clicked on a draggable ListBox.  Today’s episode addresses something that happened to me on the other end: Dropping into the ListBox.

It probably makes sense that when a new item is dropped onto my ListBox, and therefore added to the list, I am setting the new item as the ListBox’s SelectedItem.  In theory, the ScrollViewer should automatically scroll the list so that the SelectedItem is in view.  In reality, this does not work (and I have it on good authority that it should.)  So in my scenario I am programmatically updating the SelectedItem, which now means I also need to programmatically instruct the ScrollViewer to scroll appropriately.

BringIntoView: The method that didn’t do anything

I want this code to run whenever the SelectionChanged event fires, so that is the method we will be using.  The first thing I tried was the BringIntoView method.  I tried it first on the ListBox itself, but realized that’s just silly since the ListBox is already in View.  What I really need is to tell the new SelectedItem to come into view.  If you have never done this, you may be surprised to find that you cannot cast the ListBox.SelectedItem object to a ListBoxItem: it is whatever type is DataBound to the List.  Instead, what I needed was a reference to the ListBoxItem that contains the SelectedItem.

In order to get the reference we need, we have to use the ListBox’s ItemContainerGenerator property.  This property exposes an ItemContainerGenerator object whose documentation is notably sparse.  But pull the object up in Intellisense and you’ll find a couple of useful methods, specifically ContainerFromItem and ContainerFromIndex.  We can use either one of these to retrieve a reference to the ListBoxItem container we need.  The code looks like this:

ListBoxItem item = (ListBoxItem)(ImageListBox.ItemContainerGenerator.ContainerFromItem(myListBox.SelectedItem));

Now that we have a reference to the ListBoxItem container, we can call its BringIntoView() method.

if (item != null)
{
    item.BringIntoView();
}

Unfortunately this was to no avail as it did not have the desired effect.  In fact, as far as I can tell, it had no effect, but it probably caused the ListBox’s RequestBringIntoView event to fire.  So, on to the next trial.

ScrollIntoView: The method that (almost) worked

Not to be thwarted, I began thumbing through Intellisense and found that ListBox has a method called ScrollIntoView.  For some strange reason, this sounded like it might be helpful, so I figured I’d give it a whirl.

ScrollIntoView (which is a really hard name to read in certain fonts because of the two lowercase ‘l’s followed by an uppercase ‘I’) requires that you pass it the object you want to scroll to, so we’ll pass it the ListBox.SelectedItem object:

myListBox.ScrollIntoView(myListBox.SelectedItem);

Now, if you run the code at first you will be lulled into a false sense of security.  This is the worst kind of bug, because it appears to work.  Change the SelectedItem and the ScrollViewer will adjust as necessary to scroll to the newly selected item… the first time.  What I found in subsequent drops to my ListBox was a very ugly bug.  It appears that the ScrollIntoView method gets caught in some kind of infinite loop, constantly resetting itself and causing the control display to gyrate wildly.  A little searching led me to this bug report at Microsoft.

This does not explain the exact same scenario, but it does exhibit the exact same results: an endless loop caused by a call to ListBox.ScrollIntoView(). Unfortunately, the bug is over a year old with no update or new information since it was accepted by Microsoft.

ScrollToHorizontalOffset: The method that does work

I was feeling pretty daunted at this point, but I decided to check one more thing before I quit for the day.  If I could get a reference to the ScrollViewer object itself, perhaps I could manually move it to the desired location.  I posted the code yesterday that I can use to retrieve the object reference, so all I had to do at this point was figure out what my options were for manually scrolling.

I found it in the ScrollToHorizontalOffset() method.  The method takes a double, which I mistakenly assumed was the number of screen units I wanted to scroll to, so I was trying to do a lot of math like measuring the ListBoxItem and multiplying the index number by the ActualWidth, and other pocket protector-y stuff like that.  Needless to say my first efforts were unrewarding.  I finally stumbled onto the fact the the HorizontalOffset indicates the index number of the item to scroll to – I was making it way too hard!  So all I had to do was pass the Selectedndex to the method and viola, a Scroll is born:

private void myListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (myListBox.SelectedIndex >= 0)
    {
                ScrollViewer scroll = myListBox.GetScrollViewer();
                double scrollPos = myListBox.SelectedIndex;
                scroll.ScrollToHorizontalOffset(scrollPos);
    }
}

Conclusion

I’m far from done with my drag and drop reorderable ListBox, but I’ve learned a lot about the VisualTree, item containers, and obviously the ScrollViewer. This code is certainly not set in stone: if you know another way to do this or have any recommendations for the above code, please share them below in the comments.

Categories: WPF

Fun with the WPF ScrollViewer

February 16, 2009 9 comments

In my current project, I have a horizontal ListBox wrapped in a ScrollViewer.  As you might expect, this indicates that I intend to have more content than my ListBox can display at any given time.  I am also implementing Drag and Drop reordering on this same ListBox.  This seemingly average task has turned out to be anything but, and I have spent the last couple of days fighting many issues, a couple of which are ScrollViewer specific.

Dragging and the scroll bar

The first problem I ran into was in initiating the Drag on the ListBox when the scrollbar was visible.  The problem is that the ScrollViewer is part of the default ListBox template, and the PreviewMouseLeftButtonDown event is wired to the ListBox.  This meant that if I tried to use the scroll bar, PreviewMouseLeftButtonDown would interpret it as a drag attempt and react accordingly.

In order to solve this, I needed to gain access to the ScrollViewer object that was part of the ListBox template, but there is no property for such an animal because the ListBox is completely unaware of the ScrollViewer.  And I could not access it directly because the ScrollViewer is not a named object within the Window, but rather it is part of the template.  So what’s a WPF developer to do?  The answer lies in navigating the VisualTree.

Navigating the VisualTree

In the code above, our XAML hierarchy ends at the ListBox:

Window
    LayoutRoot
        StackPanel
            ListBox

But the ScrollViewer lives inside the template definition of the ListBox.  So to access it at run time,we need to use the static VisualTreeHelper class.  To do that, we need to know what the template contains.  I used Blend to open a copy of the default ListBox Template which shows us the following hierarchy:

Template
    Border
        ScrollViewer
            ItemsPresenter

What we see is that the default ListBox template consists of a Border that holds a ScrollViewer that holds an ItemsPresenter.  This is important to visualize, because the ScrollViewer object we want is two levels into our ListBox, so we have to dig a little to get to it.  Our shovel for that digging is the VisualTreeHelper.GetChild() method.  I found this code posted by Matt Hohn at MSDN forums:

private ScrollViewer FindScroll()
{
    Border scroll_border = VisualTreeHelper.GetChild(PhotosListBox, 0) as Border;
    if (scroll_border is Border)
    {
        ScrollViewer scroll = scroll_border.Child as ScrollViewer;
        if (scroll is ScrollViewer)
        {
            return scroll;
        }
        else
        {
            return null;
        }
    }
    else
    {
        return null;
    }
}

In this code, Matt is using GetChild() to get the Border out of the Template.  The second time he uses the Child property to get the ScrollViewer from the Border.  I ended up moving this to an Extension Method so I could reuse it easily:

public static ScrollViewer GetScrollViewer(this ListBox listBox)
{
    Border scroll_border = VisualTreeHelper.GetChild(listBox, 0) as Border;
    if (scroll_border is Border)
    {
        ScrollViewer scroll = scroll_border.Child as ScrollViewer;
        if (scroll is ScrollViewer)
        {
            return scroll;
        }
        else
        {
            return null;
        }
    }
    else
    {
        return null;
    }
}

As you will see later, we will also need access to the ItemsPresenter within the ScrollViewer, so another simple Extension Method will do the trick:

public static ItemsPresenter GetItemsPresenter(this ListBox listBox)
{
    ScrollViewer scroll_viewer = listBox.GetScrollViewer();
    if (scroll_viewer is ScrollViewer)
    {
        ItemsPresenter list = scroll_viewer.Content as ItemsPresenter;
        if (list is ItemsPresenter)
        {
            return list;
        }
        else
        {
            return null;
        }
    }
    else
    {
        return null;
    }
}

Meanwhile, back at the Drag event…

So now that we can get a reference to the ScrollViewer, we have to determine whether or not the user is trying to scroll or drag an item within our ListBox.  To do this, we are going to start working in the ListBox_PreviewMouseLeftButtonDown event.  Remember that this event applies to the entire ListBox, so it will fire when the Scroll bar OR the ItemsPresenter are clicked.  To figure this out, we are going to check IsMouseOver to try to determine if the user clicked on the scroll bar or the actual items.  This gets a little interesting because the template hierarchy, and the inherent bubbling that occurs, means that when you click on the ItemsPresenter, both it AND the ScrollViewer will report IsMouseOver as true.  But if only the ScrollViewer is hovered over, then ItemsPresenter.IsMouseOver will report as false.

So we need references to both, and if ScrollViewer.IsMouseOver is true, but ItemsPresenter.IsMouseOver is false, then we can assume the user clicked on the scroll bar:

private bool IsScrolling { get; set; }
private void ImageListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    IsScrolling = false;

    ScrollViewer scroll = ImageListBox.GetScrollViewer();
    ItemsPresenter presenter = ImageListBox.GetItemsPresenter();
    if (presenter != null && scroll != null)
    {
        IsScrolling = (scroll.IsMouseOver && !presenter.IsMouseOver);
    }
}

We are storing the IsScrolling variable outside our method, because we are going to use it in the ListBox_PreviewMouseMove event to determine whether we are Scrolling or Dragging:

private void ImageListBox_PreviewMouseMove(object sender, MouseEventArgs e)
{
    if (e.LeftButton == MouseButtonState.Pressed && !IsDragging && !IsScrolling)
    {
        // Initiate drag code
    }
}

Enough fun for now

In my next post, I will discuss how to force the ScrollViewer to scroll to my SelectedItem when it is programmatically selected.

Categories: WPF

MultiBinding in WPF

January 21, 2009 3 comments

In my current application, I have a ListBox that displays images horizontally.  Basically it is a WPF FilmStrip (without the control) with some additional features.  One feature is a toolbar that includes navigation buttons.  First and Last buttons are easy: they are always available and always navigate to the first or last image in the list.  The Previous and Next buttons are also easy to navigate, by simply incrementing or decrementing the SelectedIndex value of the ListBox.

Simple IValueConverter

While WPF seems to automatically handle numbers less than 0 and greater than the Items.Count, I only want the Previous and Next buttons enabled based on the SelectedIndex.  If the index is 0, I do not want the Previous button enabled, and if the index is the last one, I do not want the Next button enabled.  To accomplish this, I need to convert the SelectedIndex to a Boolean, so I wired up an IValueConverter.  In the case of Previous, it is a simple task in the converter to just compare the SelectedIndex to 0:

    public class SelectedItemIndexIsNotFirstToBoolean : IValueConverter
    {
        #region IValueConverter Members
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is int)
            {
                int index = (int)value;
                return (index > 0);
            }

            return false;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

Basically, the converter returns True if the value is > 0 so that IsEnabled will be True.  Now to wire this up, just add the Converter to the Binding:


The XAML above does several things: first, the Path is the Property name of the Element (specified by ElementName) we want to send to the Converter. The Converter is a reference to a Resource instance of the Converter, which means you’ll have to add a line for this in Window.Resources:


If you aren’t impressed yet, think about what just happened: I bound a boolean value of a control (it could be any of them, not just IsEnabled) to the SelectedIndex value of a ListBox with no code behind or events. I don’t include the Converter classes as code behind, because I can easily see working up a DLL full of these and using them in many applications.

MultiBinding using IMultiValueConverter

The Next button, however, was not as straight forward as the Previous button.  In this case, I needed to check the SelectedIndex against the Items.Count property to see if this is the last Image in the list.  At first, I tried to use RelativeSource binding to navigate the tree and send the ListBox itself, but I quit for two reasons.  The first reason is it got hairy in a hurry I couldn’t get it to work.  The second reason is that I could see using the resulting IValueConverter for other applications and Control types, so I didn’t want to limit it.  The answer therefore became finding a way to send both the SelectedIndex and the Items.Count to a IMultiValueConverter.

A IMultiValueConverter functions much like an IValueConverter, receives an object[] as the first parameter.  Then, like the IValueConverter, we can simply process the array values and use them to determine the return value:

public class SelectedItemIndexIsNotLastToBoolean : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values[0] is int && values[1] is int)
        {
            int index = (int)values[0];
            int count = (int)values[1];
            return (++index != count);
        }

        return false;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

As you can see, other than processing multiple values, there is very little difference between the two.  The question then becomes “how the heck do I wire this thing up?”  If you are using Blend, I have bad news: as far as I can tell there is no way to wire up MultiBinding in Blend.  The good news is that it was really easy to do in XAML and required no RelativeSource or anything funky like that:


    
        
            
            
        
    
    

I broke the IsEnabled property out of the button and added a MultiBinding tag to it that contains all the magic.  You have to specify the Binding tags in the order the IMultiValueConverter is expecting them.  The statement is very simple, referencing the ElementName and Path as above for each value to be passed to the Converter.  I did discover something interesting: look at the second one and you’ll see I indicated “Items.Count”.  I didn’t actually think this would work since I am referencing a subproperty, but the Count came through just fine. And of course, don’t forget to add a reference to the Converter to Window.Resources.

Conclusions

There is some concern out there that too much stuff will get shoved into XAML just because we can.  The question you have to ask yourself is whether or not the time invested and the result are worth it.  In this case, I think it is justified.  Most of the time I invested here was learning the technique, which is a cost I won’t have to pay again.  It also ended in much less coding in my button Click events, and is completely reusable for different scenarios and future projects.

Categories: WPF

Accessing Command Line Arguments in WPF

January 12, 2009 4 comments

The software I’ve been developing needs to be accessible from the command line, and in those cases it needs to be able to receive parameters in order to properly initialize the application instance.  I’d done this plenty of times in Windows Forms, but when I started looking for the public static void main entry method, I couldn’t find it.  It didn’t take long to find the answer, but I thought it was worth sharing.

Windows Forms

In Windows Forms, the way I’ve always done this is to add a string[] args parameter to Main(), which is found in “Program.cs”.  Then I override the Form constructor and pass the array to the Form.  You can also use Envorinment.GetCommandLineArgs().  That post had a couple of comments where the users seemed concerned because the first element of the array contains the full path to the executable.  If you choose this method, simply ignore index 0 and you can access the parms in exactly the same way.

WPF

The method in WPF is the same basic concept, but it occurs in a slightly different way.  Right off the bat, you’ll notice there is no “Program.cs” and no Main() method to process.  Instead, the entry for the application is found in “App.xaml.cs”.  There is no entry method here like Main: in order to process the incoming paramters, we need to add a listener to the App_Startup event.  This event will fire prior to the Window class being executed. In order to hook the event up to the application, edit “App.xaml” and add Startup=”App_Startup” to the Application declaration.

The event includes a StartupEventArgs object, which contains a reference to the Args array, so you would simply parse it as desired just like in the Windows Forms Main() method. The difference with WPF is that we do not have a call to the Window class exposed like we do in Windows Forms.  What we do have, in the Window class, is access to the Application object itself.  Microsoft’s sample, downloadable from MSDN, creates a static variable in the App class and populates it in the App_Startup event.  The Window class then accesses the static App variable for its access to the command line arguments.

The sample above parses the values and stores them in a Hashtable (yuck!), so obviously we have some better options, like a generic Dictionary.  Or even better, how about a custom class that holds the values you want?  This would be especially good if some of the values are not strings, or to enforce business rules on the incoming parameters.  I like this approach because it allows all the parsing work to be done outside the Window, leaving the Window to only determine how to use the results.  It just feels better to me since one of our primary goals with WPF is separating layers of responsibility.

If you don’t like the process above, or you really want to handle the parsing inside the Window class, you still have access to Environment.GetCommandLineArgs(), and it works exactly like it does in Windows.Forms.  Again, you can just ignore the 0 index and process the arguments directly.  Oh, and if you are a real glutton for punishment, both methods include the Environment.CommandLine property, a string that represents the complete command line entry.

Debugging Command Line Parameters

I hate trying to navigate through the DOS shell (excuse me: Command Prompt) to find the exe file of my project in order to test command line argurments.  Fortunately, we don’t have to!  If you go to the main menu and follow Projects -> “xxxApplication Properties”, and then click on the Debug tab, you’ll find a box where you can enter the Command Line Arguments you want to test.  As expected, they will be included when the application begins and you can debug their values.

Categories: WPF

WPF Visual State Manager

November 7, 2008 3 comments

Last night at RVNUG, a friend of mine pointed me to The WPF Toolkit. The toolkit is a collection of controls and features that extend the current WPF offering from Microsoft. Chief among these is the DataGrid control, but something more appealing caught my eye: Visual State Manager.

Basically, the VSM is a very easy to use feature of Blend 2 SP1 that supports Silverlight 2. It allows the developer to easily set the visual state of user controls and control templates based on the current state of the control, such as MouseOver and Pressed. I had seen this when I was exploring Silverlight 2 in Blend 2, but I’ve been focusing on WPF and was curious as to why I didn’t have the same functionality I had seen in Silverlight. Of course, now I know: VSM is currently only included as part of Blend 2 for Silverlight 2 development. But, if you install the WPF Toolkit and follow the instructions in the link above to update your Registry, you can gain access to the same basic functionality for WPF Applications.

Using VSM in WPF

Previously, these state changes could be accomplished using Storyboards and Timelines, and then assigning their beginning and ending to Triggers. The Timeline tool in Blend is robust and feature rich, but I think for a non-graphically oriented developer it can be more than a little daunting. And it seems to be overkill for simple animations. That’s where VSM comes in: it greatly simplifies the task of adding small animations to element states.

The Silverlight implementation is very nice and intuitive right out of the box: the states are already defined and all you have to do is activate one and make your changes to the art board. Unfortunately, the current version for WPF is not as straightforward. In WPF, once you have the State manager visible, you have to create the State Groups and define the Visual States you wish to modify. Once I figured out what was going on, this is not painful by any means, but it took some trial and error to get it working right. Don’t forget to add a reference to %Program Files%\WPF\WPF Toolkit\…\WPFToolkit.dll to each project in which you wish to use VSM.  Trust me, it will save you a lot of heartache!

So first things first, we need something that VSM can interact with. Since VSM cannot be applied to the default template of a control, we need to create our own. For this article, I created a button and then created an empty template. Once you create the template, either by ‘edit a copy’ or ‘create empty’, the States Manager will appear under the Triggers Manager on the left hand pane. At this point, the only item visible in the States Manager is Base. You can go ahead and create the default look of your template at this point.

Set up the State Manager pane

To begin with we need a Visual State Group to hold our Visual States. In the Top Border of the States Manager pane on the far right side is a small icon with a plus sign on it. Hovering over it will reveal that this is the “Add state group tool. Pushing it will create a new Visual State Group header under Base. Click on the title (VisualStateGroup) and rename it as you see fit. I’m not sure that the group names matter, but to be on the safe side I followed the same scheme that Silverlight reveals and called my first one “CommonStates”.

Now that we have a group to hold our collection of Visual States, we can start adding states. On the CommonStates header bar is another icon with a plus symbol: this is the “Add state” button, so press it to add a new state. Rename VisualState to the desired state. The state names do matter if we are to get the desired result, so I added three typical states: Normal, MouseOver, and Pressed.

Altered States

Now that we have all the missing pieces defined, we can use them just as we do in Silverlight 2. Clicking on the desired state in the State Manager pane will turn on State Recording [as indicated by the red border around the art board and the header message "State recording is on"]. Now you can easily adjust the display elements of the content to whatever you want them to be at that particular state. A classic example would be brightening a button on MouseOver.

When you are done editing the visual elements, click on Base in the State Manager pane to exit state recording mode. It is important that you do this, because otherwise you could easily alter just a given state when you think you are altering the control template itself. Take it from me, this is very frustrating when you do not receive the results you expect. And then, of course, to correct it you need to back all of your changes out and reapply them outside of state recording mode.

Adding transitions

If you have done all of the above, your button should be changing its visual appearance when you mouse over or press the button. You may think this is the end, but wait, there’s more! Leaving it as above will force the change from one state to another to happen immediately. While we have become accustomed to such UI, it is not the most pleasing effect. Instead, users react much better to very slight, more realistic animations. In other words our users will find our software more appealing if it takes a little time to change from one state to another. These quick, smooth changes from one state to another are very short and simple animations called “Transitions”.

To add a Transition, find the Visual State (such as “MouseOver”) in the State Manager pane and look to the right side of the header bar. There you will find an arrow icon adorned with a plus symbol (notice a pattern developing here?) Click on that and a helper window will appear with a collection of Transitions you can modify. In my case, I chose the two with the ‘*’. The one with the * on the left side indicates the transition from any other state to the selected state, and the one with the * on the right indicates the transition from the selected state to any other state.

Clicking on the desired Transition will add it beneath the selected state. On the Transition you will find a text box that lets you enter the desired amount of time you wish the particular Transition to take. The available range is 0 to 1 second. It doesn’t take much for our Transition to have the desired effect, so try .1 or .2s at first to get the feel for it, then adjust as you desire.

Conclusions

Now when you run your project, you should detect the pleasing effects of Transitions. Overall, I find this much much easier than Storyboards and traditional animations. I’m sure that eventually the tooling for WPF Applications will improve to match what is in Silverlight, but overall it is a minor complaint. I want to thank the CodePlex guys for letting us in on this great secret!

Categories: Blend, WPF
Follow

Get every new post delivered to your Inbox.