4

I have an application that opens a view that allows you to search for data. However, in order to to search, the user has to select what category he wants to search under. Currently, I'm trying to figure out how to pass that selected category from the main viewmodel(as an int) to the new search view's view model. Currently I'm trying to use something like this in the main view:

Let's say I have two views View1 and View2 an respective viewmodels for each. View2ViewlModel looks like this:

public class View2ViewlModel : ViewModelBase
{
    private IDataService _dataService;     

    public int DivisionIdnt {get; set;}

    public View2ViewModel(IDataService dataService)
    { 
        _dataService = dataService;
    }
}

And inside View1 we create and open View2 when a message is received.

public View2()
{
    InitializeComponent();
    Messenger.Default.Register<NotificationMessage<int>>(this, (m) => NotificationMesageReceived(m, m.Content));
}

private void NotificationMesageReceived(NotificationMessage<int> msg, int divisionIdnt)
{
    if (msg.Notification == "SearchCred")
    {
        var findCredentialView = new View2();

        findCredentialView.ShowDialog();
    }
}

The message get's passed in View1ViewModel as part as an action that happens when the user clicks a search button. The problem is I want to initialize the DivisionIdnt property in the View2ViewModel for the new View2 to the value of divisionIdnt form the message. How can I achieve that? I thought about instantiating a View2ViewModel in code, setting DivisionIdnt to the message parameter and then setting the DataContext of the new View2to the newly created viewmodel, like this:

private void NotificationMesageReceived(NotificationMessage<int> msg, int divisionIdnt)
{
    if (msg.Notification == "SearchCred")
    {
        var findCredentialView = new View2();

        var vm = new View2ViewModel();
        vm.DivisionIdnt = divisionIdnt;

        findCredentialView.DataContext = vm;

        findCredentialView.ShowDialog();
    }
}

However, that doesn't work because in View2ViewModel, the constructor has an IDataService injected by DI at runtime.

James Parsons
  • 6,097
  • 12
  • 68
  • 108
  • It feels to me that it might be better to set the datacontext of `view2` to be the the same as what is invoking the new view. This way you don't need to construct a new view model and you already have access the property you are trying to access. – xtreampb Jul 16 '18 at 15:43
  • @xtreampb I could do that, but `View2` has a lot more in it's vm than just that value, and doing it that way, I'd have to pollute `View1ViewModel` with *all* the fields `View2` will need – James Parsons Jul 16 '18 at 15:46

2 Answers2

1

Can you not just resolve the instance of the IDataService from your container, before instantiating the View2ViewModel?

How you do this will depend on which DI container you are using but with AutoFac it would be:

    var findCredentialView = new View2();
    var dataService = Container.Resolve<IDataService>();
    var vm = new View2ViewModel(dataService);
    vm.DivisionIdnt = divisionIdnt;

    findCredentialView.DataContext = vm;

    findCredentialView.ShowDialog();

Alternatively you could resolve the View2ViewModel from the container:

    var findCredentialView = new View2();
    var vm = Container.Resolve<View2ViewModel>();
    vm.DivisionIdnt = divisionIdnt;

    findCredentialView.DataContext = vm;

    findCredentialView.ShowDialog();

Which would take care of the ctor injection for you.

Obviously both of these approaches require that you have a reference to your container at runtime, but most applications/containers give you this. If they do not, simply expose your container at bootstrap time via a service/singleton/static as is your preference.

You may also find this answer helpful: How to handle dependency injection in a WPF/MVVM application

Sam Shiles
  • 10,529
  • 9
  • 60
  • 72
0

Did you try to initialize the viewmodel first, then pass it to the view?

public View2(View2ViewModel vm)
{
    InitializeComponent();
    Messenger.Default.Register<NotificationMessage<int>>(this, (m) => NotificationMesageReceived(m, m.Content));
}
private void NotificationMesageReceived(NotificationMessage<int> msg, int divisionIdnt)
{
    if (msg.Notification == "SearchCred")
    {
        var vm = new View2ViewModel();
        vm.DivisionIdnt = divisionIdnt;
        var findCredentialView = new View2(vm); 
        findCredentialView.ShowDialog();
    }
}
Tracy Zhou
  • 714
  • 1
  • 7
  • 11
  • The problem is that the viewmodel's constructor needs an `IDataService` parameter that get's injected inot the constructor at runtime – James Parsons Jul 13 '18 at 16:04