Ugrás a tartalomhoz

.NET Programming Technologies

Gergely Kovásznai, Csaba Biró

Eszterházy Károly College

Converters

Converters

In the above example, it is not easy to notice a hidden WPF service, namely the automatic conversion between data of certain types. As we all know, the TextBox.Text property is a string, while Slider.Value is a double. It is very convenient that programmers do not have to implement such obvious conversions.

However, there are cases when it is not so obvious to realize an appropriate conversion, and, furthermore, it would be desirable if WPF provided opportunities to customize data conversion. Consider the following simple example: let us assume that we are implementing a management software for a German company, and the price of a product (Product.Price) is represented as a double, but should be displayed in the German form of currencies (i.e., truncated to two decimal points and divided into groups of thousands). Therefore we need to create an own converter, a new class called, for instance, PriceConverter, which must implement the IValueConverter interface (in the System.Windows.Data namespace).

class PriceConverter : IValueConverter

{

        public object Convert(object value, Type targetType, object parameter,

                                                                                System.Globalization.CultureInfo culture)

        {

                double price = (double)value;

                return price.ToString("c", culture);

        }

        public object ConvertBack(object value, Type targetType, object parameter,

                                                                                System.Globalization.CultureInfo culture)

        {

                string price = value as string;

                return double.Parse(price);

        }

}

As it can be infered from the above source code, the IValueConverter interface has two methods: Convert converts the source data, while ConvertBack converts the target data. They receive the data they should convert in their value parameter; since both methods must be universal to use, the type of value is object, and, hence, we always need to cast it to the required type. Since, in the current example, the source data (Product.Price) is double, at the beginning of Convert the value parameter is converted to double; and to string in ConvertBack, since the target data (TextBox.Text) is a string.

At the end of Convert, the double data is formatted with respect to the currency format. For this, it is the simplest to use an appropriate .NET service, namely the opportunity for passing a format string as a parameter to the ToString method. The c format string[2] stands for „currency”, and makes the data formatted with respect to system settings and formatting rules used in a given language or region. For instance, if we use Windows with Hungarian language settings, then data is formatted w.r.t. the rules in Hungary. How to force our application, for example, to format prices w.r.t. the German rules? For this, one can use the Language attribute of each GUI element (as the target object of data binding); this attribute is to specify a language/region, and we are now setting it to de[3]. The data binding in XAML is going to look like as follows:

<TextBox Language="de">

        <TextBox.Text>

                <Binding Source="{x:Static Member=my:Product.prodToSale}" Path="Price">

                        <Binding.Converter>

                                <my:PriceConverter/>

                        </Binding.Converter>

                </Binding>

        </TextBox.Text>

</TextBox>

In the above example, Binding is specified by not using markup extension, on purpose. The reason is that we need to instantiate the PriceConverter class, and this is automatically done when using an XAML tag. Nevertheless, the above source code can demonstrate how to use data binding in the traditional way. If you would rather like to use markup extension, then it is worth to apply the following solution, which is the most popular in literature: to add a PriceConverter instance as a resource to our form or application, and later to reference it at every location where we need a PriceConverter.

<Window.Resources>

        <my:PriceConverter x:Key="priceConverterObject"/>

</Window.Resources>

        ...

<TextBox Language="en-US" Text="{Binding

                 Source={x:Static my:Product.prodToSale},

                 Path=Salary,

                 Converter={StaticResource ResourceKey=priceConverterObject}

}"/>

A Complex Example. Let us realize the form in Figure 2. We want to display an image (in the Image at the bottom) which belongs to the dog breed selected in the ListBox. It is a nice solution to create a Dog class in order to represent dog breeds; let this class have two properties called Name and ImageName. For the sake of example, let us introduce the Dog.dogs static list, into which we push a few dog breeds.

class Dog

{

        public string Name { set; get; }

        public string ImageName { set; get; }

        public static List<Dog> dogs = new List<Dog> {

                new Dog { Name="Bernese Mountain Dog", ImageName="bernese.jpg" },

                new Dog { Name="Chihuahua", ImageName="chihuahua.jpg" },

                new Dog { Name="Great Dane", ImageName="dane.jpg" },

                new Dog { Name="Komondor", ImageName="komondor.jpg" }

        };

}

The corresponding image files will be put next to the compiled program, into the images directory.

XIII.2. Data binding between a ListBox and an Image

First, we bind the Dog.dogs list to our ListBox, as can be seen in the 5th line in the XAML code below. The ItemSource and DisplayMemberPath attributes of list controls will be detailed in the next section; currently the point is that the ListBox receives its items from the Dog.dogs list, and displays their Name property.

<Window.Resources>

        <my:DogToImageConverter x:Key="dogConverterObject"/>

</Window.Resources>

        ...

<ListBox Name="dogListBox"

                 ItemsSource="{x:Static my:Dog.dogs}" DisplayMemberPath="Name"/>

<Image Source="{Binding

                ElementName=dogListBox,

                Path=SelectedItem,

                Converter={StaticResource dogConverterObject}

}"/>

The Image will show the picture of the currently selected item (SelectedItem) of the ListBox. At this point, it is advantageous to use a converter, since the source of the data binding in the 7th line is a Dog object, while its target is of type ImageSource. Our converter can be implemented as follows:

class DogToImageConverter : IValueConverter

{

        public object Convert(object value, Type targetType, object parameter,

                                                                                        System.Globalization.CultureInfo culture)

        {

                Dog dog = value as Dog;

                if (dog == null) return null;

                string imagepath = Path.Combine(Directory.GetCurrentDirectory(),

                                                                                                        "images", dog.ImageName);

                return new BitmapImage(new Uri(imagepath));

        }

        public object ConvertBack(object value, Type targetType, object parameter,

                                                                                    System.Globalization.CultureInfo culture)

        {

                throw new NotImplementedException();

        }

}

As can be seen, the Dog object passed in the value parameter is “converted” to a BitmapImage object by concatenating the current directory, the images directory, and the name of the image file that belongs to the Dog object. Note that backward conversion is disabled; consequently, even Mode=OneWay could have been used in the data binding for the Image.



[2] There is a bunch of different format strings. One can easily find references and tutorials on the internet, e.g., http://msdn.microsoft.com/en-us/library/26etazsy.

[3] One can find the language/region codes on the internet, e.g., http://msdn.microsoft.com/en-us/goglobal/bb896001.aspx. The code of the Hungarian language is hu-HU, the one of the American English is en-US.