Pages

Showing posts with label Converters. Show all posts
Showing posts with label Converters. Show all posts

Wednesday, 15 July 2009

Databinding and Nullable Types

In this article (Using WPF Binding StringFormat property with nullable types) Karl Shifflett describes how to bind to properties that have a nullable type.

In summary, use TargetNullValue property of the binding like this:
<TextBox
Text="{Binding Path=NumberOfComputers,
TargetNullValue={x:Static sys:String.Empty},
StringFormat=\{0:D\}}" />

TargetNullValue requires .NET 3.5 Framework Service Pack 1. Previously this would have required a IValueConverter class to deal with the Null values.

Monday, 6 April 2009

DataTemplateSelector

UPDATE: There is an alternate technique described here and example code available from GoogleCode.

On our till roll I want to display each different category of product using a specific DataTemplate.

We start by defining the DataTemplates to be used in the usual way:

<DataTemplate x:Key="listBookingTemplate">...
<DataTemplate x:Key="listCCardTemplate">...
<DataTemplate x:Key="listCashTemplate">...
<DataTemplate x:Key="listDefaultTemplate">...

Then the DataTemplateSelector which is the link between the xaml DataTemplates and the C# class (TillRollTemplateSelector) that determines which template to use:

<ss:TillRollTemplateSelector 
 BookingTemplate="{StaticResource listBookingTemplate}" 
 CCardTemplate="{StaticResource listCCardTemplate}"
 CashTemplate="{StaticResource listCashTemplate}"  
 DefaultTemplate="{StaticResource listDefaultTemplate}" 
 x:Key="tillTemplateSelector" />

Then we need to wire up the template selector resource (tillTemplateSelector) to the ListBox that will display the data:

<ListBox Name="theTillRoll" Height="250" Width="330"
 ItemsSource="{Binding Source={StaticResource ViewModel}, Path=SaleItems}" 
 ItemTemplateSelector="{StaticResource tillTemplateSelector}"
 />

Finally, we define the C# class:

public class TillRollTemplateSelector : DataTemplateSelector
{
public DataTemplate BookingTemplate { get; set; }
public DataTemplate CashTemplate { get; set; }
public DataTemplate CCardTemplate { get; set; }
public DataTemplate DefaultTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is BookingLineItem) return BookingTemplate;
if (item is CashPaymentLineItem) return CashTemplate;
if (item is CCardPaymentLineItem) return CCardTemplate;
return DefaultTemplate;
}
}

The object item passed to the SelectTemplate method is of the same type as bound to the ListBox via the ItemsSource.

MultiBinding

I wanted to control the IsEnabled property of a Button based on the properties of two other controls.
Specifically I wanted to only enable the LogIn button if both the Site combobox and UserName textbox had a value available.

I created a MultiBinding for the Button like this:
<Button.IsEnabled>
   <MultiBinding Converter="{StaticResource loginConverter}">
      <Binding ElementName="xSite" Path="SelectedIndex"/>
      <Binding ElementName="xUsername" Path="Text"/>
   </MultiBinding>
</Button.IsEnabled>

And a IMultiValueConverter like this:
public class LoginConverter : IMultiValueConverter
{
   public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
   {
      // SelectedIndex of the ComboBox
      int selectedIndex = (int)values[0];
 
      // Length of Text in TextBox
      int selectedLength = ((string)values[1]).Length;
 
      // Only enable Button if both Combo and TextBox have data
      if (selectedIndex == -1 || selectedLength==0)
      {
         return false;
      }
      else
      {
         return true;
      }
   }
   public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
   {
      throw new NotImplementedException();
   }
}

The array of object[] values are presented in the same order as the Bindings within the MultiBinding.

Finally here is an example of using MultiBinding with StringFormat.

Friday, 13 March 2009

Forcing OnPropertyChanged to update the UI

Having used my new Func<T, T> friend (see Action<T> and Func<T, TResult>) the bound TextBox refused to show the revised value. Even though OnPropertyChanged("...") had been called the property's "getter" was not called by WPF.

I don't really understand why.

To get round this problem I added a FormattingConverter to the TextBox called DoNothingConverter. Its presence is sufficient to ensure the UI updates.

public class DoNothingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}

Action<T> and Func<T, TResult>

I wanted to pass a validation routine to my SetValue method that uses generics. So I used the Action delegate system.
protected void SetValue(T newValue, Action validate)
{
...
validate.Invoke(newValue);
...
}
That worked fine until I wanted to change the value (for formatting purposes). So I moved to using the Func delegate system instead. Since I want the result to be of the same type as the passed argument I used <T, T> instead of <T, TResult>.

protected T SetValue(T newValue, Func validate)
{
...
newValue = validate.Invoke(newValue);
...
}

Sunday, 5 October 2008

Exceptions in Converters

Karl Shifflett says:

It is vital that your value converters not throw or be allowed to bubble exceptions because the data binding pipeline does not catch, swallow or handle these exceptions. Instead your users will get those messy exceptions messages because there is no method to catch and handle exceptions in XAML markup. Ensure that if your converter fails that you gracefully handle this conversion or convert back operation.

Validation and Conversion

The default order is for the ValidationRule to be called before the ConvertBack method of the IValueConverter. This means that the ValidationRule may have to do the same conversion as the converter before it can do the validation.

This order can be changed by specifying the ValidationStep.

<TextBox
MinWidth="100"
Style="{StaticResource tbStyle}"
>
<Binding Path="Dob" Converter="{StaticResource dateFormattingConverter}">
<Binding.ValidationRules>
<app:DobValidationRule ValidationStep="ConvertedProposedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox>