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

WPF – Automatic ViewModel Resolution with Autofac

$
0
0

In MVVM there are lots of elaborate ways to make sure our XAML’s DataContext is correctly instantiated, and if you are using IoC then you should be letting the IoC container instantiate your viewmodel, which doesn’t fit naturally into the XAML view paradigm.

An easy way to do this is to create a XAML resource that will resolve your ViewModel for you, via your IoC container.

Many MVVM frameworks offer this feature, but I wanted one to specifically work with Autofac, so I have written on for this purpose.

Here’s my data provider class

public class AutofacDataProvider : DependencyObject
{
    public static readonly DependencyProperty AutofacContainerProperty =
        DependencyProperty.Register("AutofacContainer", 
            typeof(IContainer), 
            typeof(AutofacDataProvider), 
            new PropertyMetadata(null, AutofacValuesChanged));

    public IContainer AutofacContainer
    {
        get { return (IContainer)GetValue(AutofacContainerProperty); }
        set { SetValue(AutofacContainerProperty, value); }
    }

    public static readonly DependencyProperty DataTypeNameProperty =
        DependencyProperty.Register("DataTypeName", 
        typeof(string), 
        typeof(AutofacDataProvider), 
        new PropertyMetadata(null, AutofacValuesChanged));

    public string DataTypeName
    {
        get { return (string)GetValue(DataTypeNameProperty); }
        set { SetValue(DataTypeNameProperty, value); }
    }

    private static readonly DependencyPropertyKey DataPropertyKey =
        DependencyProperty.RegisterReadOnly("Data", 
        typeof(object), 
        typeof(AutofacDataProvider), 
        new PropertyMetadata(null));

    public static readonly DependencyProperty DataProperty = 
        DataPropertyKey.DependencyProperty;

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        private set { SetValue(DataProperty, value); }
    }

    private static void AutofacValuesChanged(DependencyObject d, 
        DependencyPropertyChangedEventArgs e)
    {
        var container = d.GetValue(AutofacContainerProperty) as IContainer;
        var typeName = d.GetValue(DataTypeNameProperty) as string;
        if (container == null || string.IsNullOrEmpty(typeName))
            return;
        var assembly = Assembly.GetExecutingAssembly();
        var type = assembly.GetType(typeName, false, true);
        if (type == null)
            return;
        var data = container.Resolve(type);
        d.SetValue(DataPropertyKey, data);
    }
}

here’s an example of a viewmodel I want to instantiate

public interface IMainViewModel
{
    string Name { get; set; }
}

public class MainViewModel : IMainViewModel, 
    INotifyPropertyChanged
{
    private string name = "test name";
    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            OnPropertyChanged("Name");
        }
    }

    public event PropertyChangedEventHandler 
        PropertyChanged;

    protected virtual void OnPropertyChanged
        (string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, 
            new PropertyChangedEventArgs(propertyName));
    }
}

Here is where I wire up my container (you probably wouldn’t do it like this but this will do for our example)

public partial class App : Application
{
    public static IContainer Container { get; private set; }
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var builder = new ContainerBuilder();
        builder.RegisterType<MainViewModel>().As<IMainViewModel>();
        Container = builder.Build();
    }
}

And here it is in my XAML

<Window x:Class="WpfApplication7.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:app="clr-namespace:WpfApplication7"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <app:AutofacDataProvider 
            DataTypeName="WpfApplication7.IMainViewModel" 
            AutofacContainer="{Binding Source={x:Static app:App.Container}}" 
            x:Key="DataSource" />
    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource 
        DataSource}, Path=Data}">
         <TextBlock Text="{Binding Name}" />
    </Grid>
</Window>

Filed under: C#, MVVM, WPF

Viewing all articles
Browse latest Browse all 26

Trending Articles