Showing posts with label Converter. Show all posts
Showing posts with label Converter. Show all posts

Sunday, March 2, 2014

Converters in WPF

Converters give you a lot supremacy as it allows you to insert an object between a source and a target object. At high level, converters are a chunk of custom code that hooked up through the binding and data will flow via that converter. So, whenever data is flown from source to target, one can change the value or can change the type of object that needs to be set on target property.

So, whenever data travels from source to target, it can be transformed in two ways:

Data value: Here transformation will be done with just the value by keeping the data type intact. For example, for number fields, you can transform value to floating point number to an integer, by keeping the actual value as a float.

Data type: One can also transform the data type. For example, setting a style based on some Boolean flag, this is one of the most common example. Isn't it?

Defining a converter:
Defining any converter requires implementation of IValueConverter interface in a class. This interface has two methods:

Convert: Is called when data is flowing from source to the target
ConvertBack: Is called when data is flowing from target to source. It is basically useful in two-way binding scenarios

Where to place converters:
To implement IValueConverter, one has to create a class and then put the instance of that class in a ResourceDictionary within your UI.

How to use:
Once your class is part of ResourceDictionary, then you can point to the instance of converter using the Converter property of Binding, along with StaticResource markup extension.

Using Code:
Before digging into the code, let’s discuss on what I want to achieve with below code snippet. Whenever value entered by the user is negative, I want to display 0 with * as a final amount. 

So, let’s start by creating a class called RoundingOffConverter and inherit IValueConverter as shown below:

In above class, Convert method will be called when direction of data flow is from source to target. value object is the one, which we are going to set here, targetType will tell you the type of target property, next is optional parameter and last one is for culture.

ConvertBack method will be called when you have two-way data binding and direction of data flow is from target to source object.

Code for using Data Value:
So, to achieve our need, let’s replace default convert methods to our own implementation as:









To make this example simple, I am simply creating 2 properties in code behind as:




















Now coming to XAML, first I need to add a reference of my converter class and then need to add that as a resource. It goes, here:




Once the reference is added, I have to bind this converter to my TextBox object as:

Once everything is in place, let’s run this application. You will find below output:












Code for using Data Type: Here aim is to select a style based on a Boolean property or in other words user want to change the color of a text box based on checkbox status. So let’s go ahead and write our BoolToStyleConverter.

As before, quickly create a class for this new converter as:
















Next step is to create styles in App.xaml. It can be done as:













Now in order to use above styles in application, one has to add these as a Window Resources as shown below:




Once the converter is added to resources, next step is to use this converter in our UI. So, let’s add:





We are done. Now quickly run this application and you will find color change based on text box as:

















Hope this tutorial on converter was useful.

Wednesday, January 1, 2014

Troubleshooting data binding

We all know that DataBinding is one of the most powerful concept of WPF. So, today I thought to write something on how to troubleshoot data binding related issues while working with any XAML based application. Here I'll not talk about what and how data binding works, instead I'll jump directly on the relevant part. So, let's start by picking up the troubleshooting methods which can make developer's work bit easy.

Way 1: Using Visual Studio output window
Visual Studio provides high level information about binding which is sufficient to resolve very small problems like name mismatch, etc. Let's understand this along with a code snippet:
 <Grid>
        <TextBlock Text="{Binding  ElementName=label, Path=Hello, Mode=OneWay}"/>
        <Label Content="Welcome" Name="label"/>
  </Grid> 
Now open your output window and press F5, application will launch. In output window, you will notice that the message stating: 

System.Windows.Data Error: 40 : BindingExpression path error: 'Hello' property not found on 'object' ''Label' (Name='label')'. BindingExpression:Path=Hello; DataItem='Label' (Name='label'); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String') 

After reading the above message, it is very easy to identify and resolve the issue. Isn't it ??? 

Way 2: Using TraceLevel
TraceLevel is an AttachedProperty which was introduced with .Net 3.5. This adds the trace information to the output window of Visual Studio with detail level of None, Medium or High. To use this TraceLevel property, one extra namespace needs to be incorporate in XAML as:   
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase" 
and add the TraceLevel to the binding as: 
<Grid>
    <TextBlock Text="{Binding  ElementName=label, Path=Hello, Mode=OneWay,diag:PresentationTraceSources.TraceLevel=High}"/>
        <Label Content="Welcome" Name="label"/>
</Grid> 

Way 3: Using Snoop
Snoop is the WPF utility which allows you to browse through the visual tree of a running WPF application. More details about snoop can be found here

Way 4: Debugging data binding using an converter
One can write a converter and put a breakpoint inside the convert method. Once this is done, the converter need to be setup in the binding. Let's have a look at the code: 

 namespace DebuggingDataBinding
{
    /// <summary>
    /// Converter to debug the binding values
    /// </summary>
    public class DebugConvertor : IValueConverter
    {
        #region IValueConverter Members

        /// <summary>
        /// ask the debugger to break
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Debugger.Break();
            return Binding.DoNothing;
        }

        /// <summary>
        /// ask the debugger to break
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Debugger.Break();
            return Binding.DoNothing;
        }

        #endregion
    }

    /// <summary>
    /// Markup extension to debug databinding
    /// </summary>
    public class DebugBindingExtension : MarkupExtension
    {
        /// <summary>
        /// Creates a new instance of the Convertor for debugging
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <returns>Return a convertor that can be debugged to see the values for the binding</returns>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return new DebugConvertor();
        }
    }
} 
In above snippet, provide value will create an instance of converter and return it. Now the XAML binding to sue this extension will be as:
<Window x:Class="DebuggingDataBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DebuggingDataBinding"
    Title="Window1" Height="300" Width="300">
        <Grid Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=Height, Converter={local:DebugBinding}}"/>
 </Window> 

Way 5: Using ExceptionValidationRule
One can associate ValidationRules with binding objects. So, whenever exception is thrown while updating binding source property, ExceptionValidationRule class will take care of it based on how we specify our logic to react with these exceptions. Let's have a look at the code.

I have a class named Person as:
public class Person
    {
        private string firstname;
        public string Firstame
        {
            get { return this.firstname; }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    throw new Exception("First name cannot be null or empty");
                }
                this.firstname = value;
            }
        }
    } 
Now we need to associate a rule with the binding and that can be done using in-built class calledExceptionValidationRule and XAML code will be:   
<TextBox Height="30" Width="80">
    <TextBox.Text>
        <Binding Path="FirstName" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ExceptionValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox> 
Please note, once can always come up with their own validation rules. 

Bit on performance
  • Less converters can lead to good performance - If possible, use minimal converters because converters require boxing and unboxing. One alternative is to create a property in a ViewModel, do conversion related activity in getter and bind to it.
  • Binding.DoNothing -  This seems to be useful in the scenario where return value of a converter need not to interfere in the binding process. In such cases, FallbackValue can be used.
  • Data binding cheatsheet can be found here