在现在的Silverlight项目中,客户要求鼠标在可拖动面板上时为手形,拖动时为抓形。无奈Silverlight还不支持这个,只能自动动手做了。
自定义鼠标的思路就是把原始鼠标隐藏,然后做一个图片,跟着鼠标的位置移动,并在事件中改变图片,就是我们想要的自定义鼠标效果了。这个东西说起来简单,做起来并不那么容易,思路总是不容易理清。特别是嵌套元素都自定义了鼠标样式时,到底取谁的样式就成了一个问题了,还要考虑效率问题。
参考了网上两篇文章:
http://www.codeproject.com/KB/silverlight/SilverlightCustomCursors.aspx
http://www.cnblogs.com/dino623/archive/2010/04/01/1702260.html
看了codeproject上的那篇文章,发现在我们的项目中很合适,主要是简单。 就是有些bug,比如在程序中改变鼠标样式都不行,嵌套元素就出问题。 嵌套的问题当然是取最上层的那个元素了。主要代码如下:


public
class
CursorSet
{
const string ImagePath = " /Resources/Cursors/ " ;
internal static Point ? mousePoint;
internal static Popup Popup;
internal static Canvas AdornerLayer;
internal static List < string > IDs;
internal static Dictionary < FrameworkElement, ContentControl > ActiveElements;
internal static UIElement currentElement;
internal static Point currentPoint;
#region public string ID (attached)
public static string GetID(DependencyObject d)
{
return ( string )d.GetValue(IDProperty);
}
public static void SetID(DependencyObject d, string id)
{
d.SetValue(IDProperty, id);
}
public static readonly DependencyProperty IDProperty = DependencyProperty.RegisterAttached(
" ID " ,
typeof ( string ),
typeof (CursorSet),
new PropertyMetadata( new PropertyChangedCallback(OnIDPropertyChanged)));
static void OnIDPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = d as FrameworkElement;
string oldId = e.OldValue as string ;
string newId = e.NewValue as string ;
Cursor cursor;
if (e.OldValue != null )
{
AdornerLayer.Children.Clear();
}
if (IsValidID(newId))
{
if (IsSystemType(newId, out cursor))
{
element.Cursor = cursor;
if (ActiveElements.ContainsKey(element))
ActiveElements[element] = null ;
else
{
ActiveElements.Add(element, null );
AddMouseEventHandlers(element);
}
}
else
{
ContentControl customCursor = CreateControl(newId);
if (ActiveElements.ContainsKey(element))
ActiveElements[element] = customCursor;
else
{
ActiveElements.Add(element, customCursor);
AddMouseEventHandlers(element);
}
if (mousePoint.HasValue)
{
ShowCursor(element, mousePoint.Value);
}
}
}
else
{
element.Cursor = null ;
RemoveMouseEventHandlers(element);
if (ActiveElements.ContainsKey(element))
ActiveElements.Remove(element);
}
}
#endregion
static CursorSet()
{
IDs = new List < string > ();
ActiveElements = new Dictionary < FrameworkElement, ContentControl > ();
IDs.Add( " Cross " );
IDs.Add( " SizeAll " );
IDs.Add( " Fist " );
IDs.Add( " Palm " );
Application.Current.Host.Content.Resized += OnContentResized;
}
static ContentControl CreateControl( string id)
{
if ( string .IsNullOrEmpty(id) || IDs == null || ! IDs.Contains(id))
return new ContentControl();
Image image = new Image()
{
Margin = new Thickness( - 12 , - 12 , 0 , 0 ),
Source = new BitmapImage(
new Uri( string .Format( " /{0};component{1}{2}.png " , typeof (CursorSet).Namespace, ImagePath, id), UriKind.Relative))
};
Canvas canvas = new Canvas();
canvas.Children.Add(image);
return new ContentControl { Content = canvas };
}
static bool IsValidID( string id)
{
Cursor cursor;
if (IsSystemType(id, out cursor))
return true ;
return IDs.Contains(id);
}
static bool IsSystemType( string id, out Cursor cursor)
{
cursor = null ;
if ( string .IsNullOrEmpty(id))
return true ;
switch (id)
{
case " Arrow " :
cursor = Cursors.Arrow;
return true ;
case " Eraser " :
cursor = Cursors.Eraser;
return true ;
case " Hand " :
cursor = Cursors.Hand;
return true ;
case " IBeam " :
cursor = Cursors.IBeam;
return true ;
case " None " :
cursor = Cursors.None;
return true ;
case " SizeNS " :
cursor = Cursors.SizeNS;
return true ;
case " SizeWE " :
cursor = Cursors.SizeWE;
return true ;
case " Stylus " :
cursor = Cursors.Stylus;
return true ;
case " Wait " :
cursor = Cursors.Wait;
return true ;
}
return false ;
}
static void OnContentResized( object sender, EventArgs e)
{
if (AdornerLayer != null )
{
AdornerLayer.Width = Application.Current.Host.Content.ActualWidth;
AdornerLayer.Height = Application.Current.Host.Content.ActualHeight;
}
}
static void EnsurePopup()
{
if (Popup == null || AdornerLayer == null )
{
AdornerLayer = new Canvas()
{
IsHitTestVisible = false ,
Width = Application.Current.Host.Content.ActualWidth,
Height = Application.Current.Host.Content.ActualHeight
};
Popup = new Popup
{
IsHitTestVisible = false ,
Child = AdornerLayer
};
}
}
static void AddMouseEventHandlers(FrameworkElement element)
{
element.MouseEnter += OnElementMouseEnter;
element.MouseMove += OnElementMouseMove;
element.MouseLeave += OnElementMouseLeave;
}
static void RemoveMouseEventHandlers(FrameworkElement element)
{
element.MouseEnter -= OnElementMouseEnter;
element.MouseMove -= OnElementMouseMove;
element.MouseLeave -= OnElementMouseLeave;
}
static void OnElementMouseEnter( object sender, MouseEventArgs e)
{
ShowCursor(sender as FrameworkElement, e.GetPosition( null ));
}
static void OnElementMouseMove( object sender, MouseEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
Point newPoint = e.GetPosition( null );
// Debug.WriteLine(string.Format("{0}:{1} - {2}", element.Name, CursorSet.GetID(element), newPoint));
if (newPoint != currentPoint)
{
currentPoint = newPoint;
currentElement = VisualTreeHelper.FindElementsInHostCoordinates(newPoint, element).Where(f => { return ! string .IsNullOrEmpty(CursorSet.GetID(f)); }).FirstOrDefault();
}
if (currentElement == element)
{
ShowCursor(element, newPoint);
}
}
static void OnElementMouseLeave( object sender, MouseEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
ContentControl control = ActiveElements[element];
if (control == null ) return ;
element.Cursor = null ;
AdornerLayer.Children.Remove(control);
Popup.IsOpen = false ;
mousePoint = null ;
}
static void ShowCursor(FrameworkElement element, Point point)
{
ContentControl customCursor = ActiveElements[element];
if (customCursor == null ) return ;
EnsurePopup();
element.Cursor = Cursors.None;
if ( ! AdornerLayer.Children.Contains(customCursor))
{
AdornerLayer.Children.Clear();
AdornerLayer.Children.Add(customCursor);
}
Canvas.SetTop(customCursor, point.Y);
Canvas.SetLeft(customCursor, point.X);
Popup.IsOpen = true ;
mousePoint = point;
}
}
{
const string ImagePath = " /Resources/Cursors/ " ;
internal static Point ? mousePoint;
internal static Popup Popup;
internal static Canvas AdornerLayer;
internal static List < string > IDs;
internal static Dictionary < FrameworkElement, ContentControl > ActiveElements;
internal static UIElement currentElement;
internal static Point currentPoint;
#region public string ID (attached)
public static string GetID(DependencyObject d)
{
return ( string )d.GetValue(IDProperty);
}
public static void SetID(DependencyObject d, string id)
{
d.SetValue(IDProperty, id);
}
public static readonly DependencyProperty IDProperty = DependencyProperty.RegisterAttached(
" ID " ,
typeof ( string ),
typeof (CursorSet),
new PropertyMetadata( new PropertyChangedCallback(OnIDPropertyChanged)));
static void OnIDPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = d as FrameworkElement;
string oldId = e.OldValue as string ;
string newId = e.NewValue as string ;
Cursor cursor;
if (e.OldValue != null )
{
AdornerLayer.Children.Clear();
}
if (IsValidID(newId))
{
if (IsSystemType(newId, out cursor))
{
element.Cursor = cursor;
if (ActiveElements.ContainsKey(element))
ActiveElements[element] = null ;
else
{
ActiveElements.Add(element, null );
AddMouseEventHandlers(element);
}
}
else
{
ContentControl customCursor = CreateControl(newId);
if (ActiveElements.ContainsKey(element))
ActiveElements[element] = customCursor;
else
{
ActiveElements.Add(element, customCursor);
AddMouseEventHandlers(element);
}
if (mousePoint.HasValue)
{
ShowCursor(element, mousePoint.Value);
}
}
}
else
{
element.Cursor = null ;
RemoveMouseEventHandlers(element);
if (ActiveElements.ContainsKey(element))
ActiveElements.Remove(element);
}
}
#endregion
static CursorSet()
{
IDs = new List < string > ();
ActiveElements = new Dictionary < FrameworkElement, ContentControl > ();
IDs.Add( " Cross " );
IDs.Add( " SizeAll " );
IDs.Add( " Fist " );
IDs.Add( " Palm " );
Application.Current.Host.Content.Resized += OnContentResized;
}
static ContentControl CreateControl( string id)
{
if ( string .IsNullOrEmpty(id) || IDs == null || ! IDs.Contains(id))
return new ContentControl();
Image image = new Image()
{
Margin = new Thickness( - 12 , - 12 , 0 , 0 ),
Source = new BitmapImage(
new Uri( string .Format( " /{0};component{1}{2}.png " , typeof (CursorSet).Namespace, ImagePath, id), UriKind.Relative))
};
Canvas canvas = new Canvas();
canvas.Children.Add(image);
return new ContentControl { Content = canvas };
}
static bool IsValidID( string id)
{
Cursor cursor;
if (IsSystemType(id, out cursor))
return true ;
return IDs.Contains(id);
}
static bool IsSystemType( string id, out Cursor cursor)
{
cursor = null ;
if ( string .IsNullOrEmpty(id))
return true ;
switch (id)
{
case " Arrow " :
cursor = Cursors.Arrow;
return true ;
case " Eraser " :
cursor = Cursors.Eraser;
return true ;
case " Hand " :
cursor = Cursors.Hand;
return true ;
case " IBeam " :
cursor = Cursors.IBeam;
return true ;
case " None " :
cursor = Cursors.None;
return true ;
case " SizeNS " :
cursor = Cursors.SizeNS;
return true ;
case " SizeWE " :
cursor = Cursors.SizeWE;
return true ;
case " Stylus " :
cursor = Cursors.Stylus;
return true ;
case " Wait " :
cursor = Cursors.Wait;
return true ;
}
return false ;
}
static void OnContentResized( object sender, EventArgs e)
{
if (AdornerLayer != null )
{
AdornerLayer.Width = Application.Current.Host.Content.ActualWidth;
AdornerLayer.Height = Application.Current.Host.Content.ActualHeight;
}
}
static void EnsurePopup()
{
if (Popup == null || AdornerLayer == null )
{
AdornerLayer = new Canvas()
{
IsHitTestVisible = false ,
Width = Application.Current.Host.Content.ActualWidth,
Height = Application.Current.Host.Content.ActualHeight
};
Popup = new Popup
{
IsHitTestVisible = false ,
Child = AdornerLayer
};
}
}
static void AddMouseEventHandlers(FrameworkElement element)
{
element.MouseEnter += OnElementMouseEnter;
element.MouseMove += OnElementMouseMove;
element.MouseLeave += OnElementMouseLeave;
}
static void RemoveMouseEventHandlers(FrameworkElement element)
{
element.MouseEnter -= OnElementMouseEnter;
element.MouseMove -= OnElementMouseMove;
element.MouseLeave -= OnElementMouseLeave;
}
static void OnElementMouseEnter( object sender, MouseEventArgs e)
{
ShowCursor(sender as FrameworkElement, e.GetPosition( null ));
}
static void OnElementMouseMove( object sender, MouseEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
Point newPoint = e.GetPosition( null );
// Debug.WriteLine(string.Format("{0}:{1} - {2}", element.Name, CursorSet.GetID(element), newPoint));
if (newPoint != currentPoint)
{
currentPoint = newPoint;
currentElement = VisualTreeHelper.FindElementsInHostCoordinates(newPoint, element).Where(f => { return ! string .IsNullOrEmpty(CursorSet.GetID(f)); }).FirstOrDefault();
}
if (currentElement == element)
{
ShowCursor(element, newPoint);
}
}
static void OnElementMouseLeave( object sender, MouseEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
ContentControl control = ActiveElements[element];
if (control == null ) return ;
element.Cursor = null ;
AdornerLayer.Children.Remove(control);
Popup.IsOpen = false ;
mousePoint = null ;
}
static void ShowCursor(FrameworkElement element, Point point)
{
ContentControl customCursor = ActiveElements[element];
if (customCursor == null ) return ;
EnsurePopup();
element.Cursor = Cursors.None;
if ( ! AdornerLayer.Children.Contains(customCursor))
{
AdornerLayer.Children.Clear();
AdornerLayer.Children.Add(customCursor);
}
Canvas.SetTop(customCursor, point.Y);
Canvas.SetLeft(customCursor, point.X);
Popup.IsOpen = true ;
mousePoint = point;
}
}
不知道博客园的silveright demo 是如何插入的,我在html中编辑之后发布,总是被改掉了,无法显示。