microsoft / XamlBehaviorsWpf

Home for WPF XAML Behaviors on GitHub.
MIT License
852 stars 140 forks source link

InvokeCommandAction for IsVisibleChanged Command not work #137

Closed CodingOctocat closed 1 year ago

CodingOctocat commented 1 year ago

I use MVVM Toolkit and XamlBehaviorsWpf, They worked fine until I started using the IsVisibleChanged event, and I found that the event handler UserControl_IsVisibleChanged was called correctly (no doubt), but the FooCommand was not called.

To add, this should not be a problem with my code, as other event commands can be called (e.g. Loaded)

View:

<UserControl IsVisibleChanged="UserControl_IsVisibleChanged">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="IsVisibleChanged">
            <i:InvokeCommandAction Command="{Binding FooCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</<UserControl>

Vm:

[RelayCommand]
private void Foo()
{
    // not called.
}

Code-Behind:

private void UserControl_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
    // normal called.
}

Sample:

Issue137Sample.zip

brianlagunas commented 1 year ago

As stated in the bug issue template:

"Please upload or provide a link to a reproduction case. If no reproduction sample is included, this issue may be closed or ignored until a sample has been provided".

CodingOctocat commented 1 year ago

@brianlagunas Sorry, I forgot about the issue template requirement, now I added the sample.

Issue137Sample.zip

image

brianlagunas commented 1 year ago

Thanks for the sample. The reason this is happening is because the IsVisibileChanged event is not the correct event type for the built-in EventTrigger.

The IsVisibleChnaged event is actually a DependencyPropertyChangedEventHandler delegate, and does not have an EventArgs object, but rather a struct of type DependencyPropertyChangedEventArgs.

If you want to support this type of event, you'll need to provide a custom implementation of the event trigger. Here is an example of a very simple and incomplete approach you may take.

public class DependencyPropertyChangedEventTrigger : Microsoft.Xaml.Behaviors.EventTrigger
{
    protected override void OnSourceChanged(object oldSource, object newSource)
    {
        RegisterEvent(newSource, GetEventName());
    }

    private void RegisterEvent(object obj, string eventName)
    {
        Type targetType = obj.GetType();
        EventInfo eventInfo = targetType.GetEvent(eventName);
        var eventHandlerMethodInfo = typeof(DependencyPropertyChangedEventTrigger).GetMethod("OnEventImpl", BindingFlags.NonPublic | BindingFlags.Instance);
        eventInfo.AddEventHandler(obj, Delegate.CreateDelegate(eventInfo.EventHandlerType, this, eventHandlerMethodInfo));
    }

    private void OnEventImpl(object sender, DependencyPropertyChangedEventArgs eventArgs)
    {
        //todo: handle your event args and pass them as a proper EventArgs based class
        this.OnEvent(new EventArgs());
    }
}
<local:DependencyPropertyChangedEventTrigger EventName="IsVisibleChanged">
     <i:InvokeCommandAction Command="{Binding BorderIsVisibleChangedCommand}" />
</local:DependencyPropertyChangedEventTrigger>

This should get you pointed in the right direction.