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
Related
My question is exactly like in the title.
I'm starting with Caliburn.Micro for MVVM approach (which also is new for me) and in every tutorial the first step is to remove the default MainWindow.xaml file and create a new UserControl file. Why is that? UserControl does not even accept a Title. Isn't it possible to build application using normal Windows? I already tried that, but with every launch I get error "Cannot find view for ViewModel", although both MainView.xaml and MainViewModel.cs are present. When I created a pair of USerControl and ViewModel for it, everything started to work as expected. So again, why Windows don't work?
It wouldn't really be a problem, but I'm thinking that some additions like Modern UI themes for WPF might not work without a window. I'm not sure of that.
Probably one solution would be to display a defined UserControl View inside of a Window, but it's just a workaround.
You could create your own custom shell window by creating a custom WindowManager:
public class CustomWindowManager : WindowManager
{
protected override Window CreateWindow(object rootModel, bool isDialog, object context, IDictionary<string, object> settings)
{
Window window = new Window();
window.Title = "custom...";
return window;
}
}
...that you register in your bootstrapper:
public class HelloBootstrapper : BootstrapperBase
{
...
protected override void Configure()
{
_container.Singleton<IWindowManager, CustomWindowManager>();
...
}
}
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.
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.
I have a ListBox that is bound to a ViewModel that exposes a parameter of type ObservableCollection. I have setup an ICommand that gets fired when one of the rows in the ListBox is selected. (using method 3 in this post - it works great by the way).
Now my question (which has nothing to do with method 3 described above or the ListBox) is when my ICommand is fired and what I want to do is navigate to a different page (eg: details page), where is the logic stored (or how do I do it?).
The reason I ask is that I am not sure how to setup the command method in the ViewModel class such that it remains testable.
ps: I am using Prism and was also wondering if it provides any classes/patterns for Navigation.
Just to elaborate on the use of IEventAggregator - it gives you a simple Pub/Sub model for sending arbitrary messages between decoupled (ie neither needs to know anything about the other) parts of the application. So we can get a reference to the IEventAggregator in our ViewModel constructor (this is automatically resolved for you by the framework) ie:
private IEventAggregator eventAggregator;
public PublisherViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
Then in our selection changed handler, we can publish the event:
var changedEvt = eventAggregator.GetEvent<MyListboxChangedEvent>();
changedEvt.Publish(selectedItemId);
This relies on our custom event class MyListboxChangedEvent:
public class MyListboxChangedEvent : CompositePresentationEvent<int> {}
So finally, in the ViewModel which responds to this action, we set up a subscription to the event, and corresponding handler method:
public SubscriberViewModel(IEventAggregator eventAggregator)
{
var changedEvt = eventAggregator.GetEvent<MyListboxChangedEvent>();
changedEvt.Subscribe(OnListBoxChanged, ThreadOption.UIThread);
}
public void OnListBoxChanged(int selectionId)
{
// do whatever we need
}
Seems like a lot of glue, but it becomes a logical method for wiring the different parts of the UI together, and it becomes second nature pretty quickly.
Have you considered using the EventAggregator to send the message that you want to show a different view. The StockTrader application included in the PRISM distribution will provide a good example of the use.
I've got a prism app, containing a Shell.xaml (with a MainRegion), ShellViewModel.cs.
This Shell window is opened when the app starts. Now I want to open a second Popup-Window containing the very same shell window (Shell.xaml, ShellViewModel).
The Shell definition is like in the prism StockTraderRI example. Shell.xaml contains a MainRegion (very simplified source):
<Window x:Class="Bsoft.Test.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.codeplex.com/CompositeWPF"
Title="MainWindow" Height="550" Width="825">
<Grid>
<ContentControl cal:RegionManager.RegionName="MainRegion"/>
</Grid>
</Window>
Code behind contains just the basic ViewModel reference:
namespace Bsoft.Test.bmedApp
{
[Export]
public partial class Shell : Window
{
[ImportingConstructor]
public Shell()
{
InitializeComponent();
}
[Import]
ShellViewModel ViewModel
{
set
{
this.DataContext = value;
}
}
}
}
The ShellViewModel is automatically inserted by the MEF loader:
namespace Bsoft.Test.bmedApp
{
[Export]
public class ShellViewModel : NotificationObject
{
[ImportingConstructor]
public ShellViewModel()
{
}
}
}
This does work like intended.
Now I want to open the shell window a second time as a popup window. It's easy enough to mark the Shell and ViewModel as not being shared using:
[PartCreationPolicy(CreationPolicy.NonShared)]
But my problems are:
1) I load other View(Models) into the MainRegion. How do I tell the program if the View(Model) should be loaded into the main Shell MainRegion or into the popup window MainRegion? I guess I need scoped RegionManagers, but I got no clue how to use them for this.
2) I've got some events (EventAggregator) for the Views loaded into a region to communicate notification and commands (status update, view closing, errors) for the Shell to report. How can I seperate the main shell events from the popup window events (since both are the same shell)?
I want to be able to open several of the popup windows, so using different region names for both is not enough for me, I need more separation. Maybe there is a way to create a separate internal prism/mef/region/container framework??
I do not completely understand what do you mean by opening two shells ?
If you run your silverlight application in two different windows or you have 2 instances of your WPF app then your Shells do not conflict.
Even if you have one application with 2 instances of Bootstrapper there is no conflict - your two shells work completely independently.
Let me know whether this help.
What you are trying to achieve is possible, although there might be some things I don't completely understand about your approach.
I assume that when you are talking about having two Shells you actually mean having two active windows at the same time.
There are many ways to achieve this in Prism, so let's get on with your doubts.
For (1) the best thing I can think of is creating a different instance of the Region manager an attaching it to the other Shell (the popup one). This is similar to working with scoped regions (as you would have a separate RegionManager), but you create the manager and attach it to the Shell instead. Then register the new RegionManager in MEF with a string Id so you can differentiate it from the MainWindow RegionManager and simply add regions to the correct region manager.
(2) is a different subject, as you are trying to get the same code to behave differently. Perhaps, if you require such different behaviors, using the same Shell class for both windows is not the best approach. If you require this kind of differentiability but would still like to reuse code I'd recommend using some form of inheritance and combining virtual methods in a BaseShell with template methods to perform the things that are different for each Shell.
I hope this illustrates my point.