I have my MainView and an associated MainViewViewModel which are linked by ViewModelLocator.
Within MainViewViewModel there is a command which should trigger a new Window to open which has it's own View and ViewModel (NewView and NewViewViewModel).
In a lot of the examples I've seen it is suggested to use Mvvmlight's Messenger to do something like this:
public class MainViewViewModel
{
private void OpenNewWindow()
{
Messenger.Default.Send(new NotificationMessage("NewView"));
}
}
And then register the NewViewViewModel and handle the message like this:
public class NewViewViewModel
{
public NewViewViewModel()
{
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
}
private void NotificationMessageReceived(NotificationMessage obj)
{
if (obj.Notification == "NewView")
{
NewView view = new NewView();
view.Show();
}
}
}
However, this doesn't work because the NewViewViewModel isn't yet instantiated (so isn't registered with Messenger). Additionally, this doesn't fit with MVVM because NewViewViewModel is responsible for creating NewView.
What is the correct way to achieve a simple command which instantiates and opens a new View and ViewModel pair which are linked via ViewModelLocator and setting of DataContext="{Binding NewView, Source={StaticResource Locator}}" in NewView.xml?
Use a window service:
MVVM show new window from VM when seperated projects
You may either inject the view model to with an IWindowService implementation or use a static WindowService class:
public static class WindowService
{
public static void OpenWindow()
{
NewView view = new NewView();
view.Show();
}
}
Dependency injection is obviously preferable for being able to unit test the view model(s) and switch implementations of IWindowService at runtime.
Related
Could I get help with a little issue I am encountering regarding splitting projects into different tiers. In my ViewModel logic I have code where I create a new instance of a window when a button is clicked (I use ICommand interface for that)
The problem is however, that this requires my View folder which is in the presentation layer, I can’t reach it as my presentation layer is dependent on my ViewModel in the logic layer.
I would just move the code that deals with the creation of the pages to the view code behind but I also pass the current instance of a viewmodel as a parameter for that new window being created (for eventhandling purposes).
Any help is much appreciated! Thanks.
A view model shouldn't create instances of windows. What you could do is to inject your view model with a service that is responsible for creating windows, e.g.:
public class MainWindowViewModel
{
private readonly IWindowService _windowService;
public MainWindowViewModel(IWindowService windowService)
{
_windowService = windowService;
CreateWindowCommand = new DelegateCommand(() =>
{
_windowService.CreateWindow(new SomeViewModel());
});
}
public ICommand CreateWindowCommand { get; }
}
Define the IWindowService interface in the view model project and the concrete implementation of it in the view/presentation project:
public class WindowService : IWindowService
{
public void CreateWindow(SomeViewModel vm)
{
Window win = new Window();
win.DataContext = vm;
win.Show();
}
}
I would love to find a simple and MVVM compliant way,
to open a new view from the MainWindow.
I have already worked through some instructions and tutorials.
But somehow none really worked or it was a mixture of code behind.
I would like to open a view after pressing a button and edit an ObservableCollection in it.
I have already created DataTemplates in App.xaml and mapped the ViewModels with the respective views.
But I don't know how to cleanly open a separate window from the MainWindow (MainViewModel) via an ICommand for another ViewModel.
You should't open a window directly from the MainWindow nor the MainWindowViewModel. But the view model may use a service to open a window:
public class MainWindowViewModel
{
private readonly IWindowService _service;
public MainWindowViewModel (IWindowService service)
{
_service = service;
}
//...
public void OpenWindowExecuted()
{
_service.ShowWindow();
}
}
Service:
public interface IWindowService
{
void ShowWindow();
}
public class WindowService : IWindowService
{
public void ShowWindow();
{
Window window = new Window()
window.Show();
}
}
You could easily mock out the service in your unit tests.
I've following architecture:
desktop application, .Net 4.5, C#, WPF, MVVM Light, Messenger, IoC - ViewModel locator, so ViewModels doen't know anyhing about Views.
I have main view with data grid of some elements, and I want to display details of each individual element in new/child windows after double click on data grid.
I've bind event double click on main view to main view model. From this event handler in main view model, message is sent via Messanger.
New view (new/child window) is created in main view via delegate of also double click.
New/child window is a view which locate his view model and this view model register to the specific message in his constructor.
The problem is that new/child window (new view, and view model so on) is created too late, because message is already sent when new view model register for it.
Do you know maybe some patterns for such architecture. Any ideas will be appreciated.
It would help to know exactly what you try to do.
If your problem is just to display a detailed Window when double click on a row, I would say: create only one childWindow at start, and play with its visbility when required.
If you really need a new window each time, you could create it from your viewModel with an injected service for example.
In any case, you never has to create your window from main view! Either you create one window at start, either you dynamically create it from view model.
You cannot hope to create it from view and send the message in your view model.
Edit about the injected service, you could use something like that:
public interface IWindowService
{
void Open<TWindow>(ViewModelBase viewModel)
where TWindow : Window;
}
public class WindowService : IWindowService
{
private readonly IUIDispatcher _dispatcher;
public WindowService(IUIDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public void Open<TWindow>(ViewModelBase viewModel)
where TWindow : Window
{
_dispatcher.Run(() => OpenThreadSafe<TWindow>(viewModel));
}
private static void OpenThreadSafe<TWindow>(ViewModelBase viewModel) where TWindow : Window
{
var view = (TWindow) Activator.CreateInstance(typeof(TWindow), viewModel);
view.Show();
}
}
public class UIDispatcher : IUIDispatcher
{
public void Run(Action action)
{
var dispatcher = DispatcherHelper.UIDispatcher;
if (dispatcher == null)
{
action();
return;
}
DispatcherHelper.CheckBeginInvokeOnUI(action);
}
Note this DispatcherHelper come from MVVMlight, but you could erplace it easily.
Hope it helps.
The problem is that the ViewModel Locator creates the viewmodel instance only when it is needed (lazy loading).
just configure the ViewModelLocator to instantiate the viewmodel eager instead of lazy. This is done by passing the parameter "true" to the IoC Container.
Sample:
namespace Administration.ViewModel
{
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
//Eager Loading
SimpleIoc.Default.Register<UserManagementViewModel>(true);
//Lazy Loading
SimpleIoc.Default.Register<InformationManagementViewModel>();
}
public UserManagementViewModel UserManagementViewModel
{
get
{
return ServiceLocator.Current.GetInstance<UserManagementViewModel>();
}
}
public InformationManagementViewModel InformationManagementViewModel
{
get
{
return ServiceLocator.Current.GetInstance<InformationManagementViewModel>();
}
}
public static void Cleanup()
{
SimpleIoc.Default.Unregister<UserManagementViewModel>();
SimpleIoc.Default.Unregister<InformationManagementViewModel>();
}
}
}
I am using MVVM Light and I'm currently using SimpleIoC that comes with the package. I'm getting a bit stuck with the dependency injection. I have a bunch of services that I want to use in my view models, however most windows are a List-Edit paradigm, i.e. one screen lists all of type Person and then you can Add or Edit a Person via a new screen.
When I was doing all code in the code behind my code for adding and editing a record was as follows:
View
private void btnEdit_Click(object sender, RoutedEventArgs e)
{
_viewModel.Edit();
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
_viewModel.Add();
}
View Model
public void Add()
{
var f = new TypeDetails();
f.Show();
}
public void Edit()
{
if (SelectedItem == null)
return;
var f = new TypeDetails(SelectedItem.Id);
f.Show();
}
The constructor of TypeDetails is as follows:
public TypeDetails(int id = 0)
{
InitializeComponent();
_viewModel = new TypeDetailsViewModel(id);
DataContext = _viewModel;
}
What would the best be to implement this type functionality with MVVM Light? I have been using the ViewModelLocator class for the List screens, however I cannot see a way to do this using the SimpleIoC. My way round so far has been to keep the constructor the same, which works fine until I need to inject dependencies into the TypeDetailsViewModel such as a service. With a service the constructor of TypeDetailsViewModel would be:
public TypeDetailsViewModel(ISomeService someService, int id = 0)
{
...
}
But that means in my view constructor I have to build these dependencies one at a time and manually inject them...
public TypeDetails(int id = 0)
{
InitializeComponent();
_viewModel = new TypeDetailsViewModel(SimpleIoC.Current.GetInstance<ISomeService>(),id);
DataContext = _viewModel;
}
Is there a better way to do this?
First off I would look into the "RelayCommand" class which is part of MVVM Light. It will remove the need for events in your code behind. Start with that.
You should always favor "Constructor Injection" instead of the ServiceLocator (ex: SimpleIoC.Current.GetInstance())
Your ViewModel constructor should only be injecting services and not primitive types like "int". In your example "int id" should be the parameter of a method and not injected.
Ex: Instead, your TypeDetailsViewModel should look more like:
public TypeDetailsViewModel(ISomeService someService)
{
TypeDetail GetDetailsCommand(int id)
{
...
}
}
Lastly, your Models should never have any reference to your ViewModels.
For your DataContext, you can use a ViewModelLocator (ViewModels in ViewModelLocator MVVM Light)
To hook up your View and ViewModel to use the GetDetailsCommand, you can use the EventToCommand behavior (http://msdn.microsoft.com/en-us/magazine/dn237302.aspx). Ex: The OnLoaded event on the View calls the GetDetailsCommand on your ViewModel.
I'm currently trying out some MVP patterns sample and I have been told not create concrete Presenter objects in the View. Is there any way to have Presenter objects created dynamically ?
public partial class View: Window, IView
{
private Presenter _presenter;
public View()
{
InitializeComponent();
_presenter = new Presenter(this); //Asked to avoid this
}
}
You're thinking it wrong. You don't create the presenter in the view. You create it elsewhere (application startup, other presenters) and it passes itself to the view, either as a constructor parameter, or by setting a property.
Like this:
class FooView : IFooView
{
private readonly IFooPresenter presenter;
public FooView(IFooPresenter presenter)
{
this.presenter = presenter;
}
}
class FooPresenter1 : IFooPresenter
{
private readonly IFooView view;
public FooPresenter1()
{
view = new FooView(this);
}
}
// or
class FooPresenter2 : IFooPresenter
{
private readonly IFooView view;
public FooPresenter2(IFooView view)
{
this.view = view;
view.Presenter = this;
}
}
And by the way, you seem to be using WPF. If that's the case you may want to have a look at the Model-View-ViewModel pattern instead.
With view first creation you can use an IoC container to create your Presenter:
public View(IMyPresenter presenter)
{
InitializeComponent();
_presenter = presenter;
}
Alternatively, you can use model (presenter) first where the View is passed to the Presenter in much the same way. See Which came first, the View or the Model? for discussion on this topic.
Or you could use a third object to bind the View and Presenter together, like the IBinder service in Caliburn.