Main Window starts to depend on too many things, but I don't want a service locator - wpf

I am creating a WPF application. Naturally my entry point is MainWindow.xaml, which is opened up by App.xaml
var mainWindow = container.Resolve<MainWindow>();
Application.Current.MainWindow = mainWindow;
Application.Current.MainWindow.Show();
I am using Dependency Injection and so far all the dependencies are passed as parameters in the ctor of the MainWindow's View Model.
i.e. my Main Window is
public partial class MainWindow : MetroWindow
{
private readonly MainWindowModel mainViewModel;
public MainWindow(MainWindowModel mainViewModel)
{
and its View model is:
public MainWindowModel(IDataRepository dataRepo, ICommand command1, ICommand command2, etc ...)
{
However, I am now starting to realize this might be a problem. Given that the MainWindow is the entry point to the entire app, it seems like any dependency, anywhere in the application will have to first pass through the MainWindow View Model constructor. This seems crazy.
I am coming from the background of ASP.NET MVC and there we have Controllers, which receive only the dependencies that they need. i.e. the concept of a main entry point there is missing and this makes things easier and more manageable.
Here is an example in my WPF app. A control, on the Main View needs to open up a dialog. This dialog is another Window and of course that window receives its ViewModel in its ctor. To me, it seems like to be able to resolve the dialog properly, I need to pass it through the Main Window View Model ctor first, keep it as private readonly field of the Main Window View Model and launch it when necessary. Ok, but what if I have 100 dialogs. That's just one of the examples. I have such issue with the ICommand implementations too.
To sum up my question:
How do I manage the dependencies in WPF properly, without using the Service Locator anti-pattern and without passing every single abstraction through the ctor of the main window view model? I could very easily pass a Container around and let, e.g., the create ABC command solve the ABCDialog before opening it, but I feel this will cause more issues than it would solve.
I am probably doing something wrong. Please advise me what is the best practice.

Related

WPF/MVVM Navigation with child ViewModels having dependencies

I'm trying to use both MVVM and Dependency Injection pattern in my WPF MDI application.
I'm using VM first approach.
Basically, my app starts with the App.xaml.cs class which is supposed to be, if I understood the thing well, my composition root (where all dependencies are resolved). Here's a sample :
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
...
var login = new LoginView();
login.DataContext = new LoginViewModel(Dependency1, Dependency2);
loginView.ShowDialog();
if (loginView.DialogResult.GetValueOrDefault())
{
var app = new MainWindow();
var mainVM = new MainViewModel(Dependency3, Dependency4);
app.DataContext = mainVM;
app.Show();
}
}
}
No problem so far, I can resolve dependencies for both LoginViewModel and MainViewModel whether I use a DI container or Dependency Injection by hand. Now let's dig into MainViewModel.
I was inspired by Rachel Lim's approach and used a SelectedViewModel property to get/set the currently used ViewModel which is bound to its View using DataTemplates. I'll let you look at the link for more details on the process since it is quite unrelated to my issue here.
The important thing is that my MainViewModel is in charge of switching ViewModels when needed. But my children ViewModels have dependencies. Here's a simplified sample :
class MainViewModel
{
private ViewModel1 vm1;
private ViewModel2 vm2;
public MainViewModel(Dependency1, Dependency2)
{
...
}
...
// Method used by an ICommand to display the ViewModel1's associated View
private void DisplayView1()
{
vm1 = new ViewModel1(Dependency3, Dependency4, Dependency5);
// Method used by an ICommand to display the ViewModel2's associated View
private void DisplayView2()
{
vm2 = new ViewModel2(Dependency3, Dependency6);
SelectedViewModel = vm2;
}
...
}
As you can see, some dependencies are shared between several children ViewModels and some are not.
My problem is, I have trouble injecting those from the composition root. So far, I have found only two solutions :
Having two composition root (kinda) : resolving LoginViewModel and MainViewModel in App.xaml.cs and children ViewModels in MainViewModel. This implicates, when using an IOC container, referencing the container in both classes.
Passing children ViewModels as MainViewModel's constructor parameter and treat them like any other dependencies. My problem with this approach is that, if I have, let's say, ten ViewModels, the MainViewModel's constructor will become huge.
I read that one could pass a factory to the MainViewModel and delegate the responsibility to create the children ViewModels to it, but I didn't see any sample using children ViewModels with constructor parameters.
I don't understand how I could use this method without passing all children's dependencies to the MainViewModel's constructor and hence, without making it huge again.
Maybe there's something I don't see, but it seems like a deadend to me.
Please help me getting this right and show me the right direction.
Thanks.
I just realized I got this whole factory thing wrong up until now.
Actually, the DI container that I use, Ninject, has an extension which address the exact same issue I am having. This extension needs an interface containing the methods to create the required dependencies (the ViewModels in my case) and create the concrete factory behind the scenes. I was just misusing it, thinking I needed to pass all dependencies to the interface somehow while it can resolve the ViewModels itself since these are already registered in the container.
So now, all I have is a single composition root (my App.xaml.cs) where the container resolve the LoginViewModel and the MainViewModel. The latter has the factory interface as a dependency which is used to resolve all children ViewModels and is also resolved by the container. No extra reference to the container needed !
Thank you so much Coops for your help ! You definitly got me on the right track here !

Avoid Prism AutoWireViewModel Creating the ViewModel Twice

Prism can create an unneeded ViewModel if you have a one-arg constructor on your view. I am trying to understand how this can be avoided, or, if I can design something to work differently. Here is what happens.
The XAML view declares ViewModelLocator.AutoWireViewModel:
mvvm:ViewModelLocator.AutoWireViewModel="True"
And the class declares two constructors:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
public MainWindow(MainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
There is a reason that I declare the one-arg constructor: it is because the ViewModel is serializable; and when deserialized, the view is constructed by explicitly invoking that constructor with the restored ViewModel. But the issue can happen in two ways.
First, when you invoke the one-arg constructor:
MainWindowViewModel viewModel = new MainWindowViewModel();
MainWindow window = new MainWindow(viewModel);
Then Prism constructs the view, and that invokes the ViewModelLocator from the XAML, which creates and sets a ViewModel ... And then your explicit argument is set; replacing the auto-created instance (or if you reverse the lines in the constructor, than your explicit argument is actually wiped off).
AND, perhaps unexpectedly, or perhaps by some folly in my understanding, or some other unknown design aspect, it ALSO will happen if you resolve the view from the Container --- you might do this expecting to invoke the default constructor for the view; but that in fact does not happen; and, once again you will create two ViewModels:
MainWindow window = Container.Resolve<MainWindow>();
This line of code actually begins by discovering the one-arg constructor on the View, and then RESOLVING a ViewModel and invoking that constructor ... which again triggers the XAML auto-ViewModel; and then your one-arg constructor continues to wipe off the auto-ViewModel ...
It's consuming resources; and in fact, I tripped an exception where the view was binding based on some other state that was inconsistent with the ViewModel that I was expecting to explicitly be set.
I can't see a way to defeat the auto-created instance, and so I am not seeing how to invoke a one-arg constructor around the AutoWireViewModel behavior; or, how to resolve the view from the Container and avoid two ViewModels being created.
Perhaps resolving the View from the Container is abuse if it hasn't been registered, but the one-arg constructor seems to be reasonable, and it creates two instances ...
Is there some way? [Perhaps you can customize that behavior to check for an existing DataContext and then not set it if present ... or something along those lines?]
I created a simple example on GitHub:
https://github.com/steevcoco/PrismAutoCreatesViewModelTwice
To be clear here, Prism isn't creating the ViewModel twice. You are. You do it once in the code behind with the ctor, and you do it again with the ViewModelLocator. Pick one approach and use it. There is no need to have two different ways of setting the VM on the same View.
You should read up on containers and how they work. That will help your understanding of what is going on here. Every thing is working exactly as it should.
Well, for what it's worth, I have actually implemented a custom ViewModelLocator that works around this behavior. It is somewhat crude: it simply first checks if the current DataContext is non-null; and if so, the AutoWire will not create or set a Viewmodel.
I have updated the code in the repository for anyone interested.

WPF best practice to get current MainWindow instance?

I got a warning that this may be a subjective question and might be closed, but I'm going to ask anyway.
I'm basically trying to access a button on my MainWindow in a WPF application from a UserControl that gets loaded up from within the MainWindow.
I'm currently accessing it like this from the UserControl's code behind:
((MainWindow)Application.Current.MainWindow).btnNext
But it does look messy, and from what I've read is not considered a best practice. Anyone able to provide an answer that constitutes a best practice for Accessing controls / properties from the current instance of a MainWindow - or any other active windows / views for that matter?
You can get a reference to the parent window of the UserControl using the Window.GetWindow method. Call this once the UserControl has been loaded:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
MainWindow parentWindow = Window.GetWindow(this) as MainWindow;
if (parentWindow != null)
{
//...
}
};
}
}
You could also access all open windows using the Application.Current.Windows property:
MainWindow mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
Which one to use depends on your requirements. If you want a reference to the application's main window for some reason, you could stick with your current approach. If you want a reference to the parent window of the UserControl, using the Window.GetWindow method would be better.
The best practice is generally to use the MVVM design pattern and bind UI controls to source properties of a view model that may be shared by several views. But that's another story. You could refer to the following link for more information about the MVVM pattern: https://msdn.microsoft.com/en-us/library/hh848246.aspx

What should compose View with ViewModel and show result?

I have problem of thinking ideal solution for creating and showing window in WPF MVVM application. Some part of application needs to show some window with some data. I create VM, set its properties, create View, assign its VM (in constructor), then display window. This is done using class that I named ViewController and this class have methods with parameters for every window in my application. I think there can be better solution than this, but not overengineered.
The normal solution is you have a class that wraps and instantiates a View ViewModel pair. This is often called screen. it would look something like this.
public class Screen<TView> where TView : Window
{
public Screen(TView view, object viewModel){
//store view and viewModel props
//display view
//set viewModel as DataContext of view
}
}
This is a very rough example, there are lots of ways you can do it.
In the last I created implementation of IWindowManager, which have methods for showing required windows and these methods have parameters if needed. Methods create view model, set its properties and inject it to window. Only drawback of this solution is when new window is needed, new method must be added to interface and implementation of WindowManager.

Command binding in multiwindow WPF app

My application can have multiple designer windows. Each window constitutes of several user controls which communicates dynamically with the help of RelayCommands. I created the following class as the backbone of the commanding infrastructure.
public static class Commands
{
public static readonly RoutedCommand EntityEditRequest = new RoutedCommand();
public static RelayCommand EntityEditorChangeRequest;
public static RelayCommand XMLUpdateRequest;
public static RelayCommand SaveRequest;
}
Each viewmodel for the user controls will do something like this in the constructor
public XMLEditorViewModel()
{
Commands.Commands.SaveRequest = new RelayCommand(Save_Executed);
Commands.Commands.XMLUpdateRequest = new RelayCommand(UpdateXML);
}
However, I completely missed the point that the application can have multiple windows. When each window is opened the static Commands are set for that particular window.
Example:
Window A is opened-the constructors for the usercontrols set the RelayCommands and all is well.
Window B opened-the constructors for the usercontrols set the RelayCommands. Window A's command binding is lost!
So when I change the tab to Window A(the windows are tabbed) no commands are working.
I need some idea so that when I change the tab the active window always sets the commands. I can try to put the commanding in tab_selection_changed event, but somehow it is looking bad to me. Is there a proper way to do this? Any help is much appreciated.
Edit:
The question proved a bit confusing among the readers. I am not trying to make multiple subscribers for a command. At any given point only one window is active. This window consists several user controls-some of them loaded dynamically with the help of commands; but each command is handled by a single view model class-so no multi subscribers. My problem is the application can load multiple windows in tabs-only one window is active at any given point-but the user can do to a different tab and make another window active. As the view model constructor assigns the static RelayCommands, when each new window is loaded the static command is set to a new binding.
Window A opened-window A view model constructor sets the static command bind to its object command handler. Window A is active. Commanding is fine.
Window B loaded-window B view model constructor sets the static command bind to its object command handler. Window B is active. Commanding is fine.
Now, User select the Window A tab to set the Window A as active. Commanding wont work. Of course it wont as the Command is bind to Window B command handler.
Theoretically static commands can handle the scenario as at any given point there will be only one active window. But how??
The global command should be a CompositeCommand or similar approach (CompositeCommand is from Prism). This will allow multiple children to register with the command.
public static CompositeCommand SaveCommand = new CompositeCommand();
The command can then be accessed across the ViewModels or where applicable like so...
SaveCommand = new DelegateCommand<object>(Save, CanExecuteSave);
GlobalCommands.SaveCommand.RegisterCommand(SaveCommand);
You can then leverage the IActiveAware interface to define which Window is the active Window and act on the command accordingly.
There is also an MSDN posting on creating globally available commands. Don't forget to unregister the command to avoid a memory leak.
Any reason why have you decided to put it into static class?
class XMLEditorViewModel
{
public ICommand SaveRequest { get; private set; }
public XMLEditorViewModel()
{
SaveRequest = new RelayCommand(Save_Executed)?
}
}
Commands that are not view specific can be defined on static classes.
Commands that are view specific should either be defined on view-model,
passed as DataContext to view, enabling separate implementation
for different views with different view models,
or at least have the views pass a CommandParameter that
can be used to either identify them (e.g. reference to the view) or their DataContext.
If the commands are static, register them once only,
perhaps on a singleton used by view models.
create a globally available command, create an instance of the DelegateCommand or the CompositeCommand and expose it through a static class.
public static class GlobalCommands
{
public static CompositeCommand MyCompositeCommand = new CompositeCommand();
}
In your module, associate child commands to the globally available command.
GlobalCommands.MyCompositeCommand.RegisterCommand(command1);
GlobalCommands.MyCompositeCommand.RegisterCommand(command2);
To increase the testability of your code, you can use a proxy class to access the globally available commands and mock that proxy class in your tests.
The following code example shows how to bind a button to the command in WPF.
Execute My Composite Command

Resources