Home > Software design >  Why can't I implement one of my Wpf behaviors as an EventTrigger?
Why can't I implement one of my Wpf behaviors as an EventTrigger?

Time:09-20

I have the following Xaml where I'm using a behavior to activate my Login window:

<Window ...
 xmlns:behaviors="clr-namespace:"..."
 xmlns:interactivity="http://schemas.microsoft.com/xaml/behaviors"
 .
 .>
    <interactivity:Interaction.Behaviors>
        <behaviors:ActivateBehavior Activated="{Binding Activated, Mode=TwoWay}"/>
    </interactivity:Interaction.Behaviors>
 .
 .
 </Window>

together with the corresponding Behavior that reacts on the OnActivated event for a Window:

public class ActivateBehavior : Behavior<Window>
{
.
. (here goes some other code like the DP Activated)
.

protected override void OnAttached()
{
    AssociatedObject.Activated  = OnActivated;
    AssociatedObject.Deactivated  = OnDeactivated;
}

protected override void OnDetaching()
{
    AssociatedObject.Activated -= OnActivated;
    AssociatedObject.Deactivated -= OnDeactivated;
}

void OnActivated(object sender, EventArgs eventArgs)
{
    _isActivated = true;
    Activated = true;

    if (string.IsNullOrEmpty(App.UserId))
    {
        LoginView loginView = new LoginView();
        loginView.ShowDialog();
    }
}

void OnDeactivated(object sender, EventArgs eventArgs)
{
    _isActivated = false;
    Activated = false;
}

}

You can implement this in code-behind by using the following code:

    protected override void OnActivated(EventArgs e)
    {
        base.OnActivated(e);

        if (string.IsNullOrEmpty(App.UserId))
        {
            LoginView loginView = new LoginView();
            loginView.ShowDialog();
        }
    }

but since I'm only working with MVVM this is not an option. Now, my qustion is why this cannot be implemented with an EventTrigger instead, i.e., using the following code in my xaml:

<Window ...
 xmlns:behaviors="clr-namespace:"..."
 xmlns:interactivity="http://schemas.microsoft.com/xaml/behaviors"
 .
 .>
    <interactivity:Interaction.Triggers>
        <interactivity:EventTrigger EventName="Activated">
            <interactivity:InvokeCommandAction Command="{Binding OnActivatedCommand}" />
        </interactivity:EventTrigger>
    </interactivity:Interaction.Triggers>
 .
 .
 </Window>

with the following command in my NotesViewModel.cs:

public RelayCommand OnActivatedCommand { get; set; }

and in my NotesViewModel constructor:

        OnActivatedCommand = new RelayCommand(o =>
        {
            if (string.IsNullOrEmpty(App.UserId))
            {
                LoginView loginView = new LoginView();
                loginView.ShowDialog();
            }
        });

With this implementation the command is never hit which means the EventTrigger "Activated" is never hit.

I know there's another discussion whether you should reference another view in the ViewModel but that's not what I'm out for here, I just want to know why I can't use Interaction.Triggers and EventTrigger to fire the event Activated instead of using Interaction.Behaviors (which nevertheless should be the MVVM Purist way I would say)?

Thanks.

CodePudding user response:

I have also observed that Microsoft.Xaml.Behaviors.EventTrigger with Window.Activated event fails to fire when a Window opens for the first time but succeeds when the Window is activated for the second time and after.

The test code is quite plain.

<Window x:Class="WpfInteractionTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:local="clr-namespace:WpfInteractionTest"
        Width="600" Height="400">

    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Activated">
            <i:InvokeCommandAction Command="{Binding OnActivatedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

</Window>

Then, I have found that it succeeds to fire even for the first time in the following code.

<Window x:Class="WpfInteractionTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:local="clr-namespace:WpfInteractionTest"
        Width="600" Height="400">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Activated" SourceObject="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
            <i:InvokeCommandAction Command="{Binding OnActivatedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>

</Window>

I have no clear idea but guess the order in initialization process of Window affects this issue. Anyways, Window.Loaded event seems to work well and so it would be an alternative as suggested.

Test project

CodePudding user response:

With this implementation the command is never hit which means the EventTrigger "Activated" is never hit.

It should work assuming your DataContext is set up correctly and the binding works. Did you confirm that the window was actually activated? Try to mimimize and restore it.

Or maybe you want to handle the Loaded event instead of, or in combination with, Activated.

  • Related