简体   繁体   中英

Opening a new window on button click WPF MVVM

I am learning WPF MVVM and want to open a new window on a button click from a main window.

I know each View has to have an equivalent ViewModel and one of the basic principles of MVVM is that the ViewModel must not know anything about the View.

So please can anybody provide me a simple clean example that does not violate any MVVM principles on how to create two Views and two ViewModels that have the following functionality:

Show a new view by clicking a button from a main View.

You can create a separate service for launching views as dialog so that it can be used in a generic way across the application. And will inject this service to the ViewModel via Constructor which wants to launch any dialog.

public interface IDialogWindowService<T>
{
    void Show();
    void ShowDialog();
}

public class DialogWindowService<T> : IDialogWindowService<T> where T : Window
{
    public void Show()
    {
        container.Resolve<T>().Show();
    }

    public void ShowDialog()
    {
        container.Resolve<T>().ShowDialog();
    }
}

Now just inject this service to the respective ViewModel.

public class YourViewModel
{
    //commands
    public ICommand someCommand { get; set; }

    private IDialogWindowService<BookingView> _dialogService;
    public YourViewModel(IDialogWindowService<YourView > dialogService)
    {
        _dialogService = dialogService
        someCommand = new RelayCommand(someCommandDoJob, () => true);
    }

    public void someCommandDoJob(object obj)
    {
        //Since you want to launch this view as dialog you can set its datacontext in its own constructor.    
        _dialogService.ShowDialog();
    }
}

OR

you can use DataTemplates to change view. It allows to dynamically switch Views depending on the ViewModel :

<Window>
   <Window.Resources>
      <DataTemplate DataType="{x:Type ViewModelA}">
         <localControls:ViewAUserControl/>
      </DataTemplate>
      <DataTemplate DataType="{x:Type ViewModelB}">
         <localControls:ViewBUserControl/>
      </DataTemplate>
   <Window.Resources>
  <ContentPresenter Content="{Binding CurrentView}"/>
</Window>

If Window.DataContext is an instance of ViewModelA, then ViewA will be displayed and

Window.DataContext is an instance of ViewModelB, then ViewB will be displayed.

The best example I've ever seen and read it is made by Rachel Lim. See the example.

Depending on your usage, there isn't anything wrong with opening a view from the view's code-behind. It's still view code after all.

MyView view = new MyView();
view.Show();

Otherwise, if you need to open a window from the ViewModel or using a ICommand , then you can look at the "Open Windows and Dialogs in MVVM" library I wrote on GitHub . This will demonstrate how to open a Window by clicking on a button using the MVVM design pattern.

What I've done with some success in the past is to create what is basically a View Factory, that constructs a view and assigns it a viewmodel. This gives me a one-stop shop to do the stiching for the views, much like you would to using IoC.

There may be advantages and disadvantages to this, so I'd be curious to learn if there are other/better ways, but so far, this is the practice I've found least painful.

Window newWindow = new Window();
Window.Show(); 

or if you want to disable the previous window

Window newWindow = new Window();
Window.ShowDialog();

To be a more pure implementation of MVVM you can use a factory or have an interface-driven controller written for each view that handles a) what to show and b) how to bind it to the data. So for the controller : FormAlpha has FormAlphaViewModel and FormAlpha.VPS. The interface is perfect for this since every implementation is different.

So create a standard that each view had an interface-driven class that ends with vps - or visual presentation service. And you want to trigger FormAlpha on that button click. You would use reflection or factory to load FormAlpha.vps as an IVisualPresentor and call IVisualPrentor.Display(parms);

Each VPS has the job of loading a specific form, binding the databinding with the view model associated with that form (which not many solutions provided), and the first parameter always has a required parameter of Show() or ShowDialog().

And...of course...the first thing your viewmodel gets are the parameters passed. So VPS will break out those parameters and confirm that the correct parameters were passed. In this way (from your example) you have a function that the button represents and wants to deploy. The VPS handles the data and visuals needed to accomplish that. The caller just knows that the VPS will hand whatever that function is. Now I just rattled the code below from memory so it isn't perfect. But presents the thoughts I'm talking about above.

public interface IVisualPresentor
{ void Display(params object[] parms) };

public class FormAlpha.VPS : IVisualPresentor
{  
     public void Display(params object[] parms)
     {
       //validate parms if needed and cast to type specific data
       //for example it needs session data parms[1] and customer parms[2]
       var form = new FormAlpha();
       var model = new FormAlphaViewModel( sessionData, customer );
       form.DataBinding = model;
       if ((bool)parms[0])
          form.Show(); 
       else 
          form.ShowDialog();
    }
 }

Let's say that Form1 is your main form,and Form2 is the one you want to open: You can use this code(insert it into Form1):

Form2 frm = new Form2();
frm.Show();

Also if you want to close the main form you can use this.Close(); (it will close current form). Furthermore if you want just to open Form2 but still Form1 to be the main one you can use:

Form2 frm = new Form2();
frm.Show();
this.Focus();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM