Quantcast
Channel: MVVM – Dean Chalk
Viewing all articles
Browse latest Browse all 26

MVVM – DataContext Outside The Visual Tree With Freezables

$
0
0

Often in WPF development I am looking for ingenious ways to maximise my data usage opportunities within XAML. I try hard not to add additional code or inflate my ViewModel if I can use XAML to achieve the desired effect.

As an example of what I mean, I’m sure you all have used elaborate binding expressions where you are trawling up the logical tree with FindAncestor in order to locate a more comprehensive DataContext for binding.

Often it would be much easier to use StaticResource bindings to your ViewModel. The problem you have with this is that a resource (from which you can bind to with StaticResource) is not part of the Visual or Logical tree, so does not have access to the DataContext.

However, there is a neat little trick that can be a life saver in these situations.

Lets create an example of a particular situation where we may need the DataContext available with a StaticResource. Lets say we have a ValueConverter that not only needs the binding value, bit also a reference to the overall DataContext in order to calculate a value.
Let also suppose in this scenario that a MultiBinding where we FindAncestor in order to access the DataContext isn’t going to work, perhaps because the performance hit is too much inside a deep logical tree.

What we really need is to be able to reference the DataContext directly from our ValueConverter. However, as value converters are static resources, this is difficult to achieve …. UNLESS … we derive our IValueConverter class from Freezable.

Freezable are typically used to improve performance in WPF visual rendering, but they also have another lesser known property – even though they are a resource, they also have access to the DataContext.

So here is the code for my example ValueConverter that can pickup the DataContext whilst declared in a resource dictionary

public class ComplexValueConverter : Freezable, IValueConverter
{
    public static readonly DependencyProperty MainViewModelProperty =
        DependencyProperty.Register("MainViewModel", typeof(MainViewModel), typeof(ComplexValueConverter),
            new PropertyMetadata(null));

    public MainViewModel MainViewModel
    {
        get { return (MainViewModel)GetValue(MainViewModelProperty); }
        set { SetValue(MainViewModelProperty, value); }
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is double) || MainViewModel == null)
            return value;
        var val = (double)value;
        return val * MainViewModel.Multiplier;
    }

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

    protected override Freezable CreateInstanceCore()
    {
        return new ComplexValueConverter();
    }
}

As you can see, my ValueConverter derives from Freezable. As Freezable derives from DependencyObject we can create a DependencyProperty in order to bind to our ViewModel. Then in the ValueConverter we use a property on our ViewModel called ‘Multiplier’ to calculate our value.

Here is our ViewModel

public class MainViewModel
{
    public MainViewModel()
    {
        Value1 = 23.333;
        Multiplier = 3;
    }
    public double Value1 { get; private set; }
    public int Multiplier { get; private set; }
}

And I hook it all together in my XAML here

<Window x:Class="FreezableIValueConverter.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:f="clr-namespace:FreezableIValueConverter"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <f:ComplexValueConverter x:Key="ComplexValueConverter" MainViewModel="{Binding}" />
    </Window.Resources>
    <Grid>
        <TextBlock Text="{Binding Path=Value1, Converter={StaticResource ComplexValueConverter}}" />
    </Grid>
</Window>

And my code-behind hooks it up in the usual manner

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}

So there we have it – our main ViewModel bound to a Static Resource by inheriting from Freezable.

We could also use this technique to create a binding proxy class that sits in the resource dictionary and allows any piece of XAML (including templates) to bind to the main data via a StaticResource binding.

What do you think of this idea ? Is there any way it could be improved ? Let me know, and connect with me on LinkedIn if you like – the more connections the better

Dean


Viewing all articles
Browse latest Browse all 26

Latest Images

Trending Articles





Latest Images