Tuesday, August 31, 2010

WPF - Loading Combo Box Items with Enum Members

In this post, we will discuss how we can load the items of a combo box with members of an Enumeration. For developing our applications, we need to show data which might be selected e.g. Gender (Male / Female).

Let's create an enumeration, named Gender in our View Model.

namespace WpfApplication1
{
public class EnumsViewModel
{
public enum Gender
{
Male,
Female
}
}
}

Now let's create a view, EnumsView. We add a combo box and specify the members of the enumeration specified above as its data source. In order to do that, we need to use ObjectDataProvider in the Resource section. We can use GetValues() method of enum to get the members of the enumeration. We specify the key of this object as GenderList. Please don't forget to have a look at how we are accessing nested Gender enumeration with + syntax (local:EnumsViewModel+Gender).

<Window x:Class="WpfApplication1.EnumsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication1"
Title="EnumsView" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider x:Key="GenderList" MethodName="GetValues" ObjectType="{x:Type System:Enum}" >
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:EnumsViewModel+Gender" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<Label Height="22" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" Width="55">Gender</Label>
<ComboBox Height="19" Margin="73,15,57,0" Name="cmbGender" VerticalAlignment="Top" >
<ComboBox.ItemsSource>
<Binding Source="{StaticResource GenderList}" />
</ComboBox.ItemsSource>
</ComboBox>
</Grid>
</Window>

As you can see that we are using the GenderList, the list of members of enum (Gender) as the data source of the Combo box. When we run the application, it is loaded as follows:



It seems WPF has a designer issue with "+" syntax for inner classes. The designer has not been able to load the view when I used this:



This issue is already reported on Connect site:
https://connect.microsoft.com/VisualStudio/feedback/details/361509/xaml-designer-cannot-handle-typename-with-nested-classes?wa=wsignin1.0

Monday, August 30, 2010

WPF - Attached behaviors (Closing View from ViewModel)

There are certain times when View Model has to use the behavior of View. It has to call methods on it. You might say, we should use Binding with the properties in the view model but how to bind to cause a method to be called. As you might have thought that we should be keeping a reference of View in our ViewModel and should use it to call these methods on the view. But doing this we would be breaking the sanctity of MVVM.

The answer to the above problem is Attached Behaviors. With this technique, View Model still does not directly reference to View to call its methods. But it causes it to be executed using the same feature of Binding provided by WPF. The View causes a Data Trigger to be fired for this value change. We update the Value of one of the dependency properties in the View which cause this method to be executed from the ValueChanged event handler of PropertyMetaData for the property.

Simple Example:
Let's see this in action through an example. Let's we have a view model, named ..., and a view, named ... We have a button on the view, We want to close the view when the use clicks the button. Since we want to write the Click action on the View Model, we use RelayCommand and bind it to the button in the View using Binding. The RelayCommand is described by Josh Smith in this article about MVVM:

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090030

Let's create a ViewModel which has a boolean dependency property, named IsClose. It also has a RelayCommand, named CloseCommand which has its canExecute (CanClose) and Execute (Close) actions. The Close action just sets the IsClose dependency property as true.

class AttachedBehaviorViewModel : DependencyObject
{
#region Dependency Properties
public static DependencyProperty IsCloseProperty =
DependencyProperty.Register("IsClose", typeof(bool),
typeof(AttachedBehaviorViewModel), new PropertyMetadata(false));

public bool IsClose
{
get { return (bool)GetValue(IsCloseProperty); }
set { SetValue(IsCloseProperty, value); }
}
#endregion Dependency Properties

#region Commands
RelayCommand _closeCommand;
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
{
_closeCommand = new RelayCommand(param => this.Close(),
param => this.CanClose);
}
return _closeCommand;
}
}

public void Close()
{
IsClose = true;

}

bool CanClose
{
get
{
return true;
}
}
#endregion Commands
}


We create an attached property IsCloseView in a static class WindowAttachedBehavior. It has a PropertyChangedCallBack. It just closes the DependencyObject (Window) causing it to be executed.

public static class WindowAttachedBehavior
{
public static DependencyProperty IsCloseViewProperty =
DependencyProperty.RegisterAttached("IsCloseView", typeof(bool),
typeof(WindowAttachedBehavior), new UIPropertyMetadata(false, OnIsCloseView));


public static bool GetIsCloseView(DependencyObject obj)
{
return (bool)obj.GetValue(IsCloseViewProperty);
}

public static void SetIsCloseView(DependencyObject obj, bool value)
{
obj.SetValue(IsCloseViewProperty, value);
}

public static void OnIsCloseView(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
Window wnd = (Window)d;
if ((bool)e.NewValue)
{
wnd.Close();
}
}
}


Now we create a View, named AttachedBehaviorView which uses the view model described above (AttachedBehaviorViewModel) and the attached command. The most interesting thing is the DataTrigger where it is waiting for a property to be set in the View Model. As soon as this trigger fires, then the attached property IsCloseView is set, which causes our window to be closed.

<Window x:Class="WpfAttachedBehaviors.AttachedBehaviorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfAttachedBehaviors"
DataContext="{DynamicResource ViewModel}"
Title="AttachedBehaviorView" Height="180" Width="300">
<Window.Resources>
<local:AttachedBehaviorViewModel x:Key="ViewModel" />
</Window.Resources>
<Window.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsClose}" Value="true">
<Setter Property="local:WindowAttachedBehavior.IsCloseView" Value="true" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Style>
<Grid>
<Button Margin="12,21,12,28" Name="button1" Content="Close"
Command="{Binding CloseCommand}">

</Button>
</Grid>
</Window>


Sunday, August 29, 2010

WPF - Restricting a Specific Windows Theme

In this post we will discuss how we can target our applications to a particular Windows Theme. We will be restricting it to Royale theme.

First we need to add a reference of PresentationFramework.Royale.dll. Right click References and select "Add Reference".



The assembly should be available in .net tab:



After selecting this should be available in References folder:



After adding the reference, we should merge the resources in App.xaml.

<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="/PresentationFramework.Royale;component/themes/Royale.NormalColor.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>


Now we can design our views. The theme should be automatically applied to the controls used in the application.

<Window x:Class="WPFComboBoxEditable.Window10"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
DataContext="{DynamicResource Data}"
Title="Window7" >
<Window.Resources>
<x:Array x:Key="Data" Type="{x:Type sys:String}">
<sys:String>First</sys:String>
<sys:String>Second</sys:String>
<sys:String>Third</sys:String>
</x:Array>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>

<RowDefinition Height="100" />
<RowDefinition Height="100" />
<RowDefinition Height="100" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.Column="1"></TextBox>
<Button Grid.Row="1" Grid.Column="1">testttt</Button>
<ComboBox Visibility="Visible" Grid.Column="1"
Name="comboBox1" ItemsSource="{Binding}"
Grid.Row="2">
<ComboBox.SelectedIndex>-1</ComboBox.SelectedIndex>
</ComboBox>
</Grid>
</Window>

When we run the application, this should appear as follows:



Note:
If user personalize theme settings then those changes would also be updated to the application.

Saturday, August 28, 2010

WPF Binidng - IsSynchronizedWithCurrentItem with SelectedIndex = -1

In this post, we will discuss another behavior of Binding, when IsSynchronizedWithCurrentItem is used on a combo box for which we don't want to set any default selection by assigning SelectedIndex = -1.
Let's create a window with two combo boxes. We are binding one combo box with some array. We are binding the other combo box with the items in first combo box. To synchronize the selection of both combo boxes, we are setting IsSynchronizedWithCurrentItem on both. Since we don't want any default value to be selected, we are setting SelectedIndex as -1.

<Window x:Class="WPFComboBoxEditable.Window7"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
DataContext="Data"
Title="Window7" Height="300" Width="300">
<Window.Resources>
<x:Array x:Key="Data" Type="{x:Type sys:String}">
<sys:String>First</sys:String>
<sys:String>Second</sys:String>
<sys:String>Third</sys:String>
</x:Array>
</Window.Resources>
<Grid>
<ComboBox Visibility="Visible" Margin="285.527,186.648,0,0"
Name="comboBox1" ItemsSource="{Binding}" Background="Plum" Height="29.997"
IsSynchronizedWithCurrentItem="True"
VerticalAlignment="Top" HorizontalAlignment="Left" Width="191.092" SelectedIndex ="-1" >
</ComboBox>

<ComboBox Visibility="Visible" Margin="0,186.648,337.744,0" Name="comboBox2"
ItemsSource="{Binding ElementName=comboBox1,Path=ItemsSource}"
IsSynchronizedWithCurrentItem="True"
Background="Plum" Height="29.997" VerticalAlignment="Top"
HorizontalAlignment="Right" Width="191.092" SelectedIndex ="-1" >
</ComboBox>
</Grid>
</Window>

This would appear as follows:


You can see that setting SelectedIndex as -1 had no effect. Still first value of the combo box items appears. This is an observed behavior. Now how to fix this?

To fix this, we can set SelectedIndex separately as follows:

<Window x:Class="WPFComboBoxEditable.Window7"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
DataContext="Data"
Title="Window7" Height="300" Width="300">
<Window.Resources>
<x:Array x:Key="Data" Type="{x:Type sys:String}">
<sys:String>First</sys:String>
<sys:String>Second</sys:String>
<sys:String>Third</sys:String>
</x:Array>
</Window.Resources>
<Grid>
<ComboBox Visibility="Visible" Margin="285.527,186.648,0,0"
Name="comboBox1" ItemsSource="{Binding}" Background="Plum" Height="29.997"
IsSynchronizedWithCurrentItem="True"
VerticalAlignment="Top" HorizontalAlignment="Left" Width="191.092" >
<ComboBox.SelectedIndex>-1</ComboBox.SelectedIndex>
</ComboBox>

<ComboBox Visibility="Visible" Margin="0,186.648,337.744,0" Name="comboBox2"
ItemsSource="{Binding ElementName=comboBox1,Path=ItemsSource}"
IsSynchronizedWithCurrentItem="True"
Background="Plum" Height="29.997" VerticalAlignment="Top"
HorizontalAlignment="Right" Width="191.092" >
<ComboBox.SelectedIndex>-1</ComboBox.SelectedIndex>
</ComboBox>
</Grid>
</Window>

You can see that we have specified the value of SelectedIndex as a child tag. Now the window would appear as follows:

WPF - Using Converter and StringFormat together for same Binding

We are so used to using Converters. Sometimes it is easier to use StringFormat then defining a converter (the whole class implementing IValueConverter or IMultiConverter. The unexpected happen when we use them together. Well it should not be unexpected because it is well documented. When we use StringFormat together with Converter then Converter is applied first then StringFormat is applied on the bound data.

http://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.stringformat.aspx

For example, if we apply like this:
<TextBlock Text="{Binding Path=CurrentDate, StringFormat='\{0:MMMM\}', Converter={StaticResource DefaultLowerCaseConverter}}"/>

It would be better if we format within the Converter. In this example, we are converting a date to string in a particular format. We are then formatting the string to lower case.

class DefaultLowerCaseConverter: System.Windows.Data.IValueConverter
{

#region IValueConverter Members

public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return ((DateTime) value).ToString("MMMM").ToLower();
}

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

#endregion
}

We can use this converter like this:
<TextBlock Text="{Binding Path=CurrentDate, Converter={StaticResource DefaultLowerCaseConverter}}"/> 

Sunday, August 22, 2010

WPF - Saving Application Memory by Specifying Decoded Size for Images

We use images alot in our WPF applications. It is nearly always the case that we have to show the image in different size than it was originally created. To do that, we specify Width and Height for <Image>. WPF caches the image. It is interesting to note that it is not cached as displayed. It is cached as its original size. So if we have a 800 X 600 image which we are just showing as thumbnail size of 50 X 50 by specifying the Height and Width of <Image>, we are caching it as 800 X 600. Now assume we have 100s of these images throughout the application. This would be a big loss for the application memory if we don't specify the Decoded Size explicitly.

In this post, we will be using the same image as used in the previous post. The image is as follows:


Let us instantiate this as a BitmapImage in the Resources section of Window.

<Window x:Class="WPFComboBoxEditable.WindowImageDecodedSize"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowImageDecodedSize" >
<Window.Resources>
<BitmapImage UriSource="C:\Chundrigar.jpg" x:Key="image1"
DecodePixelWidth="200" DecodePixelHeight="150" />
</Window.Resources>
<Grid>
<Image Source="{StaticResource image1}" Height="200" Width="150" />
</Grid>
</Window>

As you can see that we have specified the DecodePixelWidth and DecodePixelHeight for BitmapImage. This would cache the image as the size specified. Since we are showing the image in the same size in <Image>, it would be shown as follows:



This would definitely save a lot in terms of memory usage of the application.

WPF - Image Format Conversion (Including Gray Scaling)

In this post we will be discussing about how we can change the format of an image using XAML in WPF. The format conversion is performed using FormatConvertedBitmap. The destination formats are specified using DestinationFormat (dependency property) of FormatConvertedBitmap. It can be assigned various values from enumeration PixelFormats in System.Windows.Media namespace. Different pixel formats and their description can be found here:

http://msdn.microsoft.com/en-us/library/system.windows.media.pixelformats_members.aspx

Let's use the following image and see the effects of application of various formatting option on this. [This is night view of I.I.Chundrigar road @ Karachi].



To apply these formats to an image, we have to create a FormatConvertedBitmap instance in the relevant Resources section. We can apply different formatting options during initialization. We can then specify it as source of an image.

For our example, let us use three different Pixel formats and specify it to the same image as above. The XAML code of sample Window is as follows:

<Window x:Class="WPFComboBoxEditable.WindowGreyScale"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowGreyScale">
<Window.Resources>
<BitmapImage UriSource="C:\Chundrigar.jpg" x:Key="image1" />
<FormatConvertedBitmap Source="{StaticResource image1}" x:Key="formattedImage" DestinationFormat="Gray32Float" />
<FormatConvertedBitmap Source="{StaticResource image1}" x:Key="formattedImage2" DestinationFormat="BlackWhite" />
<FormatConvertedBitmap Source="{StaticResource image1}" x:Key="formattedImage3" DestinationFormat="Cmyk32" />
</Window.Resources>
<Grid>
<ScrollViewer>
<StackPanel Orientation="Vertical">
<Image Source="{StaticResource image1}" Height="400" Width="400" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Image Source="{StaticResource formattedImage}" Height="400" Width="400" />
<Image Source="{StaticResource formattedImage2}" Height="400" Width="400" />
<Image Source="{StaticResource formattedImage3}" Height="400" Width="400" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>


So we have created an instance of BitmapImage. Then we have created three different formatted images by applying different formats to the image. These formats include:
- Gray32Float
- BlackWhite
- Cmyk32

Afterwards, we have specified these formatted images as image sources of Images. When we run the application, the window is displayed as follows:



By the way, Gray32Float format looks really cool on this image :)

WPF - Images from Project Resource

In this post we will discuss how to use images stored in the project resource in our WPF application. We will be creating a simple sample application which would get an image from project resource and display it in a WPF window.

First let's use an image and specify it as a project resource. [By the way this is the image of great historical site at Lahore called Minar-e-Pakistan. So if you visit Lahore, don't forget to see this.]



Let's save this image as Project Resource. To do that we can copy and paste the image or add as existing item in Resource section of Project. Just take the properties of the project and go to the Resource tab.



This is interesting to note that the images exposed by Project properties are System.Drawing.Bitmap type. So we need to define a converter to convert it to BitmapImage. So let's define an IValueConverter.

public class WPFBitmapConverter : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
MemoryStream ms = new MemoryStream();
((System.Drawing.Bitmap)value).Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
BitmapImage image = new BitmapImage();
image.BeginInit();
ms.Seek(0, SeekOrigin.Begin);
image.StreamSource = ms;
image.EndInit();

return image;
}

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

#endregion
}

In Convert() method, we have used a System.IO.MemoryStream to convert from System.Drawing.Bitmap to System.Windows.Media.Imaging.BitmapImage.

Now we add a Window named WindowImageProjectResource.xaml. We are initializing the converter in the Resources section of the Window. We are using this as the image converter during binding of the image with the image in the project resource.

<Window x:Class="WPFComboBoxEditable.WindowImageProjectResource"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ProjectProperties="clr-namespace:WPFComboBoxEditable.Properties"
xmlns:local="clr-namespace:WPFComboBoxEditable"
Title="WindowImageProjectResource" Height="368" Width="616">
<Window.Resources>
<local:WPFBitmapConverter x:Key="ImageConverter" />
</Window.Resources>
<Grid>
<Image Margin="2,3,4,4" Name="image1" Stretch="Fill" >
<Image.Source>
<Binding Source="{x:Static ProjectProperties:Resources.Minar}"
Converter="{StaticResource ImageConverter}" />
</Image.Source>
</Image>
</Grid>
</Window>

As we run the application, the window is displayed with image (Minar) as follows:

Wednesday, August 18, 2010

x:Shared for Sharing Resources

WPF has many cool features. One such feature is defining Resources. We can define content & data templates, styles and objects (including FrameworkElement instances) to be later used. Whenever WPF application demands the resource, runtime fulfills the request by gettting an instance from these resources. We can define these resources anywhere in the logical hierarchy, at application level or at cross-application level. It is good to know that a single instance is created for these resources and it is shared for different requests. Basically WPF runtime fulfills the request of resource by providing the same instance to the requester. There might be circumstances when we want new instance for each request e.g. we might define a FrameworkElement in Resource section and want a new instance every time it is requested. x:Shared is there for all those circumstances.

<Window x:Class="WPFComboBoxEditable.WindowXShared"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowXShared" Height="356" Width="547">
<Window.Resources>
<Rectangle x:Shared="false" x:Key="rect" Width="20" Height="20" Fill="Green" />
</Window.Resources>
<StackPanel>
<Border Child="{StaticResource rect}" />
<Button Content="{StaticResource rect}" />
<Button Content="{StaticResource rect}" />
</StackPanel>
</Window>




Limitation:
It must be remembered that we can only set x:Shared in XAML. It can not be set through code.

Visual Studio 2008 seems issues with usage of resources using x:Shared. That is why even if we disallow sharing [using x:Shared = false], it still can not load the designer.



It also shows it as error when we try to Load the Designer. Don't worry, it builds and run fine.



XAML also highlights this as error. If we hover over the highlighted text, it shows the error message. Remember this is just designer issue that it does not support that. As discussed above, the build should be fine.


Note:
The default value of x:Shared is true (x:Shared = true) that is why the resources are shared among all the components using them.

Sunday, August 15, 2010

DrawingContext & ShaderEffect

DrawingContext does not support ShaderEffect.

Using DrawingContext in OnRender() method of adorner, we can Push effect using PushEffect() method. But the strange thing is that DrawingContext.PushEffect() does not support ShaderEffect. All it supports is BitmapEffect :(

So if you don't want to use BitmapEffect as it is obsolete now then you DrawingContext wouldn't be of much use to you.

http://msdn.microsoft.com/en-us/library/system.windows.media.drawingcontext.pusheffect.aspx

If you want to study about ShaderEffect, you can refer this post:

http://shujaatsiddiqi.blogspot.com/2010/08/wpf-uielements-effects.html

Friday, August 13, 2010

WPF - Editable ComboBox with DataTemplate

In this post, we will discuss with issue when we define data template for a combo box and make it editable. When we select the item from combo box, it shows class name. Let's create a view with editable combo box. We are defining the DataTemplate of items in the ComboBox and displaying them in a TextBlock. We can also define several style formatting with item data template.
<Window x:Class="WPFComboBoxEditable.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFComboBoxEditable"
Title="Window1" Height="300" Width="300"
DataContext="{DynamicResource ViewModel}" >
<Window.Resources>
<local:Window1ViewModel x:Key="ViewModel" />
</Window.Resources>
<Grid>
<Label HorizontalAlignment="Left" Margin="12,13,0,0" Name="label1" Width="62" Height="24" VerticalAlignment="Top">Student</Label>
<ComboBox Height="25" Margin="80,12,12,0" Name="comboBox1" VerticalAlignment="Top"
ItemsSource="{Binding StudentList}" IsEditable="True">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StudentName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Window>

This view is expecting a DataContext (Window1ViewModel) with list StudentList. This is a list of Student objects.
public class Window1ViewModel : DependencyObject
{
public static DependencyProperty StudentListProperty =
DependencyProperty.Register("StudentList", typeof(List), typeof(Window1ViewModel));

public List StudentList
{
get { return (List)GetValue(StudentListProperty); }
set { SetValue(StudentListProperty, value); }
}

public Window1ViewModel()
{
StudentList = new List();
StudentList.Add(new Student() { StudentName = "Amir" });
StudentList.Add(new Student() { StudentName = "Asif" });
StudentList.Add(new Student() { StudentName = "Catherine" });
StudentList.Add(new Student() { StudentName = "Cindrella" });
StudentList.Add(new Student() { StudentName = "David" });
StudentList.Add(new Student() { StudentName = "Ellis" });
StudentList.Add(new Student() { StudentName = "Farooq" });
StudentList.Add(new Student() { StudentName = "Muhammad" });
StudentList.Add(new Student() { StudentName = "Saleem" });
StudentList.Add(new Student() { StudentName = "Usman" });
}
}

The Student class used above can be defined as follows:
public class Student
{
public string StudentName { get; set; }
}

When we run this, it is shown like this:


Everything is fine up until now. As soon as we select any item from the list, the unexpected happens.


This can be fixed by setting TextPath of TextSearch to the property you want to use for searching.
TextSearch.TextPath="StudentName"

This is updated in the combox box declaration as follows:

<ComboBox Height="25" Margin="80,12,12,0" Name="comboBox1" VerticalAlignment="Top"
ItemsSource="{Binding StudentList}" IsEditable="True" TextSearch.TextPath="StudentName">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StudentName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

After the application of above update, running the application and selecting the combobox item, the correct item seems selected.



Note:
After finding the fix, it seems an obvious solution because it makes sense that WPF runtime would not know what property to use for selection because it is based on a data template.

Thursday, August 12, 2010

WPF Localization - StringFormat for Formatting Currency

In this post we will be discussing how we can format currency in different localized currency formats. I will be using same resource files as in the previous post but would be adding one resource string to it. This resource string is PaymentLabel.

Culture ur-PK



Culture Neutral:



Culture es-ES


Now we use the resource strings and create our view. Please have a special look at the binding of textbox3. As you can see that we are specifically formatting the string using StringFormat = "c" in the Binding section of Text property.
<Window x:Class="WPFLocalization.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFLocalization"
Title="Window1" Height="300" Width="300"
DataContext="{DynamicResource ViewModel}" >
<Window.Resources>
<local:WPFLocalizationViewModel x:Key="ViewModel" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="42*" />
<RowDefinition Height="33*" />
<RowDefinition Height="187*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="88*" />
<ColumnDefinition Width="190*" />
</Grid.ColumnDefinitions>
<Label Margin="12,12,3,5" Name="label1" Content="{x:Static local:WindowStaticText.NameLabel}" Grid.Row="0" Grid.Column="0"/>
<TextBox Margin="4,12,8,5" Name="textBox1" Grid.Column="1" />
<Label Margin="5,1,0,8" Name="label2" Content="{x:Static local:WindowStaticText.FatherNameLabel}" Grid.Row="1" />
<TextBox Grid.Column="1" Grid.Row="1" Margin="4,3,8,8" Name="textBox2" />
<Label Grid.Row="2" Height="24" Margin="8,4,3,0" Name="label3" VerticalAlignment="Top"
Content="{x:Static local:WindowStaticText.PaymentLabel}"></Label>
<TextBox Grid.Column="1" Grid.Row="2" Height="24" Margin="4,7,8,0" Name="textBox3" VerticalAlignment="Top" >
<TextBox.Text>
<Binding Path="Payment" StringFormat="c" /> <!--ConverterCulture="ur-PK"-->
</TextBox.Text>
</TextBox>
</Grid>
</Window>

The view model used in the above xaml is as follows. It has a dependency property Payment containing the payment information. This is desired to be shown in currency format in the view.

public class WPFLocalizationViewModel : DependencyObject
{
public decimal? Payment
{
get { return (decimal?)GetValue(PaymentProperty); }
set { SetValue(PaymentProperty, value); }
}
public static DependencyProperty PaymentProperty =
DependencyProperty.Register("Payment", typeof(decimal?), typeof(WPFLocalizationViewModel));

public WPFLocalizationViewModel()
{
this.Payment = 25001m;
}
}

Enforcing UI Culture for StringFormat:
It seems StringFormat avoids the CurrentUICulture settings. In order to enforce it, we have two options.

1. Set XML language for XAML to the current culture. This is used to tell XAML to use culture specific format strings localization (such as date formats, decimal symbol, etc). This setting would be applied for all XAML loaded after setting this.

FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(CultureInfo.CurrentUICulture.IetfLanguageTag)));

2. Specify the culture as ConverterCutlure attribute of Binding. This culture would not only apply to converter but StringFormat also seems to be using the same culture settings.
<Binding Path="Payment" StringFormat="c" ConverterCulture="ur-PK" />

Running the Application:
Running the application would result in different formatting based on the UI culture. You can set the UI culture before the XAML is loaded. You can set it in the constructor of the window before InitializeComponent() is called.

public Window1()
{
System.Threading.Thread.CurrentThread.CurrentUICulture =
new System.Globalization.CultureInfo("ur-PK");

if (Thread.CurrentThread.CurrentUICulture.TextInfo.IsRightToLeft == true)
{
this.FlowDirection = System.Windows.FlowDirection.RightToLeft;
}

InitializeComponent();
}

Now run the application and see the Payment being displayed in different culture specific currency formats.

Culture ur-PK



Culture es-ES



Culture Neutral



Note:
It must be remembered that if Converter and StringFormat are used simultaneously with Binding then first the value is converted using Converter and then formatted using StringFormat.

Tuesday, August 10, 2010

WPF Localization - Using RESX

English has become a global language. People communicate using this language if they are in different parts of the world. The problem is that it has not yet become the only language of the world. There are many languages spoken in the nooks and corners of the world. People like to communicate, read poetry and do many other wonderful things because they can't or don't want to use a foreign language. As engineers, we still need to get scientific solutions to them. To consider this need softwares are localized for different cultures and sub-cultures. WPF also supports that. In this discussion, we will be discussing how we can use RESX files to localize our applications.

Let's create three resource files (*.resx) as follows:

1. WindowStaticText.resx (Culture Neutral)
2. WindowStaticText.es-ES.resx (Spanish - es-ES)
3. WindowStaticText.ur-PK.resx (Urdu - ur-PK)

Basically the .net framework will use any of the resource file by comparing the culture between the name of resource file and the culture of current UI thread. If no culture is matched, it would use the culture neutral file (WindowStaticText.resx in this case). I have used google/translate to translate the text in these languages.

http://translate.google.com/

I create two resource strings in all these resource files as follows:

Culture Neutral:



Spanish - es-ES


Urdu - ur-PK



Urdu is written from Right To Left unlike English and Spanish. If you could understand the language then you could realize that the letters are flowing from left to right in the resource file. But there is no need to worry because the runtime would load them just fine in the application (Right to Left).

UI Culture:
The application should be reading the culture information seamlessly from regional settings of the machine. We can also change the culture of UIThread as follows:
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("es-ES");
If you are planning to change the culture through code then you must realize that for reading each item from the resource file, the runtime makes culture check to decide about source resource file (resx). So if you have to get a few items from the resource file and you set culture information after some items are already loaded then the runtime would not go back and update the already loaded information and would apply the new culture (and hence use the new culture specific resource file) only for the items loaded afterwards. In this example, similarly, since we are setting the contents of labels in XAML, we need to set the UI culture before initializeComponent() of the Window gets called.

WPF Window using RESX file:

<Window x:Class="WPFLocalization.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFLocalization"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="42*" />
<RowDefinition Height="220*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="88*" />
<ColumnDefinition Width="190*" />
</Grid.ColumnDefinitions>
<Label Margin="12,12,3,5" Name="label1" Content="{x:Static local:WindowStaticText.NameLabel}" Grid.Row="0" Grid.Column="0"/>
<TextBox Margin="4,12,8,5" Name="textBox1" Grid.Column="1" />
<Label Height="24" Margin="5,1,0,0" Name="label2"
VerticalAlignment="Top" Content="{x:Static local:WindowStaticText.FatherNameLabel}" Grid.Row="1" />
<TextBox Grid.Column="1" Grid.Row="1" Height="22" Margin="4,3,8,0" Name="textBox2" VerticalAlignment="Top" />
</Grid>
</Window>

When you run the application, it should appear as follows:

Urdu (ur-PK)


Spanish (es-ES)


Neutral:


The world is not a very simple place to live. We are used to writing languages from left to right. There are languages which are written from right to left, there are others which are written from top to bottom. We can check if any language is written from right to left by checking IsRightToLeft property of TextInfo available in CurrentUICulture. We can use it define flow direction of window.


if (Thread.CurrentThread.CurrentUICulture.TextInfo.IsRightToLeft == true)
{
this.FlowDirection = System.Windows.FlowDirection.RightToLeft;
}




Note:
If you want to learn more about how .net framework package and deploy resources, you can read the following msdn article. This discusses how .net framework uses hub and spoke model for resources.

http://msdn.microsoft.com/en-us/library/sb6a8618%28v=VS.71%29.aspx

Thursday, August 5, 2010

Spell Check for WPF Text entry

WPF provides support of spell checking for the text entered by user. This spell check feature is independent of any software already installed (like Microsoft office). If we enter any misspelled word then it is underlined grasping immediate user attention by providing the visual cue. Enabling this feature would result in the behavior as below if any word is misspelled:



Enabling Spell Check:
Spell check can be enabled by setting IsEnabled property of SpellCheck object in TextBox to true.
<TextBox Height="138" Name="textBox1" Width="451" SpellCheck.IsEnabled="True" />

Spell check can also be enabled / disabled through code. Just set the IsEnabled property of the SpellCheck in TextBoxBase to True for enabling spell checking and false otherwise.

Spelling and Language reforms:
This refers to French and German language reforms. If we set this property for any other language, its value has no effect. The value is set based on enum. The possible values are:

- PreAndPostreform: Default value for French language. Uses the combination of spelling as before and after the reforms in spellings.

- Prereform: Only uses the spellings before the reform.

- Postreform: Default value for German language. It only uses the spelling after the reform in the language.

Specifying language for spell check:
We might specify the language for spell checking by setting the Language attribute of the FrameworkElement. The spell checking for different cultures are different even if the language is same, e.g. U.S English spellings can be specified as en-US and the British spelling might be set as en-GB.

Let's develop a window with two text boxes. Both text boxes check spelling for the text entered. One text box is specified with U.S. English as language and the other is specified as British English.

<Window x:Class="WPFSpellCheck.WindowSpellCheck"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowSpellCheck" Height="367" Width="631">
<Grid Height="330">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="330*" />
<ColumnDefinition Width="68*" />
<ColumnDefinition Width="211*" />
</Grid.ColumnDefinitions>
<Label Height="26" HorizontalAlignment="Left" Margin="12,4,0,0" Name="label1"
VerticalAlignment="Top" Width="151">British English Spell Check</Label>
<TextBox Margin="14,26,12,0" Name="textBox1" SpellCheck.IsEnabled="True" Language="en-GB" Grid.ColumnSpan="3" Height="135" VerticalAlignment="Top" />
<Label HorizontalAlignment="Left" Margin="13,163,0,141" Name="label2" Width="136">U.S. English Spell Check</Label>
<TextBox Margin="14,0,12,12" Name="textBox2" Height="133" VerticalAlignment="Bottom"
SpellCheck.IsEnabled="True" Language="en-US" Grid.ColumnSpan="3" />
</Grid>
</Window>

When we run this the following Window appears. There are various words which are spelled differently in british and american english e.g. colour (UK) and color (US). So as soon as we type color in first text box, it is highlighted as misspelled. You can see the otherwise effect in the second text box.


You might need to install language pack for the intended language based on your operating system. They are required for WinXP. The language packs for .net framework 3.5 sp1 can be downloaded from here:

http://www.microsoft.com/downloads/details.aspx?FamilyID=8489ed13-b831-4855-96f7-dd35e4c02a20&displaylang=en

The installation experience of the language pack is in the same language. So if you are installing Arabic language pack then the installer's instructions are also in Arabic and there seems to be no option to change the language. This might be some issues for developers who are developing applications for languages they don't understand. Let's download the Spanish Language pack and use it from the link above.

<Window x:Class="WPFSpellCheck.WindowSpellCheck"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowSpellCheck" Height="367" Width="631">
<Grid Height="330">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="330*" />
<ColumnDefinition Width="68*" />
<ColumnDefinition Width="211*" />
</Grid.ColumnDefinitions>
<Label Height="26" HorizontalAlignment="Left" Margin="12,4,0,0" Name="label1"
VerticalAlignment="Top" Width="151">U.S. English Spell Check</Label>
<TextBox Margin="14,26,12,0" Name="textBox1" SpellCheck.IsEnabled="True" Language="en-US" Grid.ColumnSpan="3" Height="135" VerticalAlignment="Top" />
<Label HorizontalAlignment="Left" Margin="13,163,0,141" Name="label2" Width="136">Spanish Spell Check</Label>
<TextBox Margin="14,0,12,12" Name="textBox2" Height="133" VerticalAlignment="Bottom"
SpellCheck.IsEnabled="True" Language="es-ES" Grid.ColumnSpan="3" />
</Grid>
</Window>

So we have created two text boxes, the first is expecting the text entry in U.S english and the other is expecting text entry in Espanol (Spanish). When you run the application, it appears as follows:



Note:
If you are using Language pack for supporting a particular language then you must not forget to provide support of installation of the language pack during installation of your software.

Custom Dictionaries:
.Net 4.0 added support of adding custom dictionary to augment the spell check feature of WPF. All you have to define is a lexicon file (*.lex). The list of words in the lexicon file would be considered as legitimate and wouldn't be marked as error providing error visual cues.
<TextBox x:Name="textBox1" SpellCheck.IsEnabled="True">
<SpellCheck.CustomDictionaries>
<sys:Uri>dictionaries\dictionary.lex</sys:Uri>
</SpellCheck.CustomDictionaries>
</TextBox>

Here dictionary.lex is a lexicon file. By default the list of words in the lexicon file would not be marked as spelling error irrespective of the Language. However, you can set so that the lexicon file is applied for a specific locale. This is specified by setting locale ID (#LID) followed by the language specific number in the beginning of lexicon file.

#LID 4106 (Spanish - Guatemala)
#LID 1031 (German - Germany)
#LID 1036 (French - France)
#LID 1033 (English - United States)
#LID 2057 (English - United Kingdom)
#LID 1056 (Urdu) (Not supported by WPF Spell Check yet)

The reference chart of locale - ids is available here:

http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx

There is no settings for providing Add to Dictionary to the user. But this feature might be provided by adding a MenuItem to the context menu of the text box. When the user selects this option, you might add the word to the same lexicon file being used. You might get the error at the current caret location in the text box as follows:
SpellingError error = this.textBox1.GetSpellingError(this.textBox1.CaretIndex - 1);

SpellingError not only gives you the error at the specified index. It also provides the list of suggestions (correct word). You can correct the word using Correct() method of SpellingError object (error in above code). As in the code below, we are correcting the error with the first suggestion available:
error.Correct(error.Suggestions.First());

Limitations:
WPF spell checker (even in .net 4.0) is only limited to four languages. These languages include English, Spanish, French and German. This means that even if you install the language pack for the language (e.g. Arabic), it just wouldn't work.

Monday, August 2, 2010

WPF UIElements Effects

WPF controls are lookless. We can apply different styles / templates to drastically change the appearance of the control. We can also update these styles and templates based on certain user or system events. These UIElements also supports different effects to be applied to them. We can define various effects like we can define the buttons to have shadows. We might want to blur / swirl them to be replaced by any other UIElement. This provides richness to the user interface as well it allows creation of cool animations.

Assembly:
System.Windows.Media.Effects

Built-In effects:
.Net framework 3.5 sp1 released with two built-in effects for UIElements. Both of these effects inherit from Effect class in System.Windows.Media.Effects namespace. That means they both use hardware based rendering. They are as follows:
  1. Drop Shadow effect
  2. Blur effect

Drop Shadow Effect:
We can control the color, opacity, depth and direction of the shadow. The Direction is double value and its value is specified in degrees.
Let us create a button with Burlywood shadow color. We are not specifying the value of Direction so the runtime would consider its default value (315 degrees)
<Button Height="33" Margin="35,40,52,0" Name="button1" VerticalAlignment="Top" Content="Button with Drop Shadow Effect">
<Button.Effect>
<DropShadowEffect ShadowDepth="5" Color="BurlyWood" Opacity=".4" RenderingBias="Performance" />
</Button.Effect>
</Button>



Blur Effect:
The other built-in effect available is BlurEffect. For setting gaussian blur, we can set KernelType property to Gaussian. The more we set the value of Radius the more blurred the UIElement would be rendered.
<Button Margin="35,116,83,112" Name="button2" Content="Button with Blur effect">
<Button.Effect>
<BlurEffect Radius="2" KernelType="Gaussian" />
</Button.Effect>
</Button>


Note:
Like the example of Button above, other UIElements also support effects but the behavior might be a little different e.g. when DropShadowEffect is applied to a textblock, each character has a drop shadow instead of the whole UIElement.
<TextBlock Height="29" Margin="36,0,64,58" Name="textBlock1" VerticalAlignment="Bottom" Text="text block" >
<TextBlock.Effect>
<DropShadowEffect />
</TextBlock.Effect>
</TextBlock>



GPU based Acceleration:
These effects are hardware acclerated. The processing capability of GPU is used to process these effects. GPUs use pixel shaders to generate those effects. As you can see in my post below that these GPU operations are SIMD operations. The use of SIMD operation logic makes the operations execution real fast.

http://shujaatsiddiqi.blogspot.com/2010/05/microsoft-accelerator-research-project.html

We can also think about the possibility of leveraging Microsoft Accelerator as discussed in above post for these effects.

Custom Effects:
All WPF hardware accelerated effects are inherited from Effect class . The DropShadowEffect and BlurEffect are both inherited from the same class. In order to create custom effects, we need to inherit from ShaderEffect, which also inherits from Effect class.



You might find other names like BitmapEffect. They use software based rendering. They also require full trust permissions. They also have other issues with runtime updates in the visual hierarchies. These are the issues resulting them being obsolete in framework 4.0.

To create custom effects, we need to write the code for pixel shaders which is written in HLSL, a C-based language. Now this can be difficult if we start writing HLSL code by hand. This can be easier if we could use some utilities and tools provided by WPF / Silverlight community (e.g. Shazzam).

http://shazzam-tool.com/

We simply need two items from shazzam. First is the pixel shader (*.ps) and a class inheriting from ShaderEffect which uses this pixel shader. We can provide shazzam the HLSL code in the form of a *.fx file or just copy and paste on Shazzam window. As soon as we compile it, Shazzam generates both of them for us. We can also test the effect by testing harness provided.

There are different sample shaders available with Shazam. Let's use Swirl shader. As discussed above, compiling it would generate Swirl.ps [Tools -> Explore Compiled Shaders (*.ps)] and C# code.


Add the shader (*.ps) file to any folder and add it as an existing item to your project. Change the Build Action for this file to "Resource". Now copy and paste the C# code to any code file in you project.



Now we can use this effect class (SwirlEffect) as an effect for our UIElements. Let's use it for a button control:

<Button Height="37" Margin="38,0,34,12" Name="button3" VerticalAlignment="Bottom"
Content="Button with Swirl effect">
<Button.Effect>
<local:SwirlEffect SpiralStrength="0.5" AspectRatio="1.5" />
</Button.Effect>
</Button>

This button would look like this:

Animating Effects:
Like other UIElements' properties, effect's properties can also be animated. Let us animate the the first button which uses the drop shadow effect.

<Button Height="33" Margin="35,40,52,0" Name="button1" VerticalAlignment="Top"
Content="Button with Drop Shadow Effect">
<Button.Style>
<Style>
<Style.Triggers>
<Trigger Property="Button.IsMouseOver" Value="true">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Effect.Color"
From="#FF231244" To="#FFFFFFFF"
Duration="0:0:1"
/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Effect.Color"
From="#FFFFFFFF" To="#FF231244"
Duration="0:0:1"
/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Effect>
<DropShadowEffect ShadowDepth="5" Color="BurlyWood" BlurRadius="2" Opacity=".4"
RenderingBias="Performance" />
</Button.Effect>
</Button>


Now as soon as you mouse over the button, it changes its drop shadows. As soon as you take mouse away, it reverts back to its original state.



Similarly, we can create animation for Blur effect as follows:
<Button Margin="35,116,83,112" Name="button2" Content="Button with Blur effect">
<Button.Style>
<Style>
<Style.Triggers>
<Trigger Property="Button.IsMouseOver" Value="true">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Effect.Radius"
From="2" To="50"
Duration="0:0:1"
/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Effect.Radius"
From="50" To="2"
By="1"
Duration="0:0:1"
/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Effect>
<BlurEffect Radius="2" KernelType="Gaussian" x:Name="GEffect" />
</Button.Effect>
</Button>

The mouse over effect of above animation should be as follows:


Note:
For simplicity of example, we have created these animations like these. You can create these styles anywhere in the resource section of the logical hierarchy. They can also be defined in the resource dictionary incase you want to share these styles across different applications.

Performance considerations:
Effects are cool. They provide richness to WPF based applications. But they can be expensive. I mean, e.g. it is recommended that instead of DropShadowEffect for simple UIElement we can use another Geometry shape along with UIElement. Basically what happens when we apply effects on UIElement, WPF runtime rasterizes the UIElement into a bitmap and apply operations on each pixel. They might be simple arithmetic, trigonometric or any other operations. Applying these operations might affect the responsiveness of the application.

Unlike DirectX, WPF has the fallback mechanism of using Software rendering pipeline for effects if pixel shaders are not supported by the GPU in the machine running the application. This is really a great but we must test our applications on such systems before releasing them for client so that we could find out if they have acceptable responsiveness under all the possible platforms.