Aprire una nuova finestra in MVVM WPF

Ho Button e ho bind questo pulsante per il comando in ViewModel dire OpenWindowCommand. Quando clicco sul pulsante voglio aprire una nuova finestra. Ma la creazione di un’istanza di finestra e la visualizzazione di una finestra dal modello di visualizzazione sono una violazione di MVVM. Ho creato un’interfaccia simile

interface IWindowService { void showWindow(object dataContext); } 

e WindowService implementa questa interfaccia come

 class WindowService:IWindowService { public void showWindow(object dataContext) { ChildWindow window=new ChildWindow(); window.DataContext=dataContext; window.Show(); } } 

In questa class ho specificato ChildWindow. Quindi questa class è strettamente accoppiata a mostrare ChildWindow. Quando voglio mostrare un’altra finestra, devo implementare un’altra class con la stessa interfaccia e logica. Come posso rendere questa class generica in modo che possa passare solo l’istanza di qualsiasi finestra e la class sarà in grado di aprire qualsiasi finestra? Non sto usando alcun framework MVVM costruito. Ho letto molti articoli su StackOverflow ma non ho trovato alcuna soluzione per questo.

Dici “la creazione di un’istanza di finestra e la visualizzazione di una finestra dal modello di visualizzazione è una violazione di MVVM”. Questo è corretto.

Si sta ora tentando di creare un’interfaccia che accetta un tipo di visualizzazione specificato dalla VM. Questa è solo una violazione. Potresti aver sottratto la logica di creazione dietro un’interfaccia, ma stai ancora richiedendo creazioni di visualizzazione all’interno della VM.

Le macchine virtuali dovrebbero preoccuparsi solo della creazione di macchine virtuali. Se hai davvero bisogno di una nuova finestra per ospitare la nuova VM, quindi fornire un’interfaccia come hai fatto, ma uno che non ha una vista. Perché hai bisogno della vista? La maggior parte dei progetti MVVM (prima VM) utilizzano datatemplates impliciti per associare una vista a una particolare VM. La VM non sa nulla di loro.

Come questo:

 class WindowService:IWindowService { public void ShowWindow(object viewModel) { var win = new Window(); win.Content = viewModel; win.Show(); } } 

Ovviamente è necessario assicurarsi di avere VM-> Visualizza i modelli impliciti impostati in app.xaml affinché funzioni. Questo è solo MV VMM standard prima.

per esempio:

        

Una possibilità è avere questo:

 class WindowService:IWindowService { public void showWindow(object DataContext) where T: Window, new() { ChildWindow window=new T(); window.Datacontext=DataContext; window.show(); } } 

Quindi puoi semplicemente andare qualcosa come:

 windowService.showWindow(windowThreeDataContext); 

Per ulteriori informazioni sul nuovo vincolo, vedere http://msdn.microsoft.com/en-gb/library/sd2w2ew5.aspx

Nota: il new() constraint funziona solo dove la finestra avrà un costruttore senza parametri (ma immagino che in questo caso non dovrebbe esserci un problema!) In una situazione più generale, vedi Creare un’istanza di tipo generico? per possibilità.

Potresti scrivere una funzione come questa:

 class ViewManager { void ShowView(ViewModelBase viewModel) where T : ViewBase, new() { T view = new T(); view.DataContext = viewModel; view.Show(); // or something similar } } abstract class ViewModelBase { public void ShowView(string viewName, object viewModel) { MessageBus.Post( new Message { Action = "ShowView", ViewName = viewName, ViewModel = viewModel }); } } 

Assicurati che ViewBase abbia una proprietà DataContext. (È ansible ereditare UserControl)

In generale, farei un qualche tipo di bus dei messaggi e fare in modo che ViewManager ascolti i messaggi che chiedono una vista. ViewModels inviava un messaggio per chiedere di mostrare una vista e mostrare i dati. Il ViewManager userebbe quindi il codice sopra.

Per impedire a ViewModel che chiama di conoscere i tipi di vista, è ansible passare una stringa / nome logico della vista al ViewManager e fare in modo che ViewManager traduca il nome logico in un tipo.

utilizzare un contentpresenter nella finestra in cui si associa il DataConext. E quindi definire un Datatemplate per il tuo DataContext in modo che wpf possa renderizzare il tuo DataContext. qualcosa di simile al mio servizio DialogWindow

quindi tutto ciò di cui hai bisogno è la tua unica ChildWindow con ContentPresenter:

     

Trovo la soluzione accettata molto utile, ma quando lo provo praticamente, ho scoperto che non ha la capacità di rendere il controllo UserControl (la vista che risulta dalla VM -> View mapping) all’interno della finestra di hosting per occupare l’intera area fornito da esso. Quindi ho esteso la soluzione per includere questa capacità:

 public Window CreateWindowHostingViewModel(object viewModel, bool sizeToContent) { ContentControl contentUI = new ContentControl(); contentUI.Content = viewModel; DockPanel dockPanel = new DockPanel(); dockPanel.Children.Add(contentUI); Window hostWindow = new Window(); hostWindow.Content = dockPanel; if (sizeToContent) hostWindow.SizeToContent = SizeToContent.WidthAndHeight; return hostWindow; } 

Il trucco qui sta usando un DockPanel per ospitare la vista convertita dalla VM.

Quindi si utilizza il metodo precedente come segue, se si desidera che la dimensione della finestra corrisponda alla dimensione del suo contenuto:

 var win = CreateWindowHostingViewModel(true, viewModel) win.Title = "Window Title"; win.Show(); 

o come segue se hai una dimensione fissa per la finestra:

 var win = CreateWindowHostingViewModel(false, viewModel) win.Title = "Window Title"; win.Width = 500; win.Height = 300; win.Show(); 

Forse potresti passare il tipo di finestra.

Prova ad usare Activator.CreateInstance() .

Vedere la seguente domanda: Istanziare un object con un tipo determinato dal runtime .

Soluzione di chakrit:

 // determine type here var type = typeof(MyClass); // create an object of the type var obj = (MyClass)Activator.CreateInstance(type);