Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Block MouseLeave trigger if object is disposed of

I have a grid which, when hovered over with the mouse, makes one of its child elements come to life (opacity from 0 to 1) but that grid also can be disposed of (that grid is part of a listbox that can be remove via a close button on the grid).

When the user clicks the remove button it also launches the MouseLeave event below which of course can not find my DockStackPanel control anymore since it was disposed. How can I fix this issue?

     <Grid.Triggers>
         <EventTrigger RoutedEvent="UIElement.MouseEnter">
             <BeginStoryboard>
                 <Storyboard>
                     <DoubleAnimation Duration="0:0:0.5" Storyboard.TargetName="DockStackPanel" Storyboard.TargetProperty="Opacity" To="1" />
                 </Storyboard>
             </BeginStoryboard>
         </EventTrigger>
         <EventTrigger RoutedEvent="UIElement.MouseLeave">
             <BeginStoryboard>
                 <Storyboard>
                     <DoubleAnimation Duration="0:0:0.5" Storyboard.TargetName="DockStackPanel" Storyboard.TargetProperty="Opacity" To="0" />
                 </Storyboard>
             </BeginStoryboard>
         </EventTrigger>
     </Grid.Triggers>
like image 920
touyets Avatar asked Oct 20 '22 22:10

touyets


2 Answers

Have you tried creating the animation in code instead. I'm assuming here that the DockStackPanel control is a StackPanel.

public MainWindow()
    {
        InitializeComponent();

        MyGrid.MouseEnter += MyGrid_MouseEnter;
        MyGrid.MouseLeave += MyGrid_MouseLeave;
    }

    void MyGrid_MouseLeave(object sender, MouseEventArgs e)
    {
        if (DockStackPanel != null)
        {
            var dur = new Duration(new TimeSpan(0, 0, 0, 0, 500));
            var anim = new DoubleAnimation(0, dur);
            DockStackPanel.BeginAnimation(StackPanel.OpacityProperty, anim);
        }
    }

    void MyGrid_MouseEnter(object sender, MouseEventArgs e)
    {
        var dur = new Duration(new TimeSpan(0, 0, 0, 0, 500));
        var anim = new DoubleAnimation(1, dur);
        DockStackPanel.BeginAnimation(StackPanel.OpacityProperty, anim);
    }
like image 131
snacky Avatar answered Oct 30 '22 00:10

snacky


Another thing that you can do is implement DataTrigger for Grid which will override default MouseLeave and MouseEnter events to do nothing when DisableTriggers property changed.

 <Style TargetType="{x:Type Grid}">
   <Style.Triggers>
        <DataTrigger Binding="{Binding DisableTriggers}" Value="True">
          <Setter Property="Style" Value="{StaticResource GridStyleWithoutStoryboards}" />
        </DataTrigger>
   </Style.Triggers>
 </Style>

This is the case when you don't want to use AttachedBehaviors. Otherwise I suggest to handle storyboards cleanup in behavior. It is much easier:

void OnDisableTriggersPropertyChanged( object sender, EventArgs args )
{
    // If IsDisposed property was changed and it is true now - cleanup triggers.
    if ((bool)args.NewValue)
    {
       var grid = (Grid)sender;

       // Ideally you can remove specific triggers. 
       // Clear all will work for simple cases.
       grid.Triggers.Clear();
    }
}

So summing up you will have to add attached behavior with DisableTriggers dependency property which performs cleanup action in OnChanged handler.

If you want to unsubscribe only from specific events:

    foreach (var eventToUnsubscribe in grid.Triggers.OfType<EventTrigger>()
                                                  .Where(x => x.RoutedEvent == UIElement.MouseEnterEvent
                                                              || x.RoutedEvent == UIElement.MouseLeaveEvent).ToList())
    {
        grid.Triggers.Remove(eventToUnsubscribe);
    };
like image 35
Anatolii Gabuza Avatar answered Oct 29 '22 23:10

Anatolii Gabuza