MVVM - Global actions that act on the "currently selected object" - c#

I have a complex application that I am attempting to develop using MVVM (a pattern that I am new to) - the applicaton has tabs and docked windows each of which has the concept of a "selected object", and a global toolbar at the top of the application that has actions on it that need to act on the "selected object".
Imagine a slightly less complicated version of something similar to Visual Studio, for example:
If a pane is selected that contains a list view where the selected object is "inactive" then the "Activate" toolbar item should be enabled. (The global selected item is that list view item)
If however the user then clicks on a tab in another pane which has no selected object then that same toolbar item should be disabled (The global selected item is null).
Ignoring for the moment complications such as multiple selections, at the moment I have implemented this by creating an all-encompassing singleton* model class that represents the "application" itself, e.g.
class MyAppModel : INotifyPropertyChanged
{
public ISelectableObject SelectedObject { get; }
}
I then have "a system" (I admit I'm glossing over a lot of details here) in place for making sure that this property is updated (and the relevent events fired) when changes in the UI results in changes in the global "currently selected object", and the toolbar buttons use this property to determine availability etc...
However I'm getting hung up on the fact that this doesn't seem very MVVM-like (I read somewhere that UI state should be stored in the ViewModel?)
Is having a global model that represents "the application" in this way a good idea? (there are also other properties on there to keep track of other things in the application in a similar way, such as the open documents)
If not, what should I use instead to allow global components (such as items in the toolbar) to find out and keep track of what the "global selected object" is
(*) Which could just as easily be supplied using dependency injection

for MVVM you should definately have a master ViewModel that represents the top-level binding surface for your Views.That master ViewModel will have a 'SelectedItem' property that participates in the INotifyPropertyChanged notifications of the ViewModel. You should then bind your relevant ItemsControl(s) (TabControl etc) SeledctedItem to this ViewModel Property, then other parts of your app can bind to the ViewModels SelectedItem property and bindings will change automatically.

try to register PreNotifyInput event of Inputmanager.current. and register this at applicaiton level this event into global level.
InputManager.Current.PreNotifyInput += new NotifyInputEventHandler(Current_PreNotifyInput);
also refer to Inputmanager class
http://msdn.microsoft.com/en-us/library/ms617136

Related

PRISM + TabControl + Validation = Pain

In my application I have dialogs with mutiple tabs. Im using Prism to register the views with the TabControl.
What I want is a validation for the entire dialog to diable/enable the save button.
The Problem: Currently we have a view-triggered validation. Means every bound item implements the IDataErrorInfo interface. When the View is displayed the binding triggers the interface and displays and error on the UI. The Control has the HasError Property set to true, save button gets disabled.
But the validation does not get tiggered until the view is displayed. Should I move the validation to the ViewModel and validate the Properties on my own or is there a solution to validate inactive views in a TabControl?
There isn't quite enough information to go off of I your question, so I can only guess. First off, you must understand that there are no inactive views in a TabControl. There is only one view in the TabControls visual tree at one time, and that is the selected tab. This means the other views are removed from the visual tree until they are selected. This really doesn't matter though, as validation is controlled via the ViewModel. You ViewModel most likely has a Command bound to your Save button. This command should have a CanExecute defined returning IDataErrorInfo.Error != null (meaning you have no errors). This is where you will check the validity of your objects. Return false if you have any errors, and true if you don't. Hook into the property changed event of your objects, and call the SaveCommand.RaiseCanExecuteChange method to recheck the state of your button.
If each view a tab has it's own ViewModel, hence it's own Save command, I would recommend using a CompositeCommand. This is really an unknown since I don't know how you have architected your dialogs, views, or ViewModels.

How can control in the view get specific data from view model?

I have multiple of views (user controls), each with its own ViewModel. To navigate between them I am using buttons. Buttons display image and text from corresponding view model and also need column and row (because there are like 10 views: 10 columns with different number of rows each).
Right now buttons are created dynamically (I made a Navigator control for this) and for view models I have base class to hold text, image, column and row. Number of views available will be different (depends on user level and certain settings), that's why it's I need control here.
Question: how shall my control get data from view models?
Right now I have interface INavigator, defined in (lol) control itself. And view models implement it. I could go opposite, let my control to know about view models. Both looks wrong.
There is a single Navigator control what has, lets say, Items bound to a list of view models. It can cast each view model to INavigator or ViewModelBase (common for all pages) to obtain specific view model image, text, column and row. So either view model knows about control (to implement INavigator) or control knows about ViewModelBase.. And this is a problem, both solution bind tight control and view models, which is bad in mvvm.
Schematically
The way you've drawn your diagram answers your own question as to how you should structure the code for this.
What you need is one VM (let's call it MainVM) which contains an ObservableCollection<VMBase> of the other VMs (using your base type so that they can all happily live in the same collection).
Your View needs an ItemsControl (bound to your ObservableCollection<VMBase>) where you specify a DataTemplate for the Button using the properties exposed by the VMBase type only. Set the Command property in the Button to call SwitchCommand, CommandParameter is set to the item itself (i.e. {Binding .}).
Your View also needs a ContentControl bound to a SelectedVM property on MainVM which you can populate.
Implement SwitchCommand to set the SelectedVM property based on the value from the CommandParameter.
public void ExecuteSwitchCommand(object parameter)
{
var vmBase = parameter as VMBase;
if (vmBase != null)
SelectedVM = vmBase;
}
All properties mentioned here should be INotifyPropertyChanged enabled so that the View registers when they change and updates the UI.
To get the different UIs for the ContentControl, add type-specific DataTemplates for each of your specific VM types to the Resources file of your View (or if you're smart and are building a custom plug-in framework, merge the Resource Dictionaries).
A lot of people forget with MVVM that the whole point is that there is a purposeful separation of View from ViewModel, thus meaning you can potentially have many Views for a single ViewModel, which is what this demonstrates.
I find it's easiest to think of MVVM as a top-down approach... View knows about it's ViewModel, ViewModel knows about its Model, but Model does not know about its ViewModel and ViewModel does not know about its View.
I also find a View-first approach to development the easiest to work with, as UI development in XAML is static (has to be).
I think a lot of people get to wrapped up in 'making every component (M, V, VM) standalone and replaceable', myself included, but I've slowly come to the conclusion that is just counter-productive.
Technically, sure you could get very complicated and using IoC containers, create some ViewLocator object which binds a View-type to a ViewModel-type, but... what exactly does that gain you besides more confusion? It makes it honestly harder (because I've done this at one point) to develop because now you've lost design-time support first and foremost, among other things; and you're still either binding to a specific view model interface in your view or creating the binding at run-time. Why complicate it?
This article is a good read, and the first Note: explicitly talks about View vs. ViewModel. Hopefully, it will help you draw your own conclusions.
To directly answer your question, I think having your ViewModels implement an INavigator interface of some sort is probably ideal. Remember your VM is 'glue' between your view and model/business logic, its job is to transform business data into data that is consumable by your views, so it exists somewhere between both your UI and business layers.
This is why there are things like Messengers and View Services, which is where your navigator service on the ViewModels can fit in nicely.
I think the design has led to a no way out situation.
I believe that creating a custom button control where the dependency properties tie the image, the row and column actually provide a way for the page, which it resides on ,to get that information to them; whether they are dynamically created or not.
Continuing on with that thought. There is no MVVM logic applied to a custom control, the control contains what it needs to do its job and that is through the dependency properties as mentioned. Any functionality of the button should be done by commanding; all this makes the button data driven and robust enough to use in a MVVM methodology or not.
Question: how shall my control get data from view models?
There should only one viewmodel which is the page the control resides on. The control is simply bound to information which ultimately resides on that VM. How it gets there, that is up to the programmer. If the button is going to contain state data, that is bound from its dependency property in a two way fashion back to the item it is bound to.
By keeping VMs out of the buttons and only having one VM that is the best way to segregate and maintain the data. Unless I am really missing something here....
Same as others here I find it a bit hard to actually understand what you are asking, so this is quite general. The answer to the question header is simply: the Control gets the data from the ViewModel through bindings, always. You set the DataContext of your Control to the corresponding ViewModel, and from there you keep the ViewModel and the Control synchronized:
If you add an ItemsControl containing buttons to the View, you add an ObservableCollection<ButtonViewModel> to the ViewModel and bind the ItemsSource of the ItemsControl to this.
If you allow the user to dynamically add content to the View, the actual code that does it resides in the ViewModel, e.g. when the user clicks on a button "Add Button", you use the Command property to call a ViewModel method that adds a ButtonViewModel to the collection and the View will automatically reflect your changes.
There do exist complicated cases that are impossible to code exclusively in the ViewModel, I have found Behaviors to be the missing link there, but I'll get into that when you show me the specific case.
If you'd like to get a working example, please provide as much code as you can, with your exact expectations of what it should do.

Conditionally validate Dynamically generated Controls in Silverlight

I am having a form with different type of controls like Text Box, Drop downs, Check box, Radio buttons etc. All these controls are loaded dynamically from database at run time.
I want to perform validation on Text box on conditional basis. For example, If we have selected any value in drop down, then you must have to fill details in Text box. Otherwise text box details are not required.
I am open to use database to perform this task and I am using MVVM pattern in my project.
Any help on this is highly appreciated.
Thanks.
(I started this as a comment, but it ended up being too long).
In theory you have access to all these controls and their values in your ViewModel.
Without knowing the specifics of your program, it's difficult to suggest anything useful, but in essence you need to expose some more properties from your ViewModel (probably boolean) which will be calculated based on the values in your controls. Then you need to bind IsEnabled properties on your controls to these new properties.
Sounds simple, but I think you have some architectural problems which will make it difficult to implement what I suggested above. In order for this to work and automatically update your controls whenever other controls' content change, your ViewModel needs to implement INotifyPropertyChanged and raise PropertyChanged event every time you update one of those boolean properties.
I think what you're trying to do could be achieved with ItemsControl and DataTemplates (and maybe DataTemplateSelectors). This will allow you to store "data" in your ViewModel (say List or something more specific) without referencing the actual Controls and the relevant DataTemplates will add the right controls for different data types you have in your ViewModel.

WPF Bind different commands to button/menuitem

I would like to ask you for an advice.
The application that I am working on has a docking control and should support working with different "documents". Therefore there can be multiple windows(tabs) opened at the same time.
Each can host a different content. The "documents" that I referred to could be a text file,
an excel style sheet or the main control that this app is being developed for (a geographical data visualization).
I am using the MVVM pattern and MVVM Light library. Note: I have a third party control for the excel spreadsheets.
Now I have a menu bar and a toolbar where I have common menuitems (in menubar) / buttons (in toolbar) like 'save', 'cut', 'copy' etc... When you switch between between the tabs, the 'save' button should call the appropriate save functionality.
The same goes for cut/copy/paste:
When in a tab with text document - cut/copy/paste should operate with text (there are wpf built-in commands for this).
When in the main control - It should work with the graphical elements (I will have to implement these)
When in the spreadsheet - It should work with the enclosed third party commands for the spreadsheet control.
Furthermore, there can be a tab that has some text selected - thus the menuitem/button for cut/copy should be enabled when switched to this tab, while some other tab has no graphical elements selected thus the menuitem/button for cut/copy should be disabled when switched to this tab.
With the save command I can imagine one possible way to do it, but still, I am not sure whether it is a good way to implement it: Have a RelayCommand in MainWindowViewModel
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand(() => this.SaveFile());
}
return _saveCommand;
}
}
and the SaveFile() method would call some other 'save method' on the datacontext of the selected tab (which would be a viewmodel for the according "document" type).
However I don't know how to do the enabling/disabling of the save button/menuitem and I am clueless about how to achieve the different cut/copy/paste functionality.
I apologize for the length of the question. Maybe I could have just asked:
"How do you bind different cut/copy/paste commands to the buttons depending on which part of application is selected/active?".
But I felt the added context of what I am trying to achieve would help answer the question.
and the SaveFile() method would call some other 'save method' on the
datacontext of the selected tab (which would be a viewmodel for the
according "document" type).
The SaveFile should be implemented by the ViewModel without going back to the View.
Create several ViewModels to represent different kind of documents and let each implement its Save functionality.
The CanExecute method of a command can be used by the View to decide whether or not the menu item or button should be enabled. All you need is to implement the CanExecute method in the ViewModel. In most cases a Save command's CanExecute would use an IsDirty or similar property.
EDIT
For menu items that should be context/active tab item aware you could create a main ViewModel with the commands for the menu and a collection of ViewModels (one for each tab item)
In the command handlers of the context aware menu items get the active ViewModel and pass the command on.
To get the current TabItem, bind the SelectedItem to a property of the main ViewModel (the type of the property could be a base class of the ViewModels).
That way you do not need to get back to the View to get the current tab item.

MVVM - what is the ideal way for usercontrols to talk to each other

I have a a user control which contains several other user controls. I am using MVVM. Each user control has a corresponding VM. How do these user controls send information to each other? I want to avoid writing any code in the xaml code behind. Particularly I am interested in how the controls (inside the main user control) will talk to each other and how will they talk to the container user control.
EDIT:
I know that using events-delegates will help me solve this issue. But, I want to avoid writing any code in xaml code-behind.
Typically, it's best to try to reduce the amount of communication between parts, as each time two user controls "talk" to each other, you're introducing a dependency between them.
That being said, there are a couple of things to consider:
UserControls can always "talk" to their containing control via exposing properties and using DataBinding. This is very nice, since it preserves the MVVM style in all aspects.
The containing control can use properties to "link" two properties on two user controls together, again, preserving clean boundaries
If you do need to have more explicit communication, there are two main approachs.
Implement a service common to both elements, and use Dependency Injection to provide the implementation at runtime. This lets the controls talk to the service, which can in turn, keep the controls synchronized, but also keeps the dependency to a minimum.
Use some form of messaging to pass messages between controls. Many MVVM frameworks take this approach, as it decouples sending the message from receiving the message, again, keeping the dependencies to a minimum.
Your conceptual problem is here:
Each user control has a corresponding VM.
Having a separate ViewModel for every view pretty much defeats the concept of a ViewModel. ViewModels should not be one-to-one with views, otherwise they are nothing but glorified code-behind.
A ViewModel captures the concept of "current user interface state" -- such as what page you are on and whether or not you are editing -- as opposed to "current data values'.
To really reap the benefits of M-V-VM, determine the number of ViewModel classes used based on distinct items that need state. For example, if you have a list of items each of which can be displayed in 3 states, you need one VM per item. Contrarily, if you have three views all of which display data in 3 different ways depending on a common setting, the common setting should be captured in a single VM.
Once you have strucutred your ViewModels to reflect the requirements of the task at hand you generally find there is no need nor desire to communicate state between views. If there is such a need, the best thing to do is to re-evaluate your ViewModel design to see if a shared ViewModel could benefit from a small amount of additional state information.
There will be times when the complexity of the application dictates the use of several ViewModels for the same model object. In this case the ViewModels can keep references to a common state object.
There are many differenct mechanisms for this, but you should first find out in what layer of your architecture this communication belongs.
One of the purposes of the MVVM framework is that different views can be made over the same viewmodel. Would those usercontrols talk to each other only in the view you are currently implementing, or would they have to talk to each other in other possible views? In the latter case, you want to implement it below the view level, either in the viewmodel or the model itself.
An example of the first case may be if your application is running on a very small display surface. Maybe your user controls have to compete for visual space. If the user clicks one usercontrol to maximize, the others must minimize. This would have nothing to do with the viewmodel, it's just an adaption to the technology.
Or maybe you have different viewmodels with different usercontrols, where things can happen without changing the model. An example of this could be navigation. You have a list of something, and a details pane with fields and command buttons that are connected to the selected item in the list. You may want to unit test the logic of which buttons are enabled for which items. The model isn't concerned with which item you're looking at, only when button commands are pressed, or fields are changed.
The need for this communication may even be in the model itself. Maybe you have denormalized data that are updated because other data are changed. Then the various viewmodels that are in action must change because of ripples of changes in the model.
So, to sum up: "It depends...."
I think the best solution would be using Publisher/Subscriber pattern. Each control registers some events and attaches delegetes to events exposed by other controls.
In order to expose events and attach to them you would need to use some kind of Mediator/EventBroker service. I found a good example here
The best way to do this in my opinion is via Commanding (Routed Commands / RelayCommand, etc).
I want to avoid writing any code in the xaml code behind.
While this is a laudable goal, you have to apply a bit of practicality to this, it shouldn't be applied 100% as a "thou shalt not" type of rule.
You can communicate between elements on the UI by using element binding, so assuming a user control you created exposes a property, the other user controls could bind to it. You can configure the binding, use dependency properties instead of basic properties / implement INotifyPropertyChanged but it is in theory possible, but does require some forethought to enable to communication this way.
You will probably find it far easier using a combination of events, code and properties than try a pure declarative way, but in theory possible.
You can share some View Model objects between controls as well as Commands...
For example, you have some main control, which contains two other controls. And you have some filtering functionality in the main control, but you want to allow user to set some part of the filter in the first sub-control (like "Full filter") and some part of the filter in another (like "Quick filter"). Also you want to be able to start filtering from any of sub-controls. Then you could use code like this:
public class MainControlViewModel : ObservableObject
{
public FirstControlViewModel firstControlViewModel;
public SecondControlViewModel firstControlViewModel;
public ICommand FilterCommand;
public FilterSettings FilterSettings;
public MainControlViewModel()
{
//...
this.firstControlViewModel = new FirstControlViewModel(this.FilterSettings, this.FilterCommand);
this.secondControlViewModel = new SecondControlViewModel(this.FilterSettings, this.FilterCommand);
}
}
public class FirstControlViewModel : ObservableObject
{
//...
}
public class SecondControlViewModel : ObservableObject
{
//...
}
In the main control XAML you will bind sub-controls DataContext to the appropriate View Models. Whenever a sub-control changes filter setting or executes a command other sub-control will be notified.
As others have said you have a couple of options.
Exposing DepedencyProperties on your user controls and binding to those properties provides a pure XAML solution in most cases but can introduce some UI dependencies in order for the bindings to see each other
The other option is a decoupled messaging pattern to send messages between ViewModels. I would have your user controls bind to properties on thier own VM's and then on the property change inside that VM it can "publish" a message that notifies other "subscribers" that something has happened and they can react to that message however they want to.
I have a blog post on this very topic if it helps: http://www.bradcunningham.net/2009/11/decoupled-viewmodel-messaging-part-1.html
If you're using strict MVVM, then the user-control is a View and should only "talk", or rather, bind, to its ViewModel. Since your ViewModels most likely already implement INotifyPropertyChanged, as long as they have a reference to each other, they can use the PropertyChanged events to be notified when properties change, or they can call methods (better if it's through an interface) to communicate with each other.

Categories