Mar 05 2010

Binding a Converter Parameter

There are a lot of situations when you need to bind ConverterParameter value.

Imagine that you have Receipt class with two fields: amount and currency type. And you need to format amount string to something like $1,000.00 or ¥1,000.00 depending on currency type. So the good idea is to use converter to do formatting.

The good way is to have something like AmountFormatter which takes amount and currency type and does the formatting.

Current version of the Silverlight disallowing us to bind Converter Parameter value.  But we could pass whole Reciept object to the formatter and take amount and currency type from it directly. Such formatter could look like this:

public class AmountConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var receipt = value as Receipt;
        if (receipt != null)
        {
            return String.Format("{1}{0:0,0.0}", receipt.Amount, receipt.CurrencyChar);
        }
		return value.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ...
    }
}

This method is not good cause we tide converter and particular class (in our case it is Receipt). But what could we do?

Ok, we want to have a reusable converter. But we need to pass several values to it at the same time. Then lets simply define an interface which converter is expecting to get:

 

public class AmountConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var data = value as IConverterData
        if (data != null)
        {
            return String.Format("{1}{0:0,0.0}", data.Value, data.CurrencySign);
        }
		return value.ToString();
    }

	... 

	public interface IConverterData
    {
        string Value { get; set; }
        string CurrencySign { get; set; }
    }
}

Now to prepare Receipt class to be used in conjunction with AmountConverter we just need to implement AmountConverter.IConverterData interface.

And the usage will look like this:

<TextBlock Text="{Binding Converter={StaticResource AmountConverter}}"/>

Please not what we binds to the whole object here.

Feb 26 2010

Enable Silverlight 4 Tools in Visual Studio 2010 Release Candidate

Are you still waiting for Microsoft to release Silverlight 4 for Visual Studio 2010 RC?

I'm not anymore :)

You could completely use Silverlight 4 bits for VS 2010 Beta 2 with VS 2010 RC. Alex Sorokoletov has found how to install Silverlight 4 on VS 2010 RC.

There is no magic and steps are simple as One, Two, Tree:

  1. Download Silverlight 4 Tools For VS 2010 Beta 2
  2. Run installation. The dialog window saying what Visual Studio 2010 Beta 2 should be installed will appear. Don’t hit the cancel button!
  3. Locate folder named like ‘bfb0032a835647b79718f26ba81d3392’ on root of some of your hard drives. And copy it’s content to some other location.
  4. Open ParameterInfo.xml and comment out <BlockIf DisplayText="Visual Studio 2010 Beta 2 ..."> section (it is placed on lines 13-41)
  5. Now you are ready to SPInstaller.exe to install Silverlight 4 Tools
  6. One additional step is required to force Visual Studio to use Silverlight 4 instead of Silverlight 3 for visual designers. To do it open regedit and search for DesignerPlatforms. Under this key go to Silverlight and change SilverlightHost value to ‘4.0’.

You should be happy now :)

If you need RIA services then load patched Microsoft.RiaServices.Tools.dll assembly and copy it to ‘c:\Program Files\Microsoft SDKs\RIA Services\v1.0\Libraries\Server’ folder.

And then open GAC management console by [Win+R] -> assembly ->[Enter]. Remove old one assembly and add new one.

Restart Visual Studio and you are done.

Thanks for Alex!

Nov 26 2009

Silverlight 3 access DataContextChanged event

In WPF when DataContext changes the DataContextChanged event fires. We could attach event handler to this event to execute some custom code.

In Silverligth 3 all is the same, except one small thing: DataContextCahnged is marked as internal… hmm... how then we could know what DataContext has changed?

We could do a simple trick. We can’t access DataContextChanged event, but we could bind some our dependency property to DataContext and handle dependency property changed event :)

Let’s build a small class which will do a binding for us.

public static class DataContextChangedEventManager
{
    public delegate void DataContextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args);

    public static readonly DependencyProperty DataContextChangedProperty =
        DependencyProperty.Register("DataContextChangedProperty", typeof(object), typeof(FrameworkElement), new PropertyMetadata(null, OnDataContextChanged));

    public static readonly DependencyProperty DataContextChangedEventHandlersProperty =
        DependencyProperty.Register("DataContextChangedEventHandlersProperty", typeof(EventHandler<DataContextChangedEventArgs>), typeof(FrameworkElement), new PropertyMetadata(null));

    public static void OnDataContextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var eventHandlers = obj.GetValue(DataContextChangedEventHandlersProperty) as EventHandler<DataContextChangedEventArgs>;
        if (eventHandlers != null)
        {
            eventHandlers.Invoke(obj, new DataContextChangedEventArgs(args.NewValue));
        }
    }

    ...
}

public class DataContextChangedEventArgs : EventArgs
{
    public DataContextChangedEventArgs(object value)
    {
        Value = value;
    }
    public object Value { get; private set; }
}

Here we are defining DataContextChangedProperty. This property will help us to react on DataContext changed event. When property has changed the OnDataContextChanged will be fired.

Also we have DataContextChangedEventHandlersProperty property defined which store event handlers.

Now let’s define method for attaching event handler for DataContextChanged event:

    ...

    public static void AddDataContextChangedEventHandler(this FrameworkElement control, EventHandler<DataContextChangedEventArgs> value)
    {
        if (control.GetValue(DataContextChangedEventHandlersProperty) == null)
        {
            control.SetValue(DataContextChangedEventHandlersProperty, value);
        }
        else
        {
            var eventHandler = Delegate.Combine((Delegate)control.GetValue(DataContextChangedEventHandlersProperty), value);
            control.SetValue(DataContextChangedEventHandlersProperty, eventHandler);
        }
        if (control.GetBindingExpression(DataContextChangedProperty) == null)
        {
            control.SetBinding(DataContextChangedProperty, new Binding());
        }
    }

    ...

And method for detaching event handlers:

    ...

    public static void RemoveDataContextChangedEventHandler(this FrameworkElement control, EventHandler<DataContextChangedEventArgs> value)
    {
        if (control.GetValue(DataContextChangedEventHandlersProperty) != null)
        {
            var eventHandler = Delegate.Remove((Delegate)control.GetValue(DataContextChangedEventHandlersProperty), value);
            control.SetValue(DataContextChangedEventHandlersProperty, eventHandler);
        }
        if (control.GetBindingExpression(DataContextChangedProperty) != null)
        {
            control.SetValue(DataContextChangedProperty, DependencyProperty.UnsetValue);
        }
    }

    ...

And the usage.

Lets say we have TextBlock and we want to show MessageBox when DataContext changes.

<TextBlock x:Name="TestBlock" DataContext="{Binding}"/>

We see here what there is binding defined on DataContext  propertyof the control. This binding means what we bind DataContext of the control to current control DataContext. Really this does nothing but we need it to get our DataContextChangedProperty binding to work.

And in code we need attach event handler for DataContext changed event.

public DataContextChanged()
{
    InitializeComponent();
    TestBlock.AddDataContextChangedEventHandler(OnDataContextChanged);
}

private void OnDataContextChanged(object sender, DataContextChangedEventArgs args)
{
    MessageBox.Show(args.Value.ToString());
}

And working example as usual:

[silverlight:h=180px;p=DataContextChanged;AndrewVeresov.Silverlight.Workarounds.Test.xap]

The workarounds library with examples could be downloaded from here (VS2010): Andrew Veresov Workarounds library v1.0