Archive for the ‘WPF’ Category.

WPF replacement options for an animated gif or How to make a spinner?

Option 1 – Spin an image using a Storyboard

UPDATE: Check out this more recent post: A SpinningImage control in WPF

If your animation is just a single image that moves, such as a spinner, you can use a Storyboard to spin the single image.

Here is a spinner.png file (128×128) that I made really quickly in Paint.NET.

Spinner

  1. Create a default WPF Application project in visual studio.
  2. Add the spinner.png image to the project (or use your own image).
  3. Add a Storyboard to the resources.

Here is the Xaml.

<Window x:Class="SpinAnImage.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="300">
    <Window.Resources>
        <Storyboard x:Key="Spin360" Storyboard.TargetName="Spinner" Storyboard.TargetProperty="RenderTransform.(RotateTransform.Angle)">
            <DoubleAnimation From="0" To="360" BeginTime="0:0:0" Duration="0:0:2" RepeatBehavior="Forever" />
        </Storyboard>
    </Window.Resources>
    <Grid>
        <Image Name="Spinner" Source="spinner.png" Width="128" Height="128" RenderTransformOrigin="0.5, 0.5">
            <Image.Triggers>
                <EventTrigger RoutedEvent="FrameworkElement.Loaded">
                    <EventTrigger.Actions>
                        <BeginStoryboard Storyboard="{StaticResource Spin360}" />
                    </EventTrigger.Actions>
                </EventTrigger>
            </Image.Triggers>
            <Image.RenderTransform>
                <RotateTransform Angle="0" />
            </Image.RenderTransform>
        </Image>
    </Grid>
</Window>

Here is the simple Visual Studio 2010 project:

SpinAnImage.zip

Option 2 – Loop through images using a Storyboard

This option is the one you would use if you have multiple images. You can create a Storyboard that loops through all of your images. If you have a gif, you can extract each image and use them.

Here is a BlackSpinner1.png file (128×128) that I made really quickly in Paint.NET. Actually I made eight versions of this image, where the section that is gray changes in each image.

Here is how you configure your project to animate through these images.

  1. Create a default WPF Application project in visual studio.
  2. Add a folder to the project called Images.
  3. Add the images you want to loop through to the Images directory.
  4. Add a Storyboard to the resources.

Here is the Xaml.

<Window x:Class="SpinUsingMultipleImages.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Spinner using multiple images" Height="300" Width="300">
    <Grid>
        <Grid.Resources>
            <Storyboard x:Key="BlackSpinnerStoryBoard">
                <ObjectAnimationUsingKeyFrames Duration="0:0:2" Storyboard.TargetProperty="Source" RepeatBehavior="Forever">
                    <DiscreteObjectKeyFrame KeyTime="0:0:0">
                        <DiscreteObjectKeyFrame.Value>
                            <BitmapImage UriSource="Images/BlackSpinner1.png"/>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                    <DiscreteObjectKeyFrame KeyTime="0:0:0.25">
                        <DiscreteObjectKeyFrame.Value>
                            <BitmapImage UriSource="Images/BlackSpinner2.png"/>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                    <DiscreteObjectKeyFrame KeyTime="0:0:0.50">
                        <DiscreteObjectKeyFrame.Value>
                            <BitmapImage UriSource="Images/BlackSpinner3.png"/>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                    <DiscreteObjectKeyFrame KeyTime="0:0:0.75">
                        <DiscreteObjectKeyFrame.Value>
                            <BitmapImage UriSource="Images/BlackSpinner4.png"/>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                    <DiscreteObjectKeyFrame KeyTime="0:0:1">
                        <DiscreteObjectKeyFrame.Value>
                            <BitmapImage UriSource="Images/BlackSpinner5.png"/>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                    <DiscreteObjectKeyFrame KeyTime="0:0:1.25">
                        <DiscreteObjectKeyFrame.Value>
                            <BitmapImage UriSource="Images/BlackSpinner6.png"/>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                    <DiscreteObjectKeyFrame KeyTime="0:0:1.50">
                        <DiscreteObjectKeyFrame.Value>
                            <BitmapImage UriSource="Images/BlackSpinner7.png"/>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                    <DiscreteObjectKeyFrame KeyTime="0:0:1.75">
                        <DiscreteObjectKeyFrame.Value>
                            <BitmapImage UriSource="Images/BlackSpinner8.png"/>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </Grid.Resources>
        <Image Name="BlackSpinner">
            <Image.Triggers>
                <EventTrigger RoutedEvent="Image.Loaded">
                    <EventTrigger.Actions>
                        <BeginStoryboard Storyboard="{StaticResource BlackSpinnerStoryBoard}" />
                    </EventTrigger.Actions>
                </EventTrigger>
            </Image.Triggers>
        </Image>
    </Grid>
</Window>

Here is a sample project attached.

SpinUsingMultipleImages.zip

10 Step process for developing a new WPF application the right way using C#

It makes a difference if you do something the right way from the beginning.  Everything seems to work out so much better and takes less time over all.

Here are some basic steps that I have learned will help you do it right the first time. These steps are from my experience, mostly because I did it wrong the first few times.  These are not exact steps. They are subject to change and improve.  In fact, you might have improvements to suggest immediately when you read this. But if you are new to WPF, then reading these steps before you start and following them, will have you closer it doing it the right way the first time.  It is much more pleasant to tweak a pretty good process than it is to go in with no idea for a process and do it wrong.

Step 1 – Prepare the idea

  1. Some one has an idea
  2. Determine the minimal features for release 1.
  3. Determine the minimal features for release 2.
    1. Alter minimal features for release 1 if it makes sense to do so.
  4. Determine the minimal features for release 3.
    1. Alter minimal features for release 1 and 2 if it makes sense to do so.

Step 2 – Design the Application’s back end business logic (simultaneous to Step 3)

  1. Design the backend
  2. Apply the “Keep it simple” idea to the business logic and makes changes as necessary.
  3. Apply the “Keep it secure” idea to the business logic and makes changes as necessary.
  4. Repeats steps 2 and 3 if necessary.
  5. Backend development can start now as the UI and the back end should not need to know about each other.

Step 3 – Design the UI using WPF (simultaneous to Step 2)

  1. Determine what development model should be used to separate the UI from the business logic.
    1. Model-View-ViewModel (MVVM) is the model I recommend for WPF.
    2. Gather libraries used for the model (such as common MVVM libraries that include the common ViewModelBase and RelayCommand objects)
  2. Consider using a 3rd party WPF control set will be used.  Many 3rd party companies provide WPF controls that are better and easier to use than those included by default.
    1. If you decided to use 3rd party controls, purchase or otherwise obtain the libraries for these 3rd party controls.
  3. Consider designing two WPF interfaces or skins (I will call these Views from here on out) for each screen. This will help drive the separation of the back end code from the WPF code. Also if developing two Views is not simple, it indicates a poor design.
  4. Design the interface(s) (you may be doing two Views) using SketchFlow (take time to include the libraries for the 3rd party WPF Controls in your SketchFlow project and design with them)
    1. SketchFlow allows you to design the UI, which is commonly done in paint, but instead does this in XAML, and is actually the WPF code your application will use.
  5. SketchFlow allows you to deliver the design (or both Views if you did two) as a package to the customer.
    1. Deliver it immediately and get feedback.
    2. Make changes suggested by the customer if in scope.
  6. Take time to make the XAML in SketchFlow production ready.
  7. Deliver the XAML to the customer again, to buy of that the design changes are proper.
    1. Make changes suggested by the customer if in scope.

Step 4 – Determine the delivery or install method

  1. Determine the delivery method.
  2. Determine when to develop the delivery method.
    1. The easier the application is, the longer you can wait to determine the installer or delivery method.
    2. The more complex the install or delivery method, the sooner this should be started.

Step 5 – Develop the business logic

  1. Develop the application designed in step 2.
  2. Get the application working without UI or silently. Note: Start the next step, Develop the UI, as soon as enough code is available here.

Step 6 – Develop the UI

  1. Start the UI project by copying the XAML from the SketchFlow document to your Visual Studio or Expression Blend project.
  2. Create a project for the ViewModel code and develop it to interact with the model and business logic using Binding.
  3. Remember to develop two Views for every UI screen as this will help, though not guarantee, that the the MVVM model was correctly used.

Step 7 – Consider a Macintosh version

Macintosh owns a significant market share.  Determine if this application needs to run on Macintosh as well. Sure, since we are running C# your options are limited to either rewriting in objective C and Coca, or using Mono with a MonoMac UI.  I recommend the latter.

Note: It is critical that the UI and business logic are separated to really make this successful.

  1. Completely ignore the WPF design and have Macintosh users users assist the design team in designing the new UI.  Macintosh’s have a different feel, and trying to convert the same UI is a mistake.
  2. Create the MonoMac UI project.
  3. Create a project similar to the ViewModel project in Windows, to link the UI to the business logic.

Step 8 – Consider a BSD/Linux/Unix (BLU) version

BLU (BSD/Linux/Unix) doesn’t exactly own a significant market share. However, it is still important to determine if this application needs to run on on BLU as well. Sure, since we are running C# your options are limited to either rewriting in C++, or using Mono with a GTK# or Forms UI.

  1. Completely ignore the WPF and Macintosh designs and have Linux users assist the design team in designing the new UI. Linux has a different feel, and trying to convert the same UI could be a mistake.
  2. Create the GTK# project.
  3. Create a project similar to the ViewModel project in Windows, to link the UI to the business logic.
  4. GTK# doesn’t support binding, but still keep the UI separate from the business logic as much as possible.

Step 9 – Develop the delivery method

Again, you may need to do this way sooner if the application is complex.

  1. Develop the install or delivery method.
  2. If you decided to deploy to Macintosh or BLU you may have to develop separate install or delivery methods for those platforms as well.

Step 10 – Deliver the finished Products

  1. Once finished, deliver this product.
  2. If you decided to create a Macintosh or BLU version, deliver them when ready as well.  It is OK and maybe preferred to deliver these at different times.

How to configure the WPF RadioButton's circle bullet top aligned when there are multiple lines of text?

Today I had to solve a problem that appeared quite difficult, but turned out to not be so hard if I let Expression Blend do most the work and finish it up in the XAML. I ended up having to completely recreate the default template style and then modify it.

Note: This article could also be titled: How to change the default template style of a standard control?

I had a RadioButton with text that wraps and it wasn’t displaying exactly how my team wanted. Here is the XAML.

<Window x:Class="RadioButtonTopAligned.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <RadioButton GroupName="RadioButtonList">
            <Label>
                <AccessText TextWrapping="Wrap" Text="_This is a very long radio button control line of text that should wrap." MaxWidth="300"/>
            </Label>
        </RadioButton>
    </Grid>
</Window>

The problem is that the circle bullet is center aligned like this:

Notice how the circle bullet is aligned in between the two lines of text. I need it to be top aligned like this:

Notice how the circle bullet is aligned with the top line of text. I need to get WPF to do this.

From a Visual Studio 2010 point of view, there is no easy way to do this. At first I thought it would be a simple dependency property, but it isn’t. An quick internet search led me to realize that I have to pretty much re-style the whole RadioButton. This sounds really hard and in fact, in Visual Studio, without help, it would be really hard. You would have to have the code for the default template style for the RadioButton control memorized.

Here is an forum post I found from MSDN: How do I make a RadioButton’s Bullet align top

While the post is exactly what I was looking for and has an answer, I didn’t at first grasp the answer. I wasn’t sure what was going on until one of my co-workers, Shawn, who is more skilled in Expression Blend, showed me. Now that I understand, I want to make sure the next person who finds the same forum post on MSDN can understand even easier by writing this article and adding it to the forum post.

This is where Expression Blend comes in. If you don’t have Expression Blend, don’t worry, you can still get through this article as I will include the the default style code that Expression Blend created for me right here in my post.

In Expression Blend, this is what to do.

  1. Create a blank WPF project in Expression Blend.
  2. Add a RadioButton.
  3. Right-click on the RadioButton and choose Edit Template | Edit a Copy…
  4. Click OK on the Create Style Resource window.

Here is what happens to your XAML and you can do this to the XAML in you project manually if you don’t have Expression Blend.

  1. The following reference is added to the project: PresentationFramework.Aero
  2. The same is referenced in the XAML (See line 4 of the XAML below)
  3. The default RadioButton style is copied to your XAML under the Window.Resources element.  (See lines 10-48 in the XAML below)
  4. The RadioButton is assigned the style created. (See line 51 in the XAML below)
<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
	x:Class="RadioButtonTopAligned_EB.MainWindow"
	x:Name="Window"
	Title="MainWindow"
	Width="640" Height="480">

	<Window.Resources>
		<SolidColorBrush x:Key="CheckBoxStroke" Color="#8E8F8F"/>
		<Style x:Key="CheckRadioFocusVisual">
			<Setter Property="Control.Template">
				<Setter.Value>
					<ControlTemplate>
						<Rectangle Margin="14,0,0,0" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
		<Style x:Key="RadioButtonStyle1" TargetType="{x:Type RadioButton}">
			<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
			<Setter Property="Background" Value="#F4F4F4"/>
			<Setter Property="BorderBrush" Value="{StaticResource CheckBoxStroke}"/>
			<Setter Property="BorderThickness" Value="1"/>
			<Setter Property="Template">
				<Setter.Value>
					<ControlTemplate TargetType="{x:Type RadioButton}">
						<BulletDecorator Background="Transparent">
							<BulletDecorator.Bullet>
								<Microsoft_Windows_Themes:BulletChrome BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" IsChecked="{TemplateBinding IsChecked}" IsRound="true" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}"/>
							</BulletDecorator.Bullet>
							<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
						</BulletDecorator>
						<ControlTemplate.Triggers>
							<Trigger Property="HasContent" Value="true">
								<Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
								<Setter Property="Padding" Value="4,0,0,0"/>
							</Trigger>
							<Trigger Property="IsEnabled" Value="false">
								<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
							</Trigger>
						</ControlTemplate.Triggers>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
	</Window.Resources>

	<Grid x:Name="LayoutRoot">
		<RadioButton GroupName="RadioButtonList" Style="{DynamicResource RadioButtonStyle1}">
			<Label>
				<AccessText TextWrapping="Wrap" Text="_This is a very long radio button control line of text that should wrap." MaxWidth="300"/>
			</Label>
		</RadioButton>
	</Grid>
</Window>

Now we can edit the XAML. Below is the same XAML as above with the following edits:

  1. Inside the BulletDecorator.Bullet element on line 30, create a DockPanel around the BulletChrome element.
  2. The ControlPresenter is moved to be inside the DockPanel.
  3. Add the following XAML atrributes to the BulletChrome element:
    VerticalAlignment=”Top” Margin=”0,8,0,0″ Height=”{TemplateBinding FontSize}” Width=”{TemplateBinding FontSize}”

    Note: If you change the font of the text content in the RadioButton, you should change the Margin in the style as well. I haven’t figured out how to make it always match the top line without manually tweaking it when you change the font. Also, if you don’t want the BulletChrome element to be the same size as the font, you will have to tweak Width and Height too.

<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
	x:Class="RadioButtonTopAligned_EB.MainWindow"
	x:Name="Window"
	Title="MainWindow"
	Width="640" Height="480">

	<Window.Resources>
		<SolidColorBrush x:Key="CheckBoxStroke" Color="#8E8F8F"/>
		<Style x:Key="CheckRadioFocusVisual">
			<Setter Property="Control.Template">
				<Setter.Value>
					<ControlTemplate>
						<Rectangle Margin="14,0,0,0" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
		<Style x:Key="RadioButtonStyle1" TargetType="{x:Type RadioButton}">
			<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
			<Setter Property="Background" Value="#F4F4F4"/>
			<Setter Property="BorderBrush" Value="{StaticResource CheckBoxStroke}"/>
			<Setter Property="BorderThickness" Value="1"/>
			<Setter Property="Template">
				<Setter.Value>
					<ControlTemplate TargetType="{x:Type RadioButton}">
						<BulletDecorator Background="Transparent">
							<BulletDecorator.Bullet>
								<DockPanel>
									<Microsoft_Windows_Themes:BulletChrome VerticalAlignment="Top" Margin="0,8,0,0" Height="{TemplateBinding FontSize}" Width="{TemplateBinding FontSize}" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" IsChecked="{TemplateBinding IsChecked}" IsRound="true" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" />
									<ContentPresenter RecognizesAccessKey="True" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
								</DockPanel>
							</BulletDecorator.Bullet>
							</BulletDecorator>
						<ControlTemplate.Triggers>
							<Trigger Property="HasContent" Value="true">
								<Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
								<Setter Property="Padding" Value="4,0,0,0"/>
							</Trigger>
							<Trigger Property="IsEnabled" Value="false">
								<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
							</Trigger>
						</ControlTemplate.Triggers>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
	</Window.Resources>

	<Grid x:Name="LayoutRoot">
		<RadioButton GroupName="RadioButtonList" Style="{DynamicResource RadioButtonStyle1}">
			<Label>
				<AccessText TextWrapping="Wrap" Text="_This is a very long radio button control line of text that should wrap." MaxWidth="300"/>
			</Label>
		</RadioButton>
	</Grid>
</Window>

I hope this posts clarifies how to completely recreate a template style for a default control to modify something that at first doesn’t appear modifiable.

WPF NavigationService blanks PasswordBox.Password, which breaks the MVVM PasswordHelper

I am using three things that are just not friends:

  • Pages and NavigationService
  • Model-View-ViewModel design
  • The PasswordBox control

Problem 1 – PasswordBox.Password is not a DependencyProperty

First off, Model-View-ViewModel is design centered around data binding. But PasswordBox.Password is not a DependencyProperty and therefore does not support binding. That is ok, a PasswordBoxAssistant (alternately I have seen it named PasswordHelper or PasswordBoxHelper) as described originally here and also here fixes seems to fix this.

That is, it fixes it unless you are using the NavigationService.

Problem 2 – NavigationService blanks PasswordBox.Password

See when the NavigationService navigates to another page, it somehow know that the current page has a PasswordBox and if it finds a PasswordBox, it blanks the password out.  So since we are using PasswordBoxHelper to make MVVM and data binding work, the value is blanked in the ViewModel and Model as well.

For now, I happen to be using a custom button for navigation so I can simply do this in my ViewModel:

String tempPw = MyPassword;
NavigationService.Navigate(NewPage);
MyPassword = tempPw;

However, this is not the best solution. What if there were multiple links and different ways to navigate?

I think the best solution would be to figure out how to make PasswordBoxAssistant handle this. But I am not sure how or if there is anyway to tell that the password was blanked by the NavigationService and to ignore binding in this instance.

Resource:
http://social.msdn.microsoft.com/Forums/en/wpf/thread/d91aec90-1476-41c0-a925-7661745094c5

Navigation and Pages using Model-View-ViewModel (MVVM)

I am working on a project at work that is a navigation application.

I wanted to use MVVM. I also wanted to document for others how I designed this, as there wasn’t much online about using MVVM with Navigation and Pages.

Here is my project. I will post an explanation of this project as soon as I can get to it, so look for it.

Project Download – Small: Navigation-Pages-MVVM.zip

Project Download – More complete: Navigation-Pages-MVVM 2.0

Expression + Sketch Flow for prototyping

I have historically done mock-ups in two ways:

  1. Just code it up
  2. Paint.NET

Neither are really good solutions.  But as a developer for an enterprise product, the above tools just are not sufficient.

So I have recently introduced to SketchFlow. This is a tool for quickly mocking up your UI.  It has some features that are quite nice and I am impressed.  I can create a UI very quickly and maybe even faster than I could with Paint.NET on the first try.  After using SketchFlow for a while, it will be a lot faster.

There are three features that make SketchFlow an amazing tool for UI prototyping.

  1. The ability to map and design at the same time
  2. The ability to package it for a demo
  3. The ability to export the prototype to word.

These features make it so that for the same or less work that we have to do anyway to provide sample screens, we also get 1) a Map that we otherwise would have had to spend extra time creating in Visio or elsewhere, 2) A packaged demo that we can send to customers for feed back early on before development so we develop it right the first time, and 3) Stub documentation with all the screens and topical guides exported to word in seconds.

All three of these features are demonstrated in this Video.

Get Microsoft Silverlight

Note: For my FreeBSD readers...this might not be for you though it would be interesting to see if Moonlight can load this video.

WPF and Localization in the XAML with String.Format()

I had a localization issue today.

I was trying to get our product running in Spanish to show decimals as a comma (,) instead of a period (.).

Here is the XAML snippet I have.

        <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
            <Image Source="{Binding Image}" Width="24" Height="24" />
            <Label>
                <TextBlock>
                  <TextBlock.Text>
                         <MultiBinding StringFormat="{}{0} ({1}:) - {2} {3:F} GB, {4} {5:F} GB, {6} {7:F} GB">
                            <Binding Path="Drive.VolumeLabel" />
                            <Binding Path="Drive.DriveLetter" />
                            <Binding Source="{x:Static p:Resources.Required}" />
                            <Binding Path="Drive.TotalRequiredSpace.Gigabyte" />
                            <Binding Source="{x:Static p:Resources.Recommended}" />
                            <Binding Path="Drive.TotalRecommendedSpace.Gigabyte" />
                            <Binding Source="{x:Static p:Resources.Available}" />
                            <Binding Path="Drive.AvailableSpace.Gigabyte" />
                         </MultiBinding>
                  </TextBlock.Text>
                </TextBlock>
            </Label>
            <Image Source="{Binding DriveSpaceStatusImage}" Width="24" Height="24" Margin="15,0" />
        </StackPanel>

The Drive.TotalRequiredSpace object is a DataMeasurement object (which I have posted code for in a previous post).

The Gigabyte parameter is a Double.  These Double’s displayed the decimal separator using a period (.) character even though I was testing on a Spanish version of Windows Server 2008 R2.

I located the answer on another blog.
WPF Data Binding Cheat Sheet Update – The Internationalization Fix

He was using Date’s which like Double’s require decimals, and was seeing the same issue.  The solution is the same though.

Add the following code somewhere before your first WPF window opens.

System.Windows.FrameworkElement.LanguageProperty.OverrideMetadata
(
    typeof(System.Windows.FrameworkElement),
    new System.Windows.FrameworkPropertyMetadata(
        System.Windows.Markup.XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)
    )
);

I am pretty sure this is a poor oversight and a bug in my opinion. Even though there is a one line fix, it was a costly fix, as I had to spend time troubleshooting and researching to find this one line solution which should be the default behavior.

A WPF XAML DataBinding Cheat Sheet

I found this Cheat Sheet and thought I would link to it as it could be very useful.

WPF XAML Data Binding Cheat Sheet

Binding Visibility to a bool value in WPF

I was putting code in my ViewModel that returns Visibility but I didn’t really like that very much. If the UI is created with something other than WPF, that is really not going to work. Since I intend to do cross compile my code in Mono, which doesn’t have WPF but uses either Forms or GTK#, I have already encountered this issue. What I really want to use is bool.

The solution is IValueConverter. If you just want the code and don’t want to read this post, just scroll to the bottom and grab the

IValueConverter is part of PresentationFramework (in PresentationFramework.dll) so it isn’t available in Mono, but that is OK because you don’t instantiate it in the ViewModel, you use it in the View so it will only be used when the GUI is part of WPF. So if you are separating your View into a separate DLL, this would be included in the View DLL, that way when you compile everything else, with say a different GUI that uses GTK#, you won’t get a compiler error because PresentationFramework doesn’t exist in Mono.

BooleanToVisibilityConverter

Well, there is an object already created for you called BooleanToVisibilityConverter, but it is limited. True is converted Visibility.Visible. False is converted to Visibility.Collapsed.

Here we see a problem. Visibility has three possible values but a Boolean only has two.

Boolean Visibility
True Visible
False Collapsed
Hidden

This will cover many of the scenarios, but not all.

Here is how it would be used.

For this example, I have this Person class.

    public class Person
    {
        public Person() { }
        public String FirstName { get; set; }
        public String LastName { get; set; }
        public String Age { get; set; }
    }

Here is a simple View for this object. It has a Grid that has a Label and TextBox for each property in the Person object. It also has a CheckBox. The CheckBox gives us a easy bool value, IsChecked. This works similar to a bool property in a ViewModel.

<Window x:Class="TestBooleanToVisibilityConverter.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestBooleanToVisibilityConverter"
        Title="MainWindow"
        SizeToContent="WidthAndHeight"
        >
    <Grid Margin="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid Name="PersonViewGrid">
            <Grid.Resources>
                <BooleanToVisibilityConverter x:Key="BoolToVisConverter"/>
            </Grid.Resources>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Label Content="First Name:" Grid.Column="0" Grid.Row="0" />
            <TextBox Grid.Column="1" Grid.Row="0" Name="firstNameTextBox"
                     Text="{Binding Path=FirstName, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" MinWidth="175" />
            <Label Content="Last Name:" Grid.Column="0" Grid.Row="1" />
            <TextBox Grid.Column="1" Grid.Row="1" Name="lastNameTextBox"
                     Text="{Binding Path=LastName, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" MinWidth="175" />
            <Label Content="Age:" Grid.Column="0" Grid.Row="2"
                   Visibility="{Binding IsChecked, ElementName=ShowAgeCheckBox, Converter={StaticResource BoolToVisConverter}}"/>
            <TextBox Grid.Column="1" Grid.Row="2" Name="ageTextBox"
                     Text="{Binding Path=Age, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" MinWidth="175"
                   Visibility="{Binding IsChecked, ElementName=ShowAgeCheckBox, Converter={StaticResource BoolToVisConverter}}"/>
        </Grid>
        <Grid Grid.Row="1">
            <CheckBox Name="ShowAgeCheckBox" Content="Show Age" />
        </Grid>
    </Grid>
</Window>

I am not using MVVM for this example, but instead there is a just a single object created in the code behind for demo purposes.

using System;
using System.Windows;

namespace TestBooleanToVisibilityConverter
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent(); Person p = new Person() { FirstName = "Michael", LastName = "Michaels", Age = "33" };
            PersonViewGrid.DataContext = p;
        }

    }
}

Ok, now build the project and you will see that the Label and TextBox for Age are hidden until you check the box.

Writing your own Bool to Visibility Converter

Sometimes you may need to write you own Converter.  For example, in the above project, it is annoying how the CheckBox moves up and down in position because Visibility.Collapsed is used instead of Visibility.Hidden.  You may want to use Visibility.Hidden instead.

You can write your own Converter that returns Visibility.Hidden instead of Visibility.Collapsed.

BooleanToVisibleOrHidden.cs

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

namespace TestBooleanToVisibilityConverter
{
    class BoolToVisibleOrHidden : IValueConverter
    {
        #region Constructors
        /// <summary>
        /// The default constructor
        /// </summary>
        public BoolToVisibleOrHidden() { }
        #endregion

        #region IValueConverter Members
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool bValue = (bool)value;
            if (bValue)
                return Visibility.Visible;
            else
                return Visibility.Hidden;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Visibility visibility = (Visibility)value;

            if (visibility == Visibility.Visible)
                return true;
            else
                return false;
        }
        #endregion
    }
}

Here we do the conversion ourselves and now we have a different conversion table.

Boolean Visibility
True Visible
Collapsed
False Hidden

Now replace the Converter object in your XAML.  We only change Line 15.

      <local:BoolToVisibleOrHidden x:Key="BoolToVisConverter"/>

This works, but it could be improved. This still leaves us having to choose between two objects.

Creating a Converter that supports a choice of Hidden or Collapsed.

Here we will provide a property that determines if we should collapse or not.

Add a property called Collapse and return the appropriate Visibility based on that property. Here is the new object. As you see, the code changes to add this feature is really just an empty bool property and an if statement that used the bool property.

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

namespace TestBooleanToVisibilityConverter
{
    class BoolToVisibleOrHidden : IValueConverter
    {
        #region Constructors
        /// <summary>
        /// The default constructor
        /// </summary>
        public BoolToVisibleOrHidden() { }
        #endregion

        #region Properties
        public bool Collapse { get; set; }
        #endregion

        #region IValueConverter Members
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool bValue = (bool)value;
            if (bValue)
            {
                return Visibility.Visible;
            }
            else
            {
                if (Collapse)
                    return Visibility.Collapsed;
                else
                    return Visibility.Hidden;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Visibility visibility = (Visibility)value;

            if (visibility == Visibility.Visible)
                return true;
            else
                return false;
        }
        #endregion
    }
}

Now in your XAML you have the option to do nothing, which uses the bool default value false, or to set the Collapse property to true as shown below.

      <local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True"/>

We now support either feature with the following table. We probably at this point would rename the object to BooleanToVisibilityConverter, but Microsoft already took that object name so I will leave it named as is.

Boolean Visibility
True Visible
False – Collapse=True Collapsed
False – Collapse=False Hidden

We are starting to get a little more usability from one object.

Adding the Reverse feature so False is Visible and True is Collapsed or Hidden

Lets say we want to change the CheckBox so that instead of saying “Show Age” it says “Hide Age”.

            <CheckBox Name="ShowAgeCheckBox" Content="Hide Age" />

Now we have to reverse the mapping. If Reverse=”True” we want the mapping to be like this:

Boolean Visibility
False Visible
True – Collapse=True Collapsed
True – Collapse=False Hidden

This is also quite simple. We add another bool property called Reverse. Then key of that in another if statement.

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

namespace TestBooleanToVisibilityConverter
{
    class BoolToVisibleOrHidden : IValueConverter
    {
        #region Constructors
        /// <summary>
        /// The default constructor
        /// </summary>
        public BoolToVisibleOrHidden() { }
        #endregion

        #region Properties
        public bool Collapse { get; set; }
        public bool Reverse { get; set; }
        #endregion

        #region IValueConverter Members
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool bValue = (bool)value;

                if (bValue != Reverse)
                {
                    return Visibility.Visible;
                }
                else
                {
                    if (Collapse)
                        return Visibility.Collapsed;
                    else
                        return Visibility.Hidden;
                }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Visibility visibility = (Visibility)value;

                if (visibility == Visibility.Visible)
                    return !Reverse;
                else
                    return Reverse;
        }
        #endregion
    }
}

Now you can reverse this very easily in the XAML.

      <local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True" Reverse="True" />

And now you have a much more full featured converter.

Additional Thoughts

I have to wonder why the developers didn’t do this originally with the BooleanToVisibilityConverter object. It is so simple. This is a perfect example of where Microsoft would benefit from Open Sourcing some of their code. A dozen people would have contributed this change by now if they had and all Microsoft would have to do is look at the submitted code, approve, and check it in.

WPF Label, TextBox, and Mnemonics

Hello. Today I had to get mnemonic to work. Mnemonic is the ability to navigate to a control using a key stroke combination. One of the most common mnemonics is Alt + F. Alt + F usually navigates to the File drop down menu in any Window, and most everyone is familiar with this mnemonic.

In WPF, to get mnemonics, you pretty much just put an underscore in front of a word. For example, for Alt + F, you would enter: _File

Well, in WPF I needed to do the Label, TextBox matching, so that the mnemonics is on a Label but the key strokes navigate the keyboard focus to the TextBox.

Lets say you have a simple form in WPF like this:

The XAML to make these work together using mnemonics is simple. The important property is Target.

<Window x:Class="Mnemonics.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" >
    <Grid FocusManager.FocusedElement="{Binding ElementName=textBoxFirstName}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Label Target="{Binding ElementName=textBoxFirstName}" HorizontalContentAlignment="Right">
            <AccessText TextWrapping="WrapWithOverflow">First _Name:</AccessText>
        </Label>
        <TextBox Name="textBoxFirstName" Width="200" Height="20" Grid.Column="1"/>
        <Label Target="{Binding ElementName=textBoxLastName}" HorizontalContentAlignment="Right" Grid.Row="1">
            <AccessText TextWrapping="WrapWithOverflow">_Last Name:</AccessText>
        </Label>
        <TextBox Name="textBoxLastName" Width="200" Height="20" Grid.Row="1" Grid.Column="1"/>
    </Grid>
</Window>

Lets discuss the code above briefly.

  1. Notice line 5 first.  Here I am setting the FocusManager.FocusedElement to the first text box so that you can immediately type in the first field when arriving at the window.
  2. Notice the use of AccessText instead of TextBlock (lines 15, 19).  You don’t have to do it this way.  You could have just set the Content property on the label. You don’t need the AccessText at all unless you want to wrap.  If you want to wrap, you have to do it this way.
  3. Notice the use of Target (lines 18,14) and how it is bound to the element that should receive focus when the mnemonic keys are pressed.

Hope this helps you.