WPF Adorner出现在鼠标悬停动画上 - 最好的模式是什么?

时间:2012-12-12 21:52:48

标签: wpf adorner

Adorner 是在图片部分上定义的。所需的行为如下:

  1. 当鼠标位于图像区域上方(包括Adorner区域)时,会出现Adorner。
  2. 当鼠标离开图像和Adorner区域时,Adorner消失。
  3. Adorner出现和消失是相应的淡入/淡出动画。
  4. 点击Adorner区域必须引发事件AdornerClicked
  5. 单击图像上方未被装饰器隐藏的区域,必须使用Rais ImageClicked。
  6. 一个天真的实现

    在Image的MouseEnter和MouseLeave事件上附加Adorner不透明度上的动画,并为每个事件附加Click事件。然而,当鼠标直接在其上方时(因为在下面的图像上触发了MouseLeave),这会导致Adorner消失,违反了要求编号1.

    对天真实现的可能修改是在Adorner上设置IsHitTestVisible = false。但是,Adorner不会捕获任何点击次数,违反了第4项要求。

    满足要求的正确模式是什么?

1 个答案:

答案 0 :(得分:0)

有点老问题,但我遇到了同样的问题,找不到答案,所以这就是我想出来的。

所以问题是控件及其装饰器是重叠的,并且将装饰器设置为可见触发装饰控件上的MouseLeave,因为它现在由装饰器覆盖。

解决方案是对装饰的控件及其装饰器上的每个MouseEnter和MouseLeave做出反应,并手动执行命中测试。如果其中任何一个被击中,那么装饰者应该是可见的,否则就会崩溃。

因此,您需要能够从装饰控件中获取装饰,反之亦然。从装饰器获取装饰控件是没有问题的(使用AdornedElement属性)但框架(AFAIK)不提供控件的装饰器,因此我使用将控件映射到其装饰器列表的字典。

这是我的Panel派生类中的代码(包含并安排我的控件及其装饰者):

private readonly Dictionary<Control, List<Adorner>> _controlToAdornersMap;

...

private void CreateMyControl()
{
    var control = new MyControl();
    control.MouseEnter += OnMyControlMouseEnterOrLeave;
    control.MouseLeave += OnMyControlMouseEnterOrLeave;
    Children.Add(control);
    AddAdorners(control);
}

private void AddAdorners(Control control)
{
    var myAdorner = new MyAdorner(control);
    myAdorner.MouseEnter += OnMyAdornerMouseEnterOrLeave;
    myAdorner.MouseLeave += OnMyAdornerMouseEnterOrLeave;

    var adornerLayer = AdornerLayer.GetAdornerLayer(control);
    adornerLayer.Add(myAdorner);

    _controlToAdornersMap[control] = new List<Adorner> {myAdorner};
}

private void OnMyControlMouseEnterOrLeave(object sender, MouseEventArgs e)
{
    HitTestAndSetAdornersVisibility((MyControl)sender, e);
}

private void OnMyAdornerMouseEnterOrLeave(object sender, MouseEventArgs e)
{
    var adorner = (Adorner)sender;
    HitTestAndSetAdornersVisibility((MyControl)adorner.AdornedElement, e);
}

private void HitTestAndSetAdornersVisibility(MyControl control, MouseEventArgs e)
{
    var adorners = _controlToAdornersMap[control];
    var hitTestSubjects = new List<UIElement> { control }.Concat(adorners);
    var hit = hitTestSubjects.Any(i => VisualTreeHelper.HitTest(i, e.GetPosition(i)) != null);

    SetAdornersVisibility(adorners, hit ? Visibility.Visible : Visibility.Collapsed);
}

private static void SetAdornersVisibility(IEnumerable<Adorner> adorners, Visibility visibility)
{
    if (adorners != null)
        foreach (var adorner in adorners)
            adorner.Visibility = visibility;
}