Patrick
Patrick

Reputation: 884

Setting UserControl ViewModel Property

All -

I am using Unity in my WPF application for DI (without prism). I have my MainWindow.xaml and MainWindowViewModel.cs. I have a usercontrol in my Mainwindow.xaml. The user control has its own uc1.xaml and uc1viewmodel.cs. The UC1 ViewModel is currently exposed as a property on MainWindowViewModel so I can set the datacontext on the usercontrol (as recommended by many ppl here).

The question I have is how/where can I set this property - will it be in app.xaml.cs or will it be in the constructor of mainwindowviewmodel. Code Snippets:

App.xaml.cs

protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        //Step 1 - One Time - Creating an instance of the container
        UnityContainer unity = new UnityContainer();

        //Step 2 - Registering your MainWindowViewModel
        unity.RegisterType<IViewModel, UserControl1ViewModel>();

        //Step 3 - Creating an Instance
        UserControl1ViewModel uc1_mwvm = unity.Resolve<UserControl1ViewModel>();  <-- doesnt help
        MainWindowViewModel mwvm = unity.Resolve<MainWindowViewModel>();

        MainWindow mw = unity.Resolve<MainWindow>();

        mw.Show();
    }

MainWindowViewModel.cs

public class MainWindowViewModel
{
    public IViewModel IVM { get; set; }

    public MainWindowViewModel()
    {
        //IVM = new UserControl1ViewModel(); <-- All I really want is an equivalent but letting Unity do the work. 
    }
}

MainWindow.xaml

<Window x:Class="_05_ViewFist_UC_Unity_Working.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:uc1="clr-namespace:_05_ViewFist_UC_Unity_Working"
     xmlns:uc2="clr-namespace:_05_ViewFist_UC_Unity_Working"
    Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Vertical">
    <TextBlock Text="{Binding NNN}" />
    <uc1:UC1 DataContext="{Binding UC1VM}" />
    <uc2:UC2 DataContext="{Binding UC2VM}" />
</StackPanel>
</Window>

UC1

<UserControl x:Class="_05_ViewFist_UC_Unity_Working.UC1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300" >
<StackPanel Orientation="Horizontal" Background="Red">
    <TextBlock Text="UC1 "  />
    <TextBlock Text="{Binding FirstName}"  />
</StackPanel>
</UserControl>

As you see from the code - Instance of UC1 is created in xaml (MainWindow.xaml) and hence when MainWindow instance is created in app.xaml.cs - it still doesnt create an instance of UserControl1ViewModel.

Question again is : Dont think its a good practice for me to call the Unity Resolve statement in the constructor of MainwindowViewModel. Is that correct??

Can somebody share a code snippet of how/where I can do this?

Thanks

Upvotes: 1

Views: 1764

Answers (2)

dev hedgehog
dev hedgehog

Reputation: 8791

I downloaded your solution from github and tried to solve your problem.

You did a great job just you forgot few details such as property attributes.

This is how your App.cs file shall look alike:

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        //Step 1 - One Time - Creating an instance of the container
        UnityContainer unity = new UnityContainer();

        //Step 2 - Registeration
        unity.RegisterType<IMainWindowViewModel, MainWindowViewModel>();
        unity.RegisterType<IUC1ViewModel, UC1ViewModel>();
        unity.RegisterType<IUC2ViewModel, UC2ViewModel>();


        //// Instance of MainWindowViewModel will be created once you call Resolve MainWindow.


        MainWindow mw = unity.Resolve<MainWindow>();

        mw.Show();
    }

Here is what I changed:

public class MainWindowViewModel : IMainWindowViewModel
{
    #region Public Properties

    [Dependency]
    public IUC1ViewModel UC1VM { get; set; }

    [Dependency]
    public IUC2ViewModel UC2VM { get; set; }

    public string NNN { get; set; }

    #endregion

    public MainWindowViewModel()
    {
        NNN = "This value coming from MainWindowViewModel";
    }
}

[Dependency] is a property attibute that tells Unity where to inject values.

I could merge my code to your repo in github if you wish so.

Upvotes: 1

Daniel
Daniel

Reputation: 9521

You can use the service locator pattern. I use it with Unity as a DI.

internal class ServiceLocator
{
    [...]
    public MainViewModel Main { get { return container.Resolve<MainViewModel>(); } }
}

You can intantiate your class the way you want (DI or not, the class initializes the DI or receive it as a parameter, you can store the DI in a private static property, you can initialize your class if DI is null or when the application starts etc...).

In your App.xaml

<Application.Resources>
        <vm:ServiceLocator x:Key="Locator"/>
    </Application.Resources>

And now, you can set your datacontext

DataContext="{Binding Main, Source={StaticResource Locator}}"

Edit:

I found another way of doing it (among other): Take a look at this article. In the command, you can resolve your viewmodel as you like.

Upvotes: 1

Related Questions