I'm working on a area of an application that consists of the following parts:
Explorer - Contains a TreeView
PropertyInspector - Contains a PropertyGrid
Editor - Contains an Explorer and a PropertyInspector
As you can see, the parts are organized in a hierarchical fashion. My question is:
How do I make the selected item in the tree view used by the explorer the selected object of the property grid used by the property inspector?
When the tree view raises the SelectedItemChanged event a command is sent to ExplorerViewModel. The PropertyGrids SelectedObject is bound to a property on the PropertyInspectorViewModel. How do I connect the two view models? One approach could be to let EditorViewModel attach a method on the PropertyInspectorViewModel to an event raised by the ExplorerViewModel when the command is received but is this the way to go or can it be solved through data binding somehow?
The reason why the editor doesn't use a tree view and property grid directly is because the explorer and the property inspector, together with all the undo/redo functionality, validation code etc, are meant to be reused in other areas.
I would highly recommend using a MVVM framework. I had similar requirements and decided to adopt Tony Sneed's Simple MVVM Toolkit. It is very lightweight,very well documented and easy to understand, provides very useful capabilities and basically does must of the MVVM plumbing for you.
What you need in this case is make use of the Message Bus features:
"MessageBus
Sometimes you need to pass messages among view-models in your application. A good example would be when you want to navigate to a particular view based on some business logic. It would not be a good idea to reference the main view-model directly from another view-model. Doing so would create interdependencies between view-models that would be difficult to maintain (a phenomenon referred to as spaghetti code). This is where a message bus (also called an event mediator or aggregator) comes in handy. The CustomerViewModel, for example, can then send a message to the message bus using a specific token, and the MainPageViewModel can subscribe to receive a message whenever someone sends a message with this same token to the message bus. Message tokens are simply strings that can be defined as constants in a class." From the Programming Reference page
In addition to that I also used Josh Smith's RelayCommand to simplify ICommand notifications as #eran otzap suggested
The editor VM should coordinate the two child VMs.
Add a selected item property to explorer VM (if it's not there already) and make sure it fires property change notification.
The property inspector VM should have a similar property that is the item to show properties of.
The editor then watches the explorer VM's selected item property for changes, and when it changes passes the new item to the property inspector VM.
There's no need to involve a message bus here. Message buses are for ViewModels that are loosely coupled to pass messages to each other. For example application wide notification that a file has been opened.
If you used a message bus here then any reuse of the VMs would also trigger the message, which is not what you want.
Related
I'm using Caliburn Micro to create a Visual Studio style interface for my application.
The user could have multiple errors in multiple windows which are all shown on an 'errors' view which is docked at the bottom of my application. I've managed to implement a nice notification mechanism so that when changes are made to any document, the errors VM is notified and queries the source object.
I'm having trouble, however, in figuring out how to get a double-click to navigate to the source of the error.
Imagine I have bound 'description' to a textbox on my document view and this provides a 'description is required' validation message. Double clicking the message should navigate the user to the document in question and focus on the textbox.
The validation objects that provide the messages already contain a reference to the viewmodel (as an IViewAware interface) which allows me to call GetView() to get a reference to the view. I've tried using this approach to enumerate the bindings on my view and find the controls that are bound to a particular property, though this is very slow and more of a brute force - pot luck approach
Does anyone have any recommendations on approaches to this problem? My only other thought is to pass responsibility of the 'focus' action to the view itself and have that decide how to handle a request for navigation to a certain property - maybe using attached properties to identify particular controls by a unique property identifier. The application could cache this list in a dictionary to make things quicker (all the processing done up front as the app is loading)
I assume Visual Studio implements this quite easily for the standard code editor window, since it just needs a line number to jump to the right place..
Ok so after a couple of hours messing about with this I've come to a solution which seems to work ok. I've implemented an interface class for my views which allows them to navigate.
The basic setup is:
ErrorsViewModel - receives notifications that a business object needs requerying for errors and manages the list of errors and when it needs to query (so that any work can be done on a background thread to prevent locking the UI)
WorkspaceViewModel - the main workspace VM that hosts the tool panes and documents
ICanNavigate - interface that the views will impelement to decide how to handle navigation arguments
NavigationEventMessage - the message that is sent to the event aggregator to provide a navigation notification and arguments
The ErrorsViewModel receives an event message from another VM via the event aggregator and eventually queries the object (when it can). Once the errors are gathered, it shows the items in the view (in a grid control at the moment).
When the user double clicks on a row a NavigationEventMessage is dispatched which contains some arguments such as the view that dispatched it and the target business object etc. It is handled by..
1) The WorkspaceViewModels view. This allows it to bring the correct document into activation by looking at the DataContext of each object and checking if it matches the view passed in the message
2) The target documents view, the view decides what to highlight/show/seek to based on the arguments sent in the message
This seemed to be the most logical way to separate the VM from the View as the ViewModels still have no coupling with the views. The view just implements the interface, and the VM calls it if it's there and passes in the args.
Okay, so I am working with Microsoft Prism in WPF using a MVVMC(or MVCVM) pattern.
In my ChatModule I have a series of Views, ViewModels, and one Controller.
For the Views I have
ChatAreaView - Displays the chat messages that come in to be read. This is hosted inside of a TabControl region so that I can have chat windows between the user and other users, or maybe file transfer windows, etc.
UserAreaView - This is a list of the users. Right clicking has context menu to interact with them... like sending a file or whispering.
MessageAreaView - This is where the user types in messages to be sent to all of the others.
For each view, I have a corresponding ViewModel. ChatAreaViewModel, UserAreaViewModel and MessageAreaViewModel. These ViewModels essentially only contain properties.
For example, the UserAreaViewModel defines a struct of type User which is essentially just a Name. Actually this is defined outside of the class, but still... it uses it. It has an ObservableCollection to store a list of all the Users who are currently connected. It also has ICommand properties defined to interact with the user. Right now I have SendFile, Whisper and Nudge... with intent on adding more in the future.
The Controller creates these views and ViewModels, and marriages them. It news them up, assigns the ViewModel as the corresponding View's DataContext, and sets all the initial properties of the ViewModel. Over the lifetime of the module, it will react to user interaction and execute DelegateCommands that it has assigned to each of the ViewModel's ICommand properties. These will further alter the state of the properties in a ViewModel.
I am using the actual types of Views and ViewModels, instead of interfaces, like such.
#region Views
ChatAreaView viewChatArea;
UserListView viewUserArea;
MessageView viewMessageArea;
LoginPromptView viewLoginPrompt;
#endregion
#region ViewModels
ChatAreaViewModel viewModelChatArea;
UserAreaViewModel viewModelUserArea;
MessageAreaViewModel viewModelMessageArea;
LoginPromptViewModel viewModelLoginPrompt;
#endregion
Would things be a lot more neat, less coupled if I defined interfaces for the Views and ViewModels, and operated on these interfaces within the controller instead of the concrete implementations? Could I then just register them with my Container in the Module class(which is essentially the root of each Module)?
What do I have to gain from doing this? How would I implement an interface for each view to distinguish them from the others? They don't really do ANYTHING except have XAML... and teh ViewModel's don't really do anything either except have certain properties. And those properties might be subject to change. On the UserAreaViewModel for instance, I will definitely want to add more commands so a user can interact with another user in different ways.
Can somebody help me out here? In my mind I'm thinking I should be abstracting this stuff, but I don't really know a logical way I should be going about it, or even if it's a wise idea to do so. What do I have to gain?
Thank you for your time. The below image is an example of what I'm working on. Ignore the Add new Item button and the styling of everything... that's not what I'm working on right now.
loosely coupled - can replace an entire class with altogether different implementation in future.
independent development.. can inject a dummy UI / view until final UI gets ready. two pieces can evolve at the same time (after having a common contract).
no need to add references to the modules (implementing the view). can use ConfigurationModuleCatalog to discover types from config file.
I have a view that containing a user control.
I want to make the view model to notify the user control to do some action (refresh its' data).
I can pace some bool property in my VM and bind it to the user control so it will trigger it, but I think it's a little abuse of the property.
I feel I missing something, and can't find a solution. I will appreciate any comment.
My solution:
I'm going to solve the problem by registering an event of collection changed in my UserControl, since I'm binding to that control a view of a collection thru CollectionViewSource.
My original problem was how to make a chart control inside the UserControl to get updated when I filtering the data source. After the filtering operation from the VM, an event will raise and I will make the chart to get refresh either in the UserContol's code behind
Since you've indicated MVVM tag solution would be pretty straightforward - just bind control to a data by exposing data items/whatever by ViewModel so any data updates would be automatically dispatched to View via bndings if you would provide INotifyPropertyChanged / INotifyCollectionChanged by a ViewModel.
If you've bound your UserControl correctly, you shouldn't need to manually refresh the data since WPF will automatically update the UI when INotifiyPropertyChanged triggers
That said, if you wish to send a message from the ViewModel to the View, I usually use an messaging system of some kind to keep the Views and ViewModels separate. The two I've used and would recommend are MVVM Light's Messenger, or Microsoft Prism's EventAggregator
Either system will allow your ViewModel to broadcast a message, and your View can subscribe to receive these messages and handle them as needed.
I am looking for a truly decoupled way of supporting navigation in a Silverlight application using MVVM. I am trying to accomplish more of a "purist" implementation of the pattern where the UI is completely separated from the ViewModels so that the application can actually run entirely without a UI. To do this, I need to support navigation without UI concerns.
I have several ideas how to accomplish this (with Messaging, etc) but haven't come up with a good way of "mapping" the View to the ViewModel so that the UI can show the appropriate View when the ViewModel is "displayed". I recall coming across an article some time ago that described a solution to this very problem but can't seem to locate it online anymore.
Does anyone know how to find this article or have any experience solving this problem?
So here's my somewhat long-winded description what we ended up doing:
First, we decided to use the built-in Page Navigation framework. We had multiple reasons but since it is built-in and is also the navigation framework du jour in Windows 8, we opted to try this approach.
I should also mention that we use MVVM Light and MEF in our applications. (This comes into play below.)
To make this work, we created an application Shell (UserControl) that contains the Frame control. The Shell's DataContext is set to an instance of the ShellViewModel which exposes a single CurrentPage property (of type String). We then bind the Frame's Source property to CurrentPage. This approach is similar to Rachel's app-level ViewModel.
The ShellViewModel registers with the Messenger to receive CurrentPageChanged messages. When the message is received, the CurrentPage property is updated, the PropertyChanged event raised and the UI updated. The message originates from the NavigationService (which implements INavigationService and is injected/imported using MEF).
The NavigationService exposes a NavigateTo method which accepts the string name of the ViewModel representing the destination. This name matches the contract name applied to the ViewModel when exported (using MEF) and used to lookup the instance using our ViewModelLocator.
In the NavigateTo method, we use the ViewModelLocator to retrieve the ViewModel instance, call Deactivate on the current ViewModel (if one), call Activate on the new ViewModel then send the CurrentPageChanged message with the name of the new view as a parameter. Activate/Deactivate are helper methods on the ViewModels that allow us to perform any necessary tasks when the ViewModel is navigated to or from.
This appears to be working well and gives us a very MVVM-ish implementation with all navigation isolated from our ViewModels via the INavigationService and messaging.
The only down-side right now is that while we are using string constants in code to represent the ViewModel names, we are still hard-coding the strings in the Views to set the DataContext. I will be looking into a way to set the DataContext automatically as part of the navigation 'tooling'.
I should mention that this approach was parsed together from a number of sources, including (but not limited to) Rachel and the following links:
http://blogs.microsoft.co.il/blogs/eladkatz/archive/2011/01/25/adapting-silverlight-navigation-to-mvvm.aspx
http://blog.galasoft.ch/archive/2011/01/06/navigation-in-a-wp7-application-with-mvvm-light.aspx
http://www.geoffhudik.com/tech/2010/10/10/another-wp7-navigation-approach-with-mvvm.html
Usually I have a ViewModel for the entire app, and it contains the CurrentPage and all navigation event handling.
On the View side, I use a ContentControl with it's Content bound to CurrentPage, and use a DataTemplateSelector to determine which View to display for which ViewModel
There's an example here if you're interested, although it uses DataTemplates instead of a DataTemplateSelector.
I am wondering what the correct mechanism to enable communication between controls in WPF is. My goal is to not use conventional events and have to manually wire them up. The default behavior of routed commands (tunneling, bubbling) seems to be along the right lines but I guess I'm missing something.
Routed events are a new infrastructure provided by WPF which allows events to tunnel down the visual tree to the target element, or bubble up to the root element. When an event is raised, it “travels” up or down the visual tree invoking handlers for that event on any element subscribed to that event it encounters en route. Note that this tree traversal does not cover the entire visual tree, only the ancestral element
That is from this WPF Article
Using the image in the article, I want "Immediate Element #1" to initiate (raise) an event and then have "Immediate Element #2" handle that event. I'd like to achieve this without having to put any code in the "Root Element".
Basically fire an event (save, status updated, selection changed, etc..) from any where in my app, then have it be handled somewhere else with out the 2 parties knowing anything about each other. Is this possible?
I dont believe data bainding is the answer. I'd like to use Routed Events / Commands as they were designed just across the entire tree, not just within the source control's branch. Maybe it can't be done using routed events / commands, and data binding is the answer. I just dont know...
Any ideas?
The best mechanism is to refactor and separate the data view from the data model.
Create a data model that provides DependencyProperty properties (rather than standard C# properties) for each data point, but does not provide a UI. The values in the data model can affect each other when modified.
You can then bind each WPF element to the appropriate DependencyProperty from the data model. Modify the value in one element and all other elements are updated to reflect any data model changes in the bound properties.
If you want to transfer data between elements, Binding is the way to go. There are many tutorials and books about this on the net.
If you want to effect Style changes, then you can use DataTriggers, which also use Bindings.
There is no way to send events in the traditional sense between unrelated controls without wiring it up in the common root.