I've been wondering about this for a while... What's the best practice for opening a new window (view & viewmodel) from another viewmodel IF we keep in mind that the viewmod开发者_StackOverflowel which opens the new window is not aware of the existence of that view (as it should be).
Thanks.
I prefer to use an action delegate that is inserted via the ViewModel constructor. this also means we can easily verify during unit-testing:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = new MainViewModel(() => (new Window()).Show()); // would be actual window
}
}
public class MainViewModel
{
private Action popupAction;
public MainViewModel(Action popupAction)
{
this.popupAction = popupAction;
}
public ICommand PopupCommand { get; set; }
public void PopupCommandAction()
{
popupAction();
}
}
public class SomeUnitTest
{
public void TestVM()
{
var vm = new MainViewModel(() => { });
}
}
I don't use the ViewModel to open another View/ViewModel. This is in the responsibility of a Controller. The ViewModel can inform the Controller (e.g. via Event) that the user expects to see the next View. The Controller creates the View/ViewModel with the help of an IoC Container.
How this works is shown in the ViewModel (EmailClient) sample application of the WPF Application Framework (WAF).
Use the mediator pattern such as mvvmlight's messenger class:
http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338
The basic idea is the viewmodel sends a message to its view. The receiving view then looks like this:
OnMsgRecived() {
Viewmodel vm = New Viewmodel() - Or use dependency injection to resolve
View v = new View()
v.DataContext = vm
v.Show()
}
This allows the viewmodel that sent the message to show another window with no 'knowledge' of how or who did the opening.
I personally prefer to either raise events in my ViewModel to signal to the view that it needs to do something like open a window. I try not to do this directly though so that I don't see things like an event named OpenWindow in my ViewModel because that seems to violate the separate between the View and the ViewModel in my opinion. Instead what I might do is have a Property that changes state and raises a PropertyChanged event accordingly in which the view may listen to and then decide to open a window in reaction to this signal. In some cases the opening of a window is not related to something in the ViewModel at all and is only a function of the View. In these cases I'm not afraid at all to place code for opening another view within the code-behind portion of the View.
The mediator pattern just makes this more loosely coupled and allows for possibilities where a main application window View or a highly nested View could listen to messages globally within the application without having direct access to ViewModels in order to attach event handlers and such. To filter out messages that are irrelevant you can look at some sort of message source value or some other indication of where the message originates from. For those comfortable with the idea of Windows Messages and how that works between different controls (Windows) within unmanaged and WinForms development might be a way of understanding a system could be built up on top of a mediator that broadcasts messages.
I agree with a mediator-like approach, but the OnMsgReceived is clearly handled in the code-behind of the view, is there a good way to avoid this?
精彩评论