Triggering "CanExecute" on postsharp [Command] when a document changes?
Asked Answered
S

2

7

I am currently migrating a project to PostSharp to remove a lot of boilerplate code, most of it is going very smoothly but I'm left confused about how to force a command to recheck if it CanExecute. I expected PostSharp would inspect the command like it does properties to check for dependencies, here is a minimalist sample

[NotifyPropertyChanged]
public class MyWindowViewModel
{
    /// Anything bound to this refreshes just fine as expected
    public ObservableCollection<SomeType> Documents = new ObservableCollection<SomeType>();

   [Command]
   public ICommand AddDocumentCommand { get; set; }
   public void ExecuteAddDocument () { Documents.Add(new SomeType()); }

   [Command]
   public ICommand CloseDocumentCommand { get; set; }
   public bool CanExecuteCloseDocument () => Documents.Any(); 
   public void ExecuteCloseDocument () { Documents.Remove(Documents.Last()); }        
}

At start the collection is empty and the button attached to the close command is greyed as expected, however adding a document through the button attached to AddDocument doesn't activate the close document button, what is the appropriate way to accomplish what I need? Is PostSharp only considering assignments and not method calls as changes or is it something else entirely?

Stanza answered 11/7, 2017 at 5:19 Comment(0)
F
8

According to their Command documentation

CanExecuteCloseDocument should be a property

public bool CanExecuteCloseDocument => Documents.Any();

The method option is used when the command requires parameters,

The command availability check that depends on the input argument can be implemented as a method.

for example

public bool CanExecuteCloseDocument (int blah) => Documents.Any(); 
public void ExecuteCloseDocument (int blah) { Documents.Remove(Documents.Last()); }

That aside the main issue here is that the view is unaware of the changes to the collection to know to refresh property changes.

Refer to this http://www.postsharp.net/blog/post/Announcing-PostSharp-42-RC

Dependencies to collections

When you add the [AggregateAllChanges] attribute to a field or automatic property, any change to a property of the object assigned to this field/property will be interpreted as a change to the field/property itself. The attribute now works only for collections.

[NotifyPropertyChanged]
public class MyWindowViewModel {

    /// Anything bound to this refreshes just fine as expected
    [AggregateAllChanges] // <-- when the collection changes to cause notification
    public ObservableCollection<SomeType> Documents { get; } = new ObservableCollection<SomeType>();

   [Command]
   public ICommand AddDocumentCommand { get; set; }
   public void ExecuteAddDocument () { Documents.Add(new SomeType()); }

   [Command]
   public ICommand CloseDocumentCommand { get; set; }
   public bool CanExecuteCloseDocument => Documents.Any(); 
   public void ExecuteCloseDocument () { Documents.Remove(Documents.Last()); }        
}
Fervidor answered 13/7, 2017 at 11:14 Comment(3)
This is perfect and solved all issues, you are correct both were required. I'm running postsharp 5 that just released and the command documentation is pretty empty but that did the trick and was clearly explained, thank youStanza
Referring to this post with a similar question I got this answer from PostSharp support: "[AggregateAllChanges] does not work on collections due to internal limitations of NPC aspect, we are aware of this problem. Raising command's CanExecuteChanged event is not currently possible... I don't see in any other way than using reflection (ICommand implementation object has OnCanExecute(object) method) to raise the event. I'm sorry for the inconvenience"Norway
[AggregateAllChanges] is now implemented in PostSharp 6.10Norway
N
0

With PostSharp LINQ expression (or virtual calls, delegates, external methods) wouldn't work well for CanExecute's.

But expression on properties that implement INotifyPropertyChanged work fantastic (even for nested properties). ObservableCollection implements INotifyPropertyChanged, we don't need LINQ:

public bool CanExecuteCloseDocument => Documents.Count > 0;

Norway answered 15/12, 2021 at 8:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.