這個簡單的例子; 圖片由兩個重疊的元素A和B構成的維恩圖。 如果我將鼠標懸停在(A AND(NOT B))上,則A點亮。 如果我將鼠標懸停在(B AND(NOT A))上,所有的B都點亮。 如果我將鼠標放在(A和B)上,兩者應該點亮。只有最上面的標記爲鼠標懸停在上面。我可以讓WPF爲IsMouseOver覆蓋和覆蓋元素嗎?
有沒有辦法讓IsMouseOver像這樣隧道? 如果沒有,有什麼建議嗎?
這個簡單的例子; 圖片由兩個重疊的元素A和B構成的維恩圖。 如果我將鼠標懸停在(A AND(NOT B))上,則A點亮。 如果我將鼠標懸停在(B AND(NOT A))上,所有的B都點亮。 如果我將鼠標放在(A和B)上,兩者應該點亮。只有最上面的標記爲鼠標懸停在上面。我可以讓WPF爲IsMouseOver覆蓋和覆蓋元素嗎?
有沒有辦法讓IsMouseOver像這樣隧道? 如果沒有,有什麼建議嗎?
可以使用VisualTreeHelper做手工命中測試。這可以進入某個父對象的MouseMove處理程序。在這裏我假設作了題爲RedCircle和BlueCircle橢圓的維恩圖:
bool overRed = false;
bool overBlue = false;
if (BlueCircle.IsMouseOver || RedCircle.IsMouseOver)
{
HitTestParameters parameters = new PointHitTestParameters(e.GetPosition(RedCircle));
VisualTreeHelper.HitTest(RedCircle, new HitTestFilterCallback(element => HitTestFilterBehavior.Continue), result =>
{
if (result.VisualHit == RedCircle)
overRed = true;
return HitTestResultBehavior.Continue;
}, parameters);
parameters = new PointHitTestParameters(e.GetPosition(BlueCircle));
VisualTreeHelper.HitTest(BlueCircle, new HitTestFilterCallback(element => HitTestFilterBehavior.Continue), result =>
{
if (result.VisualHit == BlueCircle)
overBlue = true;
return HitTestResultBehavior.Continue;
}, parameters);
}
使用IsMouseDirectlyOver屬性。這似乎是你需要的東西。
http://msdn.microsoft.com/en-us/library/system.windows.uielement.ismousedirectlyoverproperty.aspx
其實,這不是它。如果有任何來自子元素的遮擋,則IsMouseDirectlyOver爲false。對於IsMouseOver,子元素不計數,只是父母的邊界。 IsMouseDirectlyOver更具體。由於我的維恩圈子之間沒有任何兒童關係,因此我希望能夠直接瞭解什麼是直接結束。 – Ball 2010-07-15 19:54:34
我需要類似的東西在我的項目,並颳起了快速的解決方案。
public sealed class IsMouseOverEnchancementBehavior
{
#region MouseCurrentPosition
internal sealed class MouseCurrentPosition
{
private Point _currentPosition;
private readonly Timer _timer = new Timer(250);
public event EventHandler<EventArgs> PositionChanged;
public MouseCurrentPosition()
{
_timer.Elapsed += timer_Elapsed;
_timer.Start();
}
public Point CurrentPosition
{
get { return _currentPosition; }
set
{
if (_currentPosition != value)
{
_currentPosition = value;
var pos = PositionChanged;
if (pos != null)
PositionChanged(null, null);
}
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(ref NativePoint pt);
public static Point GetCurrentMousePosition()
{
var nativePoint = new NativePoint();
GetCursorPos(ref nativePoint);
return new Point(nativePoint.X, nativePoint.Y);
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Point current = GetCurrentMousePosition();
CurrentPosition = current;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NativePoint
{
public int X;
public int Y;
};
}
#endregion
private static readonly MouseCurrentPosition _mouseCurrentPosition = new MouseCurrentPosition();
public static DependencyProperty IsMouseOverIgnoreChild =
DependencyProperty.RegisterAttached("IsMouseOverIgnoreChild", typeof (bool),
typeof (IsMouseOverEnchancementBehavior),
new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsMouseOverEnchancementEnabled =
DependencyProperty.RegisterAttached("IsMouseOverEnchancementEnabled",
typeof (bool), typeof (IsMouseOverEnchancementBehavior),
new UIPropertyMetadata(false, OnMouseOverEnchancementEnabled));
private static void OnMouseOverEnchancementEnabled(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// todo: unhook if necessary.
var frameworkElement = (FrameworkElement) d;
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(UIElement.IsMouseOverProperty,
typeof (UIElement));
Action calculateCurrentStateAction =() =>
{
// cheap check.
if (frameworkElement.IsMouseOver)
{
SetIsMouseOverIgnoreChild(frameworkElement, true);
return;
}
// through hit-testing.
var isMouseOver = VisualTreeHelper.
HitTest(frameworkElement, Mouse.GetPosition(frameworkElement)) != null;
SetIsMouseOverIgnoreChild(frameworkElement, isMouseOver);
};
// if the mose moves,
// we shall re-do hit testing.
_mouseCurrentPosition.PositionChanged += delegate
{
frameworkElement.Dispatcher.Invoke(
calculateCurrentStateAction);
};
// If IsMouseOver changes,
// we can propagate it to our property.
dpd.AddValueChanged(frameworkElement,
delegate { calculateCurrentStateAction(); });
}
#region Misc
public static bool GetIsMouseOverEnchancementEnabled(DependencyObject obj)
{
return (bool) obj.GetValue(IsMouseOverEnchancementEnabled);
}
public static void SetIsMouseOverEnchancementEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsMouseOverEnchancementEnabled, value);
}
public static bool GetIsMouseOverIgnoreChild(DependencyObject obj)
{
return (bool) obj.GetValue(IsMouseOverIgnoreChild);
}
public static void SetIsMouseOverIgnoreChild(DependencyObject obj, bool value)
{
obj.SetValue(IsMouseOverIgnoreChild, value);
}
#endregion
}
這是一個更通用的解決方案,使用計時器。
這是我如何使用它的風格:
<Setter Property="behaviors:IsMouseOverEnchancementBehavior.
IsMouseOverEnchancementEnabled" Value="True" />
<Style.Triggers>
<!-- Just a visual feedback -->
<!-- Let the user know that mouse is over the element -->
<!-- When we are in editmode -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInEditMode" Value="True" />
<Condition Property="behaviors:IsMouseOverEnchancementBehavior.IsMouseOverIgnoreChild" Value="True" />
</MultiTrigger.Conditions>
...do stuff here....
缺失的部分是何時擊中測試。這將因情況而異。另外,您需要從重疊對象的聯合父項進行測試。雖然在我的玩具例子中這沒有問題,但在我的真實情況下,我必須穿過邏輯樹中的共同父親的根。謝謝! – Ball 2010-07-21 12:27:13