Is Josh Smith's implementation of the RelayCommand flawed? - wpf

Consider the reference Josh Smith' article WPF Apps With The Model-View-ViewModel Design Pattern, specifically the example implementation of a RelayCommand (In Figure 3). (No need to read through the entire article for this question.)
In general, I think the implementation is excellent, but I have a question about the delegation of CanExecuteChanged subscriptions to the CommandManager's RequerySuggested event. The documentation for RequerySuggested states:
Since this event is static, it will
only hold onto the handler as a weak
reference. Objects that listen for
this event should keep a strong
reference to their event handler to
avoid it being garbage collected. This
can be accomplished by having a
private field and assigning the
handler as the value before or after
attaching to this event.
Yet the sample implementation of RelayCommand does not maintain any such to the subscribed handler:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Does this leak the weak reference up to the RelayCommand's client, requiring that the user of the RelayCommand understand the implementation of CanExecuteChanged and maintain a live reference themselves?
If so, does it make sense to, e.g., modify the implementation of RelayCommand to be something like the following to mitigate the potential premature GC of the CanExecuteChanged subscriber:
// This event never actually fires. It's purely lifetime mgm't.
private event EventHandler canExecChangedRef;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
this.canExecChangedRef += value;
}
remove
{
this.canExecChangedRef -= value;
CommandManager.RequerySuggested -= value;
}
}

I've found the answer in Josh's comment on his "Understanding Routed Commands" article:
[...] you have to use the WeakEvent pattern in your CanExecuteChanged
event. This is because visual elements will hook that event, and since
the command object might never be garbage collected until the app
shuts down, there is a very real potential for a memory leak. [...]
The argument seems to be that CanExecuteChanged implementors must only hold weakly to the registered handlers, since WPF Visuals are to stupid to unhook themselves. This is most easily implemented by delegating to the CommandManager, who already does this. Presumably for the same reason.

I too believe this implementation is flawed, because it definitely leaks the weak reference to the event handler. This is something actually very bad.
I am using the MVVM Light toolkit and the RelayCommand implemented therein and it is implemented just as in the article.
The following code will never invoke OnCanExecuteEditChanged:
private static void OnCommandEditChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var #this = d as MyViewBase;
if (#this == null)
{
return;
}
var oldCommand = e.OldValue as ICommand;
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= #this.OnCanExecuteEditChanged;
}
var newCommand = e.NewValue as ICommand;
if (newCommand != null)
{
newCommand.CanExecuteChanged += #this.OnCanExecuteEditChanged;
}
}
However, if I change it like this, it will work:
private static EventHandler _eventHandler;
private static void OnCommandEditChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var #this = d as MyViewBase;
if (#this == null)
{
return;
}
if (_eventHandler == null)
_eventHandler = new EventHandler(#this.OnCanExecuteEditChanged);
var oldCommand = e.OldValue as ICommand;
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= _eventHandler;
}
var newCommand = e.NewValue as ICommand;
if (newCommand != null)
{
newCommand.CanExecuteChanged += _eventHandler;
}
}
The only difference? Just as indicated in the documentation of CommandManager.RequerySuggested I am saving the event handler in a field.

Well, according to Reflector it's implemented the same way in the RoutedCommand class, so I guess it must be OK... unless someone in the WPF team made a mistake ;)

I believe it is flawed.
By rerouting the events to the CommandManager, you do get the following behavior
This ensures that the WPF commanding
infrastructure asks all RelayCommand
objects if they can execute whenever
it asks the built-in commands.
However, what happens when you wish to inform all controls bound to a single command to re-evaluate CanExecute status? In his implementation, you must go to the CommandManager, meaning
Every single command binding in your application is reevaluated
That includes all the ones that don't matter a hill of beans, the ones where evaluating CanExecute has side effects (such as database access or long running tasks), the ones that are waiting to be collected... Its like using a sledgehammer to drive a friggen nail.
You have to seriously consider the ramifications of doing this.

I may be missing the point here but doesn't the following constitute the strong reference to the event handler in the contructor?
_canExecute = canExecute;

Related

Does inheritance matter with events and delegates?

I've been reading up on delegates, event and WPF for some days now and I am starting to get some understanding of it but there are a few things that is unclear to me.
In this question they explained on how to raise an event on a property is changed.
From what I've understood when it comes to event is that you want to do something when they happend, and that you need to add an function to an event like so
Someclass.PropertyChanged += new PropertyEventHandler(somefunction)
public void somefunction(object sender, EventArgs e){ //Do some code}
But almost in every example, when they use INotifyPropertyChanged that is never used, but they somehow manage to activate the event PropertyChanged.
I can't really make sense of it.
Do you need to add function to a new eventhandler if you implement an interface with an already declared event?
No need to give it a handler. You implement PropertyChanged so some other code can handle the event. That other code might be yours, but in the case of INotifyPropertyChanged, it's usually the bindings in your views that'll subscribe to your PropertyChanged events.
You can declare an event without adding your own handler to it. You really ought to raise the event once you bothered declaring it, but you don't have to handle it. By raising it, I mean like this:
protected void OnPropertyChanged(String propName)
{
var handler = PropertyChanged;
// If nobody gave it a handler, it'll be null, so check for that.
if (handler != null)
{
// This is what we refer to when we say "raise the event": handler has
// references to at least one handler (because it's not null), and possibly
// dozens. This one "method call" here will magically call all of them.
handler(this, new PropertyChangedEventArgs(propName));
}
}
public String Name {
get { return _name; }
set {
if (_name != value) {
_name = value;
// Call this method to raise PropertyChanged
OnPropertyChanged("Name");
}
}
}
private String _name;
Declaring an event is just saying "In case anybody cares about this thing happening, here's an event that I'll raise when it happens."
Maybe you want to handle that event in some other part of your own code. In WPF, you implement INotifyPropertyChanged so when the user interface has bindings to the properties of an instance of your class, it'll get the notifications it needs.

C#: ObservableCollection - why virtual “CollectionChanged” event?

Why CollectionChanged event is virtual in ObservableCollection? We have virtual OnCollectionChanged method, which should be enough to override event call right?
I don't see any usage of this, and also virtual events are evil. ungainly usage of virtual events can bring a lot of logical issues, but however virtual events exists even in framework.
Is this just bad design or anyone use this in real-word?
We can debate about base classes and design, but here's a not direct/scholastic answer, but more of an example. I personally find it great that I could extend ObservableCollection and override OnCollectionChanged. ObservableCollection is very chatty, every time you add/remove items it bombards the UI thread with property changed messages and slows it down (in the datagrid, for example, every binding in it to be updated). So, as far as I know many people extend the ObservableCollection to suppress such notifications until they are done adding items. Just because WPF controls DataGrids/ListViews etc.. respond to CollectionChanged this works.
here's the usage, I refresh my data and instead of adding one item at a time, I populate a List then I reset the ObservableCollection with it just once which speeds up UI responsiveness enormously:
private void OnExecuteRefreshCompleted(IEnumerable<MyObject> result)
{
UiUtilities.OnUi(() => { _myObservableCollectionField.Reset(result, true);
});
here's my extended class:
public class ObservableCollectionExtended<T> : ObservableCollection<T>
{
private bool _suppressNotification;
//without virtual , I couldn't have done this override
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suppressNotification)
base.OnCollectionChanged(e);
}
public void Clear(bool suppressNotificationUntillComplete)
{
_suppressNotification = suppressNotificationUntillComplete;
Clear();
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void ClearItems(bool suppressNotificationUntillComplete)
{
_suppressNotification = suppressNotificationUntillComplete;
base.ClearItems();
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void AddRange(IEnumerable<T> list, bool suppressNotificationUntillComplete)
{
if (list == null)
throw new ArgumentNullException("list");
_suppressNotification = suppressNotificationUntillComplete;
foreach (T item in list)
Add(item);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// clears old items, and new ones
/// </summary>
/// <param name="list"></param>
/// <param name="suppressNotificationUntillComplete"></param>
public void Reset(IEnumerable<T> list, bool suppressNotificationUntillComplete)
{
if (list == null)
throw new ArgumentNullException("list");
_suppressNotification = suppressNotificationUntillComplete;
Clear();
foreach (T item in list)
Add(item);
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
I am afraid denis didn't answer the quesiton "Why CollectionChanged event is virtual?" but rather the question "Why OnCollectionChanged() method is virtual?". The first question is more properly answered by Jon. You may be interested in different handling of subscribers like I did some time ago.
Let's have two different scenarios:
First scenario:
I want to raise CollectionChanged event and be sure that any delegate that is invoked within this event (list of delegates), doesn't interrupt the invoking of the following delegates in case of an exception. In other words, an event is made of a list of delegates. If I have let's say 10 subscribers (delegates) and the 3rd delegate raises an exception, I may continue invoking the rest of delegates. Standard implementation interrupts invoking.
Second scenario:
I may want to let some subscribers be prioritized (they receive the event earlier) even if they do the subscription later than others. In "add" event I can move some specific subscribers lower or higher in my custom list of delegates which is later used to raise the event..

Double Click on a WPF ListView - how to fire a command and not use an event handler

I am a little confused on how to implement an event as a command in my particular situation. I want to honour MVVM, but don't get how in this case.
I have a WPF 'view' - viewCustomerSearch. This has some text boxes on it, and when the user clicks 'Search' the results are populated in ListView. viewCustomerSearch is bound to viewmodelCustomerSearch, and it works great.
viewCustomerSearch is hosted on viewCustomer.
I want to know have viewCustomerSearch expose a custom command - CustomerSelectedCommand - that is 'fired' whenever the ListView in viesCustomerSearch is double clicked, and then handled by the viewmodel behind viewCustomer (which is viewmodelCustomer). This seems the theoretical MVVM pattern implemented correctly.
I have broken down the main problem into three smaller problems, but hopefully you can see they are all components of the same challenge.
FIRST PROBLEM: in order to have viewCustomerSearch expose a custom command I seem to have to put this code in viewCustomerSearch - which seems to 'break' MVVM (no code in the view code behind).
public readonly DependencyProperty CustomerSelectedCommandProperty = DependencyProperty.Register("CustomerSelectedCommand", typeof(ICommand), typeof(viewCustomerSearch));
public ICommand CustomerSelectedCommand
{
get { return (ICommand)GetValue(CustomerSelectedCommandProperty); }
set { SetValue(CustomerSelectedCommandProperty, value); }
}
SECOND PROBLEM (and this is the one that is really getting to me): Best explained by showing what I would do which breaks MVVM. I would have an event handler in the view:
private void lstResults_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (CustomerSelectedCommand != null) CustomerSelectedCommand.Execute(((ViewModels.viewmodelCustomerSearchResult)this.lstResults.SelectedItem).CustomerId);
}
Well ... I know that you shouldn't put this event handler here; rather it should have a Command to handle it in the viewmodelCustomerSearch. The two problems here are
because the 'CustomerSelectedCommand' ICommand is implemented in
viewCustomerSearch, viewmodelCustomerSearch can't see it to fire it.
I cannot see how to bind the MouseDoubleClick event to a command, instead of an event handler in the view code behind. I am reading about Attached Properties, but cannot see how they are to be applied here.
(Please note: I am using the common 'RelayCommand' elsewhere in the application; does this come into play here??)
THIRD PROBLEM: When I do use the non-MVVM way of firing the command in the code behind event handler, you can see that I am passing in the Selected Customer Id as an arguement into the command. How do I see that argument in the Command handler in viewCustomer? I create a new RelayCommand to handle it, but it seems the Execute method does not take arguments?
Given all of the above, I have to say that I do NOT personally subscribe to the 'MVVM means NO CODE IN THE VIEW'. That seems crazy to me; code that is entirely to do with the view, and the view only, should not - IMHO - go in the viewmodel. That said, though, this does seem like logic-y stuff (not view stuff).
Many thanks for some insight. Sorry for the long post; trying to balance enough information for you to help me with 'War and Peace'.
DS
In your view you can add a "Command" property in xaml and bind it to your ViewModel's command
Command="{Binding CustomerSelectedCommand}"
Parameters can be passed in multiple ways. Most of the time, I just have other items bound to my ViewModel and I can just use them directly. However there is also a property called CommandParameter, here's an example of specifying it in XAML.
CommandParameter="{Binding ElementName=txtPassword}"
then in my ViewModel the definition of my Command looks like this
private void UserLogonCommandExecute(object parameter)
{
...
var password_box = parameter as PasswordBox;
...
}
It sounds like you already know how to set up a RelayCommand in your ViewModel so I won't go into that. I found How Do I: Build Data-driven WPF Application using the MVVM pattern helpful when I was getting started.
Per Comment Request Command Property Example
I'm just going to grab some working code, here's how you add a Command property to a button in XAML.
<Button Command="{Binding ConnectCommand}">
//Your button content and closing </Button> here
This assume you have set your DataContext to a ViewModel that has a Command called ConnectCommand. Here's an example for ConnectCommand. You'll need to replace the contents of ConnectCommandCanExecute and ConnectCommandExecute with whatever work you want done.
public ICommand ConnectCommand
{
get
{
if (_connectCommand == null)
{
_connectCommand = new RelayCommand(param => ConnectCommandExecute(),
param => ConnectCommandCanExecute);
}
return _connectCommand;
}
}
private bool ConnectCommandCanExecute
{
get { return !_instrumentModel.IsConnected; }
}
private void ConnectCommandExecute()
{
if (TcpSettingsChanged()) SaveTcpSettings();
_instrumentModel.Connect(_tcpData);
}
RelayClass
One part of making this simple is the RelayClass I have in one of my core library .dlls. I probably got this from one of the videos I watched. This can be cut and pasted in it's entirety, there is nothing here you need to customize, except you'll probably want to change the namespace this is in.
using System;
using System.Diagnostics;
using System.Windows.Input;
namespace Syncor.MvvmLib
{
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public RelayCommand(Action<object> execute)
: this(execute, (Predicate<object>) null)
{
this._execute = execute;
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
this._execute = execute;
this._canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
if (this._canExecute != null)
return this._canExecute(parameter);
else
return true;
}
public void Execute(object parameter)
{
this._execute(parameter);
}
}
}
Why don't you name it "DoubleClickCommand" that way you don't put business logic in your control. And then bind this command to your viewmodel, Like Tod explained.
Regarding your code behind, there is a pure xaml solution, to be more precise it involves attached behaviors, but does not need to override a WPF class(which i like to avoid), search for "fire command on event" for example this.
One final thing: Code Behind does NOT break MVVM in any way, i wonder where this myth came from. Code behind is perfectly fine! MVVM is to separate view and logic, not telling you where to put your code. Design principles should help, not hinder you.

Notification of Collection Update for IDataErrorInfo Validation

I would like to notify the binding system in WPF of a change in a collection's item so that a validation through IDataErrorInfo gets reevaluated whenever an item inside a collection changes.
I have a custom list type which implements INotifyCollectionChanged (and works properly). But somehow the validation logic is never called, because (or at least I am assuming) that this notification does not reach the right place. Is this scenario even possible? What have I missed?
[Edit]
So basically the "architecture" is the following:
MVVM base class implements IDataErrorInfo and you can register DataValidators with lambdas in the derived MVVM classes, such as:
RegisterDataValidator(() => People, () => (People.Count == 0) ? "At least one person must be specified" : null);
The indexer on the base class checks the registered validator and returns the returned by it.
I have a SmartBindingList<T> where T: INotifyPropertyChange which is basically a list that when items are added to it, registers the items' PropertyChangedEvent and reacts to these events by firing the CollectionChanged event on the class itself:
private void OnSubPropertyChanged (object sender, PropertyChangedEventArgs e)
{
if (sender is T1)
{
if (CollectionChanged != null)
{
NotifyCollectionChangedEventArgs eventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender);
CollectionChanged(this, eventArgs);
}
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(myPropertyName));
}
}
}
So all this works nicely, but when the code runs on the CollectionChanged(this, eventArgs) line, nothing happens in terms of validation. It should be wired up correctly, because when I add something to the collection, it works perfectly. What am I missing?
This is a bit of stab in the dark without some example code, but try raising your OnPropertyChanged notification for the properties that have changed. This should cause validation to be re-evaluated.

is it correct to use OnPropertyChanged event to ask application to do something?

My MVVM application contains two views:
AllStrategiesView
StrategyView
When user click certain strategy in AllStrategiesView StrategyView with this strategy is created. I use such code to notify application that StrategyView should be created:
.............
public void OpenStrategyView()
{
OnPropertyChanged("OpenStrategy");
}
.................
private void OnWorkspacePropertyChanged(object sender, PropertyChangedEventArgs e)
{
const string openStrategyString = "OpenStrategy";
if (e.PropertyName == openStrategyString)
{
AllStrategiesViewModel vm = (sender as AllStrategiesViewModel);
OpenStrategy(vm.SelectedStrategy);
}
}
However another part of the program shows error message because there are no such property "OpenStrategy":
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
The question is:
Is it right or wrong to use OnPropertyChanged to notify application that something need to be done? Should I rewrite my code to not to use OnPropertyChanged or should I disable VerifyPropertyName code?
This is bad practice. The PropertyChanged event on INotifyPropertyChanged should be used to notify subscribers that a property on the object instance has changed. This is typically used in WPF to notify the UI that it needs to update itself with the new property value.
In MVVM, you should use some kind of commanding or alternative viewmodel/view communication mechanism to invoke verbs (methods) on your view model from the view. The commanding provided by WPF has limitations, so I would recommend using an MVVM framework and the mechanisms that they provide.
Well it depends on what you want it to do. In your case it looks like you have a property "Workspace" which indicates which VM you should be looking at. This doesn't seem too bad of a usage IMHO.
If you were doing something completely unrelated to the property that was changed then it might work, but it's certainly not what I'd expect it to do (see Principle of Least Astonishment). OnPropertyChanged is intended to indicate that a property that has been bound to has changed and should be re-fetched.
You can of course just have another event on your ViewModel, like:
public event Action<String> OpenStrategy;
One more thing... This code is completely redundant:
const string openStrategyString = "OpenStrategy";
if (e.PropertyName == openStrategyString)
the following is exactly the same, from the compiler's perspective, and much more readable:
if (e.PropertyName == "OpenStrategy")
There's nothing wrong in asking your application to do something in the PropertyChanged event, however do not raise a PropertyChanged event just to ask the application to do something.
PropertyChanged is used to indicate that a property has changed, and should be used for that only.
Devdigital's answer gives a good example, that the UI uses the PropertyChange notification to know when it should update. Other objects can also subscribe to receive change notifications, and they should only be notified when a value changes, not when you want to run some application code.
Using your example, I would rewrite it like this:
public void OpenStrategyView()
{
OpenStrategy(this.SelectedStrategy);
}
private void OnWorkspacePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedStrategy")
{
OpenStrategyView();
}
}

Resources