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