Silverlight OverrideCursor






4.80/5 (2投票s)
WPF 中一个非常棒的功能是使用 Mouse.OverrideCursor 为整个应用程序设置光标。
WPF 中一个非常棒的功能是使用 Mouse.OverrideCursor
为整个应用程序设置光标。例如,如果你的应用程序需要执行一个耗时操作,你可以将光标更改为等待光标,直到操作完成。通常,你会像这样操作:
public void DoAReallyLongOperation()
{
Cursor savedCursor = Mouse.OverrideCursor;
try
{
PerformMyLengthyOperation();
}
finally
{
Mouse.OverrideCursor = savedCursor
}
}
到目前为止一切都很好。现在,你肯定会跑到你的 Silverlight 应用程序中,对耗时操作执行完全相同的操作,对吧?毕竟,Silverlight 是 WPF 的轻量级版本,所以肯定可以使用这个功能。
然而,事实并非如此。Silverlight 不支持 OverrideCursor
,但添加它应该不难,对吧?实际上,添加 OverrideCursor
并没有你想象的那么容易。例如,如果你将页面上的光标更改为等待光标,当鼠标移动到文本框上时,它仍然会是 IBeam
(文本光标)。这意味着你的代码需要遍历视觉树,查找所有子元素,并将光标设置为新的光标。
编辑:自从我今天早上发布这篇文章以来,有人提出了一种需要解决的边缘情况。基本上,代码需要能够将文本框重置回 IBeam
,假设 OverrideCursor
被重置。
以下类提供了一个附加属性,应该可以很好地解决这个问题。
/// <summary>
/// Mouse handling class to simulate the OverrideCursor functionality in WPF.
/// </summary>
public class Mouse : DependencyObject
{
#region Members
private static Cursor _oldCursor;
private static bool _isResetting = false;
private static Cursor _overrideCursor;
#endregion
/// <summary>
/// Recursively traverse the visual tree, setting the cursor as appropriate.
/// </summary>
///
<param name="current">The element to iterate over.</param>
///
<param name="cursor">The cursor to set to.</param>
/// <remarks>
/// If the new cursor is the same as the stored original cursor,
/// then we assume that this is a reset operation taking place.
/// At this point, we reset the cursor to the original for the element,
/// irrespective of what the Cursor states.
/// </remarks>
internal static void TraverseVisualTree(object current, Cursor cursor)
{
DependencyObject ob = current as DependencyObject;
if (ob != null)
{
if (ob is FrameworkElement)
{
FrameworkElement element = ob as FrameworkElement;
Cursor oldCursor = GetOldCursor(element);
Cursor newCursor = cursor;
// If this is a reset, then get the old cursor reference
// back so that we can use this.
if (_isResetting)
{
newCursor = oldCursor;
SetOldCursor(element, null);
}
else
{
SetOldCursor(element, element.Cursor);
}
element.Cursor = newCursor;
}
int counter = VisualTreeHelper.GetChildrenCount(ob);
for (int i = 0; i < counter; i++)
{
object depObj = VisualTreeHelper.GetChild(ob, i);
TraverseVisualTree(depObj, cursor);
}
}
}
/// <summary>
/// The OverrideCursor attached property.
/// </summary>
public static readonly DependencyProperty OverrideCursorProperty =
DependencyProperty.RegisterAttached("OverrideCursor",
typeof(Cursor),
typeof(UserControl),
new PropertyMetadata(null, OnCursorChanged));
/// <summary>
/// The OldCursor attached property.
/// </summary>
private static readonly DependencyProperty OldCursorProperty =
DependencyProperty.RegisterAttached("OldCursor",
typeof(Cursor),
typeof(UserControl),
new PropertyMetadata(null));
/// <summary>
/// Called when the cursor changes.
/// </summary>
private static void OnCursorChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
if (!(source is FrameworkElement))
return;
// If the "old" cursor is the same as the "new" cursor, the
// code will reset each elements cursor back to its original value.
_isResetting = (_oldCursor == e.NewValue);
if (!_isResetting)
{
_oldCursor = e.OldValue as Cursor;
}
TraverseVisualTree(source, e.NewValue as Cursor);
}
/// <summary>
/// Get the override cursor.
/// </summary>
///
<param name="source">The object to get the override cursor for.</param>
/// <returns>The populated override cursor.</returns>
public static Cursor GetOverrideCursor(DependencyObject source)
{
return source.GetValue(OverrideCursorProperty) as Cursor;
}
/// <summary>
/// Set the override cursor.
/// </summary>
///
<param name="source">The object to set the cursor for.</param>
///
<param name="value">The cursor value to set.</param>
public static void SetOverrideCursor(DependencyObject source, object value)
{
source.SetValue(OverrideCursorProperty, value);
}
/// <summary>
/// Set the old cursor.
/// </summary>
///
<param name="source">The object to set the cursor for.</param>
///
<param name="value">The cursor value to set.</param>
private static void SetOldCursor(DependencyObject source, object value)
{
source.SetValue(OldCursorProperty, value);
}
/// <summary>
/// Get the old cursor.
/// </summary>
///
<param name="source">The object to get the old cursor for.</param>
/// <returns>The populated old cursor.</returns>
private static Cursor GetOldCursor(DependencyObject source)
{
return source.GetValue(OldCursorProperty) as Cursor;
}
}