StepUp
StepUp

Reputation: 38209

Send parameter to ViewModel through IDialogService when open new window

I have PersonViewModel, DepartmentViewModel and their PersonView, DepartmentView.

PersonViewModel has empty constructor, however DepartmentViewModel has a parameter:

public class DepartmentViewModel
{
    public DepartmentViewModel(ObservableCollection<Person> persons)
    {}
}

I use the following service to open new window:

public interface IDialogService<T>
{
   void Show(IUnityContainer unityContainer);
   void ShowDialog(IUnityContainer unityContainer);
}

public class DialogService<T> : IDialogService<T> where T : Window
{
   public void Show(IUnityContainer unityContainer)
   {            
     var container = unityContainer;
     container.Resolve<T>().Show();
   }

   public void ShowDialog(IUnityContainer unityContainer)
   {            
      var container = unityContainer;            
      container.Resolve<T>().ShowDialog();            
   }
}

the above service works really good. So far, it works okay till I wanted to send parameters to DepartmentViewModel.

My App.xaml.cs has all stuff to instantiate viewModels inside of OnStartup() method:

protected override void OnStartup(StartupEventArgs e)
{
   _container = new UnityContainer();
   _container.RegisterType<IViewMainWindowViewModel, MainWindow>();
   _container.RegisterType<IViewMainWindowViewModel, MainViewModel>();
   _container.RegisterType<IViewPersonViewModel, PersonView>();
   _container.RegisterType<IViewPersonViewModel, PersonViewModel>(new ContainerControlledLifetimeManager());
   _container.RegisterType<IViewDepartmentViewModel, DepartmentView>();
   _container.RegisterType<IViewDepartmentViewModel, DepartmentViewModel>(new ContainerControlledLifetimeManager());
   //types
   _container.RegisterType(typeof(IDialogService<>), typeof(DialogService<>));
   _container.Resolve<MainWindow>().Show();
}

My question is how can I send a parameter to DepartmentViewModel when I open new window from PersonViewModel?

My code to open new window from PersonViewModel:

private readonly IDialogService<DepartmentView> _dialogDepartmentView;

public void ContinueCommand_DoWork(object obj)
{
   //want to send "persons" to departmentViewModel
   ObservableCollection<Person> persons = new ObservableCollection<Person>();           
    // Open new dialog    
   _dialogDepartmentView.ShowDialog(_unityContainer);
}

How can I send ObservableCollection<Person> persons to DepartmentViewModel when I open new window through IDialogService?

Upvotes: 0

Views: 652

Answers (1)

wingerse
wingerse

Reputation: 3796

Perhaps you need a new Dialog/UIService then.

Here's a UiService I use to achieve the same effect as you. I don't use any IoC container. ViewLocator is a simple class with a dictionary mapping from Vm type to View type. You could replace ViewLocator with whatever "ServiceLocator" you want ,though.

public interface IUiService {
    void Close();
    void Show<TVm>(TVm vm) where TVm : ViewModel;
    public T ShowDialog<T, TVm>(TVm vm) 
        where T : class
        where TVm : ViewModel, IDialogReturnVm<T>;
}

public class UiService : IUiService
{
    private readonly Window window;

    public UiService(Window window)
    {
        this.window = window;
    }

    public void Close()
    {
        this.window.Close();
    }

    public void Show<TVm>(TVm vm) where TVm : ViewModel
    {
        Type windowType = ViewLocator.GetViewType<TVm>()

        var wnd = (Window)Activator.CreateInstance(windowType);

        wnd.DataContext = vm;
        wnd.Owner = Application.Current.MainWindow;
        vm.Init(new UiService(wnd));
        wnd.Show();
    }

    public T ShowDialog<T, TVm>(TVm vm) 
        where T : class  //T is the type of imformation which the Vm will "return" when it's window is closed.
        where TVm : ViewModel, IDialogReturnVm<T>
    {
        Type windowType = ViewLocator.GetViewType<TVm>()

        var wnd = (Window)Activator.CreateInstance(windowType);

        wnd.DataContext = vm;
        wnd.Owner = Application.Current.MainWindow;
        vm.Init(new UiService(wnd));
        wnd.ShowDialog();
        return vm.ReturnInfo;
    }
}

All of my VMs have an IUiService property which is set when I call it's Init method.

In the AppStartup, when I want to open my first window, I do :

Type windowType = ViewLocator.GetViewType<MainVm>()

var wnd = (Window)Activator.CreateInstance(windowType);

wnd.DataContext = new MainVm();
vm.Init(new UiService(wnd));
wnd.Show();

basically same as what I do in the UiService.Show.

So when you want to Show a ViewModel from another ViewModel, you call this.UiService.Show(new YourVm(parameters..));.

IUiService.Show will then determine the window type of your VM using the ViewLocator and create a new Window. It also assigns the IUiService for the new VM using that window.

IDialogReturnVm<T> is an interface with a single property T ReturnInfo{get; private set;} which you set inside your Vm.

This keeps the View and ViewModel decoupled pretty well, while still allowing you to pass parameters to ViewModel. I even have my ViewModels in a different assembly. The IUiService is defined in that assembly and the implementation : UiService is defined in the main assembly.

Upvotes: 1

Related Questions