使用依赖项属性在n秒后隐藏元素的可见性

时间:2016-09-16 11:10:31

标签: c# wpf xaml c#-4.0 attached-properties

我的目标:我有ButtonImage。默认情况下,ImageHidden,一旦用户将鼠标悬停在Button上,就会显示Image。在用户将鼠标悬停在ImageButton上之前,它应该是可见的。它应该在用户离开鼠标点6秒后(从按钮或图像)隐藏图像。鼠标在6秒前再次悬停并离开应该重新启动计时器。

我尝试了什么我已经使用AttachedProperty提供了可行的解决方案,但效率不高。我感觉因为static这里会有内存泄漏。

public class MouseHoverBehavior
{
    public static readonly DependencyProperty ElementProperty = DependencyProperty.RegisterAttached(
        "Element", typeof(UIElement), typeof(MouseHoverBehavior), new UIPropertyMetadata(OnElementChanged));

    private static UIElement target;

    static MouseHoverBehavior()
    {
        timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromSeconds(6);
        timer.Tick += Timer_Tick;
    }

    private static void OnElementChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {            
        var element = (sender as Button);
        target = (UIElement)e.NewValue;
        target.Visibility = Visibility.Hidden;

        element.MouseEnter += Element_MouseEnter;
        target.MouseEnter += Element_MouseEnter;
        element.MouseLeave += Element_MouseLeave;
        target.MouseLeave += Element_MouseLeave;
    }

    private static DispatcherTimer timer;

    private static void Element_MouseLeave(object sender, MouseEventArgs e)
    { 
        timer.Start();
    }

    private static void Timer_Tick(object sender, EventArgs e)
    {
        target.Visibility = Visibility.Hidden;
    }

    private static void Element_MouseEnter(object sender, MouseEventArgs e)
    {            
        timer.Stop();
        target.Visibility = Visibility.Visible;
    }

    public static void SetElement(DependencyObject element, UIElement value)
    {
        element.SetValue(ElementProperty, value);
    }

    public static string GetElement(DependencyObject element)
    {
        return (string)element.GetValue(ElementProperty);
    }
}

在xaml:

<StackPanel>
    <Image Source="steve.jpg" Width="200" x:Name="image"/>
    <Button Width="200" Height="100" Margin="20" local:MouseHoverBehavior.Element="{Binding ElementName=image}"/>
</StackPanel>

有没有人有更好的想法以有效的方式做到这一点。

感谢。

1 个答案:

答案 0 :(得分:2)

正如Chris W.所暗示的那样,最好的做法是使用StoryboardEventTrigger。这正是他们设计的场景。以下是它如何与您的示例一起工作(我将图像更改为Rectangle,以便我可以轻松地测试它):

   <StackPanel>
        <StackPanel.Triggers>
            <EventTrigger SourceName="_button" RoutedEvent="MouseEnter">
                <BeginStoryboard>
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="image" 
                                                       Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0"
                                                    Value="{x:Static Visibility.Visible}"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
            <EventTrigger SourceName="_button" RoutedEvent="MouseLeave">
                <BeginStoryboard>
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="image"                                                            
                                                       Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0:00:06" 
                                                    Value="{x:Static Visibility.Collapsed}" />
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
            <EventTrigger SourceName="image" RoutedEvent="MouseEnter">
                <BeginStoryboard>
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="image" 
                                                       Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0"
                                                    Value="{x:Static Visibility.Visible}"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
            <EventTrigger SourceName="image" RoutedEvent="MouseLeave">
                <BeginStoryboard>
                    <Storyboard Duration="1">
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="image"                                                            
                                                       Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0:00:06" 
                                                    Value="{x:Static Visibility.Collapsed}" />
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </StackPanel.Triggers>

        <Button x:Name="_button"
                Width="200" Height="100" Margin="20" />
        <Rectangle Visibility="Collapsed"
                   Width="200" 
                   Height="200"
                   Fill="Yellow"
                   x:Name="image"/>
    </StackPanel>

你也可以让图像在5秒之后开始淡出,完全在6之后等等。我知道它看起来像很多标记,但是它非常灵活,并且避免了你要经历的很多编码逻辑问题。你想做更复杂的事情。 (事实上​​,如果你想要更复杂的东西,你无论如何都必须使用Storyboard,并且在代码中执行它们并不比XAML特别漂亮。)

但是如果您因某些原因不想使用故事板动画,那么您所做的事情对于您的特定场景似乎也很好。如果你担心内存泄漏(我不知道这些XAML元素会在应用程序的生命周期内被创建和销毁多少次,但除非它非常多,否则我不担心这个;最重要的卸载FrameworkElement时即释放资源,即使FE本身没有得到GC,也可以在OnElementChanged订阅这些元素'Unloaded事件时以及何时处理这些活动会取消订阅Element_MouseEnterElement_MouseLeave处理程序。