首先,看一下最终的效果图。我采用的开发环境是Visual Studio 2010。

Silverlight实现类似Mac Dock特效_xml

其实,这里要点,主要有两个,一个是计算鼠标位置到每个图像子元素中心位置的函数,另一个是计算放大倍数,放大倍数是鼠标位置到到每个图像子元素中心位置的距离的,具体如下:

下面是计算标位置到每个图像子元素中心位置的距离二次函数

/// <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;
}
}
}


2.新建承载的StackPanel,代码如下:

<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);
}
}
}


3.主页面MainPage,代码如下:

<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);
}
}
}
}


有需要源码的同学可以从我的网盘下载。源码下载地址如下:​​http://yunpan.cn/QI7KHpACMfiXa​