我的目标:我有Button
和Image
。默认情况下,Image
为Hidden
,一旦用户将鼠标悬停在Button
上,就会显示Image
。在用户将鼠标悬停在Image
或Button
上之前,它应该是可见的。它应该在用户离开鼠标点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>
有没有人有更好的想法以有效的方式做到这一点。
感谢。
答案 0 :(得分:2)
正如Chris W.所暗示的那样,最好的做法是使用Storyboard
和EventTrigger
。这正是他们设计的场景。以下是它如何与您的示例一起工作(我将图像更改为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_MouseEnter
和Element_MouseLeave
处理程序。