2
Vote

Detach of Command Bindings

description

We use a lot of bindings to observable collections. I couldn't find a way to detach a Command binding which causes issues when views are recycled and those views get bound again with view models. Basically I causes the commands to fire on the previously attached viewmodels as well.

comments

stepple wrote Jan 18, 2016 at 12:53 PM

I have working code that can be used instead of the SetCommand extension method that will return an object with all the handlers that then can be used to detach. Here is the code

public static class BindingExtension
{
    public static CommandBinding SetCommandEx(
        this object element,
        string eventName,
        RelayCommand command)
    {
        var t = element.GetType();
        var e = t.GetEvent(eventName);

        if (e == null)
        {
            throw new ArgumentException("Event not found: " + eventName, "eventName");
        }

        EventHandler handler = (s, args) =>
        {
            if (command.CanExecute(null))
            {
                command.Execute(null);
            }
        };

        e.AddEventHandler(
            element,
            handler);

        var enabledProperty = t.GetProperty("Enabled");
        if (enabledProperty != null)
        {
            enabledProperty.SetValue(element, command.CanExecute(null));
        }

        EventHandler canExecuteHandler = (s, args) =>
        {
            if (enabledProperty != null)
            {
                enabledProperty.SetValue(element, command.CanExecute(null));
            }
        };

        command.CanExecuteChanged += canExecuteHandler;

        CommandBinding binding = new CommandBinding(element,eventName,handler,command, canExecuteHandler);
        return binding;
    }
}

public class CommandBinding
{
    private readonly object _element;
    private readonly string _eventName;
    private readonly EventHandler _handler;
    private readonly RelayCommand _command;
    private readonly EventHandler _canExecuteHandler;

    public CommandBinding(object element, string eventName,EventHandler handler, RelayCommand command, EventHandler canExecuteHandler)
    {
        _element = element;
        _eventName = eventName;
        _handler = handler;
        _command = command;
        _canExecuteHandler = canExecuteHandler;
    }

    public void Detach()
    {
        var t = _element.GetType();
        var e = t.GetEvent(_eventName);

        if (e == null)
        {
            throw new ArgumentException("Event not found: " + _eventName, "eventName");
        }
        e.RemoveEventHandler(_element, _handler);
        _command.CanExecuteChanged -= _canExecuteHandler;
    }
}

lbugnion wrote Apr 17, 2016 at 1:58 PM

Hey I just wanted to give some feedback and inform about the status. I initially wanted to take this feature in V5.3 which I will release at Evolve in less than 2 weeks. After starting to work on it, I realize that I need to write a few unit tests first to make sure that I don't introduce regressions. I am not sure if I will manage to do this before Evolve. As a consequence, I am moving this feature to V5.4. I already started working on it, so I am confident that I can release V5.4 (which will be a smaller update than V5.3) in a reasonable time.

I like what you are doing here, and I will release something very similar. I already improved the Binding framework to allow data binding between items and elements in lists, and this goes in the same direction exactly. I am also taking this feature in the "Next Steps" slide of my Evolve presentation.

Thanks!
Laurent

stepple wrote Apr 17, 2016 at 11:36 PM

That's awesome. Thanks for the update.
Steffen

TheAlmightyBob wrote Feb 28 at 1:49 AM

Any news on this issue or V5.4? Is the work that was started included in V5.4-alpha?