July 2010 - Posts
Update 29/07: The project was updated, there was a minor bug in the object changed notifier.
In continuation of a previous post -
WPF – Editing Mode with Save and Cancel Capability
In this post I will demonstrate an elegant and simple solution that you can utilize regarding the first option presented in the previous post.
The Use-Case
If you want to open an editable view over an existing read-only view with cancel capability, you usually wish you could use the same ViewModel instance as the DataContext.
However, this means that changes are reflected in the read-only view, plus, how would you implement the cancel operation?
The following solution demonstrates a dynamic proxy that I had written that you can use to provide elegant solution in cases where your ViewModel is simple.
By ‘simple’ I mean the ViewModel contains a set of self-contained properties (their setters don’t affect anything else) and that there are no commands needed for the editable view.
The common approach for such simple cases is defining the bindings with UpdateSourceTrigger set to ‘Explicit’.
My solution is simpler, it doesn’t require you to do that, thus you need not update the bindings upon save and such.
Moreover, my solution still provides a way to support IDataErrorInfo on the ViewModel!
The right pane contains the read-only view:
| When clicking Edit, a dialog is shown:
|
Let’s see the code
When the user clicks ‘Edit’, the code opens the dialog with the DataContext set as follows -
Code Snippet
var proxy = new ViewModelProxy<EmployeeViewModel>(emp);
editView.DataContext = proxy;
When the user chooses Save or Cancel in the dialog, the following code merges the changes into the actual EmployeeViewModel -
Code Snippet
bool? result = editView.ShowDialog();
if (result.GetValueOrDefault())
{
proxy.AcceptChanges();
}
That is all that is needed! Nothing else!
The proxy has another method - ‘RejectChanges’ if you need to reject the changes and go back to the state of the original EmployeeViewModel.
How is the magic done?
I implemented the ViewModelProxy using the DynamicObject that comes with .NET 4.0, sweet.
This can be done in previous .NET framework versions too. It would require more code though, you could implement a dynamic property bag with a type descriptor.
The ViewModelProxy performs mainly the following parts -
- When a property is set on it – it stores it into a local dictionary and notifies upon property changed
- When a property is requested from it – returns the value from the local dictionary if present, or from the source ViewModel otherwise.
- AcceptChanges applies the local dictionary values to the source ViewModel
- RejectChanges clears the local dictionary and notifies upon property changes.
- I mentioned it above, my solution supports IDataErrorInfo, make sure to check out the sample
You can download the source here - Zuker.WpfSamples.zip.
In the next post I will demonstrate another infrastructure I had implemented that you can use for more advanced scenarios.
A common scenario that is required by applications is the ability to show an entity in a view mode, and when the user wishes to edit that entity, another view is loaded in edit mode.
In most cases, you would allow the user to save or cancel his changes.
To illustrate the concept, consider the following interface -
| When a user clicks ‘Edit’:
|
Usually, in a MVVM application, you would have a view model for each list item on the left screenshot.
When the user clicks ‘Edit’ and the editable view is shown as modal dialog, you may want to use the same view model.
However, if it is the same instance, how would you support cancelling the save operation. Additionally, how would you prevent that changes in the edit mode won’t be reflected in the view mode until the user chooses to save the changes. (If you use bindings, the change would reflect in the view mode as well, as long as the binding is active and the source is updated).
Well, there are numerous ways to go about it.
- Use the same instance: To use the same actual instance, a common approach is to define all the bindings in the editable view with update source trigger set to ‘Explicit’.
This gives you the ability to save state explicitly when the user chooses to save his changes.
This method isn’t suitable for all the cases. If you have more than a plain simple view model where properties may set other properties or commands can be invoked from the edit view – you’re in a dead end. Plus, you can’t use the IDataErrorInfo validation model since the bindings don’t update the source nor calls its validation methods.
- Use a separate instance using some sort of cloning and merging methods: To support more advanced scenarios, or simply provide a complete solution, you would generally prefer binding the editable view to a new instance.
Obviously, you would need to construct the new instance, either imperative instantiation/cloning or use serialization which is quite a common technique in this scenario.
Finally, if the user chooses to save his changes, you would need to merge back the changes to the instance in the view mode or simply replace it with the new instance.
I plan to address both the approaches mentioned above in the next post to show some nice extensions that you can use, so stay tuned.
Update 29/07: The project was updated, there was a minor bug in the object changed notifier.
A few years back I was looking for a good place to start with for building a nice feedback control.
The purpose was to allow end-users to provide feedback on the application on any given subject.
After browsing the internet, I found the following -
Creating a 3D book-shaped application with speech and in using WPF 3.5 by Roberto Sonnino.
It’s a very cool and a beautiful project, nice work Roberto!
To make the application fit into my solution, I made the following changes -
- I removed the speech and rotate functionality. There was no need for these features (although they’re very cool), you can edit the code to enable it back if you like.
- I wrapped it into a reusable control. There was quite some work to do in order to do that, but I managed :)
- Currently, there’s a small hack that needs to be done in order to use it with more than one instance. I didn’t invest the time to solve this since in most cases, you should have one instance in the application and that’s it.
- In the control, I edited the template and functionality to provide the ability of saving the feedback input
- I added some API methods to get the ink as image and the text in order to allow doing something useful with it
You can then choose to save the input and persist it somewhere, send notifications and whatnot.
Then, developers and project managers can take a look at the images and feedback users had sent them.
So.. how does it look like?
All you need to do in order to add this control is place the following XAML in your application’s main area -
Code Snippet
xmlns:controls="http://schemas.zuker.com/wpf/xaml/presentation"
<controls:FeedbackView x:Name="feedbackView" />
When running, there is a new addition in the bottom right corner (that’s the default) -
The above image is a bit small, I recommend you get the sample in the link below to see it in action.
When clicking it, you get the book working for you -
You can download the source and showcases here - Zuker.WpfSamples.
NOTE: The project contains more things I plan to address in future posts, so stay tuned :) (I will post when I update the project too)
In the application, just click the ‘FeedbackViewShowcase’ in the list.
Thanks for Roberto who was kind enough to allow me to share this.
Make sure you visit WCF Contrib home.
This release adds core optimizations and internal fixes to the infrastructure.
There are no breaking changes from the last version, only internal improvements.
In previous versions you may have encountered synchronization related errors such as recursive write/read locking.
These issues were resolved and you should no longer experience that.
This release is highly recommended over previous versions, make sure you use this one.
Update 29/07: The project was updated, there was a minor bug in the object changed notifier.
Be sure to read the first post - Part One
After showing the most basic in part 1, let’s see a real-life example of my finalized solution.
I created a sample application which looks as follows -
Let me go through this very quickly -
- The main view is bound to a MainViewModel.
- The employees list view at the left is bound an EmployeeListViewModel which is a member of the MainViewModel
- The employee details at the right is bound to the SelectedEmployee property of the EmployeeListViewModel
- The top Edit / Save buttons are bound to trigger commands on the MainViewModel
- The application is aware of user permission state that can be changed in runtime – if so, a CAL event is published.
Let’s see the Employee model’s class -
Code Snippet
- public class Employee : NotifyPropertyChangedBase
- {
- public string Name { get; set; }
-
- public EmployeeType Type { get; set; }
- }
For the sake of the example, let’s assume the following requirements should be met -
- The user can edit employees only if he has a write permission
- The user can edit an employee only if he’s an in-house employee (according to his type) regardless of the user permission
You can see that the commands which are controlled by the MainViewModel in this specific example is inherently bound to the state of the current selected employee as well as a permission context which is published through CAL events.
Additionally, we would obviously need to make our commands be state-aware of the current action. (Save should be enabled if edit is on, Edit should be disabled if edit mode is on, etc..)
At this point, you would normally go and put all that logic into your ViewModel, simply rubbish it with all of these and have the related code all scattered around.
Let’s look at my final solution and walk through it:
Code Snippet
- public class EmployeeViewModel : ViewModelBase
- {
- private Employee _employee;
-
- public bool IsEditMode { get; set; }
-
- public string Name { get; set; }
-
- public EmployeeType Type { get; set; }
-
- public bool IsEditMode { get; set; }
-
- public bool CanUpdate
- {
- get { return _employee.Type == EmployeeType.InHouse; }
- }
-
- static EmployeeViewModel()
- {
- //A place to define dependent properties
- //When 'Type' is notified as changed, so will 'CanUpdate'
- //This solution supports refactoring!
- AddPropertyDependency((EmployeeViewModel o) => o.CanUpdate)
- .DependsOn(o => o.Type);
- }
- }
-
- public class MainViewModel : ViewModelBase
- {
- IEventAggregator _eventAggregator;
-
- #region Commands
- static SourcePathDescription SelectedItemPathDescription = SourcePathDescription.Create((MainViewModel o) => o.EmployeesViewModel.SelectedEmployee);
- static SourceMembersDescription CanUpdateDescription = SourceMembersDescription.Create<EmployeeViewModel>(o => o.CanUpdate, o => o.IsEditMode);
-
- private void UpdateCommands()
- {
- RegisterCommand(DelegateCommandEx.Create("EditCommand", () => EmployeesViewModel.SelectedEmployee.IsEditMode = true)
- .WithObjectNotifiers(
- ObjectChangedNotifier<EmployeeViewModel>.FromPath(this, SelectedItemPathDescription, CanUpdateDescription)
- .CanExecute(false, o => o.CanUpdate && !o.IsEditMode)
- .Create())
- .WithNotifiers(new CalEventNotifier<PermissionChangedEvent, PermissionType>(
- _eventAggregator, p => p == PermissionType.Write)));
-
- RegisterCommand(DelegateCommandEx.Create("SaveCommand", () => EmployeesViewModel.SelectedEmployee.IsEditMode = false)
- .WithObjectNotifiers(
- ObjectChangedNotifier<EmployeeViewModel>.FromPath(this, SelectedItemPathDescription, CanUpdateDescription)
- .CanExecute(false, o => o.CanUpdate && o.IsEditMode)
- .Create())
- .WithNotifiers(new CalEventNotifier<PermissionChangedEvent, PermissionType>(
- _eventAggregator, p => p == PermissionType.Write)));
- }
- #endregion
- }
Some code was minimized for brevity (automatic properties instead of full ones for example)
As you can see, I created factory methods to make the creation of commands in a fluent interface to make the code more readable.
Let’s summarize things -
- General
- I created factory methods to make the creation of commands in a fluent interface to make the code more readable
- I could implement the INotifyCanExecuteChanged I created for the specific need of these commands, but I was aiming for a more generic reusable components
- We can see two generic components I implemented
- ObjectChangedNotifier
- The notifier is bound to a certain object path or the actual source instance (another overload)
- It supports full property paths – you can see that the path to the source used here goes through two properties – o.EmployeesViewModel.SelectedEmployee.
It binds to the whole property path, if the EmployeesViewModel will be changed on the MainViewModel – the change will be notified! - The container description holds the path to the source the command is bound to
- The source description holds the properties the command is bound to. Only when these properties are notified to be changed on the source the command will execute its CanExecuteChanged
- The description classes that represent the bound state are created statically and then used within the instance
This ensures best performance since we create the lambda expressions only once and reuse the description - The CanExecute attached the implementation of whether the state is valid or not. The first parameter sets the value for when the bound path is null or N/A, therefore, in the CanExecute implementation I don’t need to check whether it is null
- The internal implementation uses weak references and the PropertyChangedEventManager to ensure there won’t be any memory leaks
- CalEventNotifier
- The notifier is bound to a certain CAL event that may be published
- Extra – you can see in the EmployeeViewModel the property dependency declaration – another sweet addition.
- Edit Command
- The edit command should be executable when the following is met -
- The employee has ‘CanUpdate’ of ‘true’
- The employee isn’t in edit mode already
- The above two are covered using the ObjectNofier
- The user permission is ‘Write’
- This is covered using the CalEventNotifier since changes in the permissions are done through the CAL’s IEventAggregator.
- Save Command
- Pretty much the same as the edit command. The only change is that it should be enabled when it IS in edit mode.
In Conclusion - What have I accomplished here?
The commands include the logic they need in order to determine fully if it can be executed or not. No other code in any other place is needed!
If more specific implementation is needed, you can always implement the INotifyCanExecute with your own implementation and attach it to your commands.
The code is more encapsulated, changes to the command state are done in one place, no need to tamper with the state of your ViewModel.
Finally, it makes the code more centralized, more readable and with a better maintainable approach.
Update 29/07: You can download the updated source here – Zuker.WpfSamples.zip
Last Note
Currently, there is no cancel operation and when the user edits the employee’s name it is already committed before clicking on ‘Save’.
Actually, saving just removes the edit mode.
I will address this in another post in the near future, so stay tuned.
Well, it has certainly been a while since I got to a good posting rhythm, I hope it changes soon because I have a series of posts in mind.
This isn’t a post about general help and description for PRISM, MVVM, or DelegateCommand. There are many resources about these already.
This post is to demonstrate how I managed to extend the DelegateCommand with some really cool features.
The DelegateCommand that ships with CAL expects an “execute method” and a “can execute method” arguments in its constructor.
This is nice, but I wanted something more.
I am involved in a big WPF development project where we have many commands that their state depends on several state-related parameters. I could just “push” it all in the “can execute” method, however, I don’t quite like it due to the following:
- The code is written very imperatively
- You need to reserve fields for every command to trigger its “CanExecuteChanged” when the appropriate state changes
- No encapsulation, the code is scattered around, code gets repeated
- Difficulty to maintain – you need to search for all related places when the requirement changes and synchronize everything appropriately.
- Plain and simple - not pretty
The goal is to make the Delegate Command to be attachable with numerous state bound objects that can alert it when it needs to be refreshed.
Starting Point – Attachable Components
I needed to make a common interface for the attachable components that could be attached to the DelegateCommand, as follows -
Code Snippet
- public interface INotifyCanExecuteChanged
- {
- event EventHandler CanExecuteChanged;
-
- bool CanExecute();
- }
To make things a bit more easy for future classes, I created also a base class -
Code Snippet
- public abstract class NotifyCanExecuteChangedBase : INotifyCanExecuteChanged
- {
- public event EventHandler CanExecuteChanged;
- protected virtual void OnCanExecuteChanged()
- {
- var evt = CanExecuteChanged;
- if (evt != null)
- {
- evt(this, EventArgs.Empty);
- }
- }
-
- public abstract bool CanExecute();
- }
Step 2 – Enable INotifyCanExecuteChanged attachments to the DelegateCommand
Code Snippet
- public abstract class BaseDelegateCommandEx<T> : ICommandEx
- {
- public BaseDelegateCommandEx(Action<T> executeMethod, params INotifyCanExecuteChanged[] notifiers)
- : this(executeMethod, (IEnumerable<INotifyCanExecuteChanged>)notifiers)
- { ... }
- }
At this point I got a delegate command that can get attachable notifiers. My command binds to the event of each notifier and raises its own CanExecuteChanged.
Additionally, there’s logic in the CanExecute to call the notifiers as well.
In Conclusion for this Part
This seals the first part of the Delegate Command Extensions.
The delegate command can get attachable notifiers which it bounds to and mange calls to them. This is the basic implementation that allows the developers to define a command and bind it to numerous state-bound notifiers.
Next Time..
I will show the continuation of the solution which turned our real nice, I recommend you stay tuned :)