首先,看一下最终的效果图。我采用的开发环境是Visual Studio 2010。
其实,这里要点,主要有两个,一个是计算鼠标位置到每个图像子元素中心位置的函数,另一个是计算放大倍数,放大倍数是鼠标位置到到每个图像子元素中心位置的距离的,具体如下:
下面是计算标位置到每个图像子元素中心位置的距离二次函数
/// <summary> /// 计算鼠标位置到SilverDockItem元素的中心距离 /// </summary> /// <param name="mouseArgs">The <see cref="MouseButtonEventArgs"/> instance containing the event data.</param> /// <param name="target">The target.</param> /// <returns></returns> private double CalculateDistance(MouseEventArgs mouseArgs, SilverDockItem target) { double mouseX = mouseArgs.GetPosition(target).X - (target.ActualWidth / 2); double mouseY = mouseArgs.GetPosition(target).Y - (target.ActualHeight / 2); double distance = Math.Sqrt(mouseX * mouseX + mouseY * mouseY); return distance; }
下面是计算放大的倍数
/// <summary> /// 计算SilverDockItem元素的放大倍数 /// </summary> /// <param name="maxZoom">放大倍数上限,即最大的放大倍数</param> /// <param name="maxDistance">感知长度,超过这个距离后放大倍数保持在1</param> /// <param name="distanceToItem">鼠标到目标Item中心的距离</param> /// <returns></returns> private double Zoom(double maxZoom, double maxDistance, double distanceToItem) { //计算放大的倍数,这里放大倍数和鼠标到SilverDockItem元素的中心距离为二次函数关系 double zoom = (maxZoom * (distanceToItem - maxDistance) * (distanceToItem - maxDistance)) / (maxDistance * maxDistance + 1); //如果计算放大倍数小于1,返回1,即不进行放大也不缩小 //也就是说鼠标到子元素中心距离大于一定值时就不进行缩放了 if (zoom < 1) { return 1; } else { return zoom; } }
1. 新建图像子元素SilverDockItem,代码如下:
<UserControl x:Class="SilverControl.SilverDockItem" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Height="60" Width="60" SizeChanged="UserControl_SizeChanged"> <Grid x:Name="LayoutRoot"> <Rectangle d:DesignHeight="60" d:DesignWidth="60" MouseMove="Rectangle_MouseMove" MouseLeave="Rectangle_MouseLeave"> <Rectangle.Fill> <ImageBrush x:Name="ContentImage" Stretch="Fill" /> </Rectangle.Fill> </Rectangle> </Grid> </UserControl>
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media.Imaging; namespace SilverControl { public partial class SilverDockItem : UserControl { public static readonly double ORIGNSIZE = 60; //宿主容器,即容纳SilverDock元素的父控件 internal SilverDock Dock { get; set; } public SilverDockItem(String uri) { InitializeComponent(); //设置图像的ImageSource依赖属性,即要显示的图像元素 //下面两句是同样的效果 //ContentImage.SetValue(ImageBrush.ImageSourceProperty, new BitmapImage(new Uri(uri, UriKind.Relative))); ContentImage.ImageSource = new BitmapImage(new Uri(uri, UriKind.Relative)); } /// <summary> /// 计算SilverDockItem元素的放大倍数 /// </summary> /// <param name="maxZoom">放大倍数上限,即最大的放大倍数</param> /// <param name="maxDistance">感知长度,超过这个距离后放大倍数保持在1</param> /// <param name="distanceToItem">鼠标到目标Item中心的距离</param> /// <returns></returns> private double Zoom(double maxZoom, double maxDistance, double distanceToItem) { //计算放大的倍数,这里放大倍数和鼠标到SilverDockItem元素的中心距离为二次函数关系 double zoom = (maxZoom * (distanceToItem - maxDistance) * (distanceToItem - maxDistance)) / (maxDistance * maxDistance + 1); //如果计算放大倍数小于1,返回1,即不进行放大也不缩小 //也就是说鼠标到子元素中心距离大于一定值时就不进行缩放了 if (zoom < 1) { return 1; } else { return zoom; } } /// <summary> /// 计算鼠标位置到SilverDockItem元素的中心距离 /// </summary> /// <param name="mouseArgs">The <see cref="MouseButtonEventArgs"/> instance containing the event data.</param> /// <param name="target">The target.</param> /// <returns></returns> private double CalculateDistance(MouseEventArgs mouseArgs, SilverDockItem target) { double mouseX = mouseArgs.GetPosition(target).X - (target.ActualWidth / 2); double mouseY = mouseArgs.GetPosition(target).Y - (target.ActualHeight / 2); double distance = Math.Sqrt(mouseX * mouseX + mouseY * mouseY); return distance; } /// <summary> /// Handles the MouseMove event of the Rectangle control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="MouseEventArgs"/> instance containing the event data.</param> private void Rectangle_MouseMove(object sender, MouseEventArgs e) { foreach (var item in Dock.items) { double distance = CalculateDistance(e, item); double zoom = Zoom(Dock.MaxZoom, Dock.MaxDistance, distance); item.Width = zoom * Dock.Size; item.Height = zoom * Dock.Size; } } /// <summary> /// Handles the MouseLeave event of the Rectangle control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="MouseEventArgs"/> instance containing the event data.</param> private void Rectangle_MouseLeave(object sender, MouseEventArgs e) { foreach (var item in Dock.items) { item.Width = ORIGNSIZE; item.Height = ORIGNSIZE; } } /// <summary> /// Handles the SizeChanged event of the UserControl control. /// 当每个SilverDockItem元素的大小改变时,动态修改其承载元素StackPanel的长度,可以可以确保StackPanel始终居中显示 /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param> private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e) { double totalWidth = 0; foreach (var item in Dock.items) { totalWidth += item.Width; } Dock.ContentPanel.Width = totalWidth; } } }
<UserControl x:Class="SilverControl.SilverDock" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Height="100" Width="900" BorderBrush="Black" BorderThickness="1"> <Grid x:Name="LayoutRoot"> <Rectangle Fill="#55000000" Stroke="Black" RadiusX="9" RadiusY="9"> <Rectangle.Effect> <DropShadowEffect/> </Rectangle.Effect> </Rectangle> <StackPanel x:Name="ContentPanel" Orientation="Horizontal" /> </Grid> </UserControl>
using System.Collections.Generic; using System.Windows.Controls; namespace SilverControl { public partial class SilverDock : UserControl { internal List<SilverDockItem> items = new List<SilverDockItem>(); public double Size { get; set; } //SilverDockItem子元素的尺寸,这里SilverDockItem长宽相等 public double MaxDistance { get; set; } //感知长度,超过这个距离后放大倍数保持在1 public double MaxZoom { get; set; } //放大倍数上限,即最大的放大倍数 public SilverDock() { InitializeComponent(); } /// <summary> /// 给SilverDock工具条动态添加子元素item /// </summary> /// <param name="item">SilverDockItem子元素</param> public void AddItem(SilverDockItem item) { item.Dock = this; item.Height = Size; item.Width = Size; items.Add(item); ContentPanel.Children.Add(item); } /// <summary> /// 从SilverDock工具条中移除SilverDockItem子元素 /// </summary> /// <param name="item">SilverDockItem子元素</param> public void RemoveItem(SilverDockItem item) { items.Remove(item); ContentPanel.Children.Remove(item); } } }
<UserControl x:Class="SilverControl.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> </Grid> </UserControl>
using System.Windows.Controls; namespace SilverControl { public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); SilverDock silverDock = new SilverDock(); silverDock.MaxZoom = 1.5; silverDock.MaxDistance = 600; silverDock.Size = 60; silverDock.Size = 60; LayoutRoot.Children.Add(silverDock); for (int i = 0; i <= 13; i++ ) { SilverDockItem item = new SilverDockItem("images/" + i + ".png"); silverDock.AddItem(item); } } } }