这里我们一起来制作一个Silverlight轮盘图片展示程序,效果如下图:

我们要实现的功能包括:
1、通过图片数量滑条来改变轮盘中图片的数目
2、通过图片X轴与Y轴半径滑条来改变轮盘的X轴方向与Y轴方向的大小
3、通过Auospin按钮来让轮盘自动转动,且其转动速度取决于鼠标距离程序背景中线的距离
4、通过Reset按钮让轮盘恢复初始设置
5、通过按下鼠标左键手工让轮盘转动,且其转动速度取决于鼠标距离程序背景中线的距离
,释放鼠标左键让轮盘停止转动。
6、点击轮盘图片时就在放映区显示此图片的大图
下面我们来创建我们的程序。
打开Vs2008,新建项目,创建一个名为SLImageCarousel的Silverlight应用程序,然后在SilverlightApplication1项目上新建一个名为images的文件夹,请拷贝二三十张图片到此文件夹下,分别改名字从0.jpg,1.jpg,......n.jpg,另外再拷贝一张图片1.jpg到外面,程序如下图:

一、界面代码如下(Page.xaml文件):

Code
<UserControl x:Class="SilverlightApplication1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="1024" Height="768">
<Canvas x:Name="root" Canvas.Top="200" Width="1024" Height="768" MouseLeftButtonDown="MainContactDown" MouseLeftButtonUp="MainContactUp" MouseMove="root_MouseMove">
<Rectangle Width="1024" Height="768" >
<Rectangle.Fill>
<RadialGradientBrush GradientOrigin="0.5,0" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="Beige" Offset="0" />
<GradientStop Color="Blue" Offset="1" />
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Image Name="mainImage" Canvas.Left="270" Canvas.Top="60" Canvas.ZIndex="169" Stretch="Fill" Height="290" Width="480" Source="1.jpg"/>
<Canvas Canvas.Left="270" Canvas.Top="350" >
<Canvas Canvas.Top="290">
<Canvas.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0.43" Color="BlanchedAlmond"/>
<GradientStop Offset="0.8" Color="Green"/>
</LinearGradientBrush>
</Canvas.OpacityMask>
<Image Name="mainImageReflection" Canvas.Left="-8" Canvas.Top="-338" Source="1.jpg"
Stretch="Fill" Height="380" Width="480" Opacity="0.4" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleY="-0.75"></ScaleTransform>
<SkewTransform AngleX="-15"></SkewTransform>
<TranslateTransform Y="-1" X="-30"></TranslateTransform>
</TransformGroup>
</Image.RenderTransform>
<Image.OpacityMask>
<LinearGradientBrush StartPoint="0.5,0.0" EndPoint="0.5,1.0">
<GradientStop Offset="0.0" Color="#00000000" />
<GradientStop Offset="1.0" Color="#FF000000" />
</LinearGradientBrush>
</Image.OpacityMask>
</Image>
</Canvas>
</Canvas>
<!--以下是定义控制面板-->
<Rectangle Width="1024" Height="68" Canvas.Top="700" Opacity="0.5">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#888" Offset="0" />
<GradientStop Color="#222" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Text="图片数量" FontSize="12" Canvas.Top="720" Canvas.Left="30" Foreground="White"/>
<Slider Value="40" ValueChanged="numPhotosSliderChanged" x:Name="numPhotosSlider" Canvas.Left="100" Canvas.Top="720" Width="200"/>
<TextBlock Text="调整转盘横向半径" FontSize="12" Canvas.Top="720" Canvas.Left="600" Foreground="White"/>
<Slider Value="500" ValueChanged="radiusSliderChangedX" x:Name="radiusSliderX" Canvas.Left="700" Canvas.Top="720" Width="200"/>
<TextBlock Text="调整转盘纵向半径" FontSize="12" Canvas.Top="740" Canvas.Left="600" Foreground="White"/>
<Slider Value="500" ValueChanged="radiusSliderChangedY" x:Name="radiusSliderY" Canvas.Left="700" Canvas.Top="740" Width="200"/>
<Button x:Name="ResetImagesButton" Click="ResetImagesButton_Click" Canvas.Top="740" Canvas.Left="460" Width="70" Content="Reset"/>
<Button x:Name="AutoSpinButton" Click="AutoSpinButton_Click" Canvas.Top="710" Canvas.Left="460" Width="70" Content="AutoSpin"/>
</Canvas>
</UserControl>
二、程序后台代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Windows.Threading; //因为要用到DispatcherTimer等
using System.Windows.Media.Imaging; //因为要用到BitmapImage
using System.Diagnostics; //因为要用到Debug

namespace SilverlightApplication1


{
public partial class Page : UserControl

{


定义变量#region 定义变量

private Boolean page_loaded = false;
List<object> angleValues = new List<object>(); //转盘图片在转盘中的角度位置

private int num_images = 20; //当前图片数量,初始化成20

private int radiusX = 500; //设置椭圆的长半径
private int radiusY = 110; //设置椭圆的短半径

private int centerX = 460; //设置椭圆的X坐标
private int centerY = 260; //设置椭圆的Y坐标

private double speed = 0;
private DispatcherTimer myTimer;

private bool main_down = false;

private DispatcherTimer autoTimer = new DispatcherTimer();

#endregion


定义要调用的函数#region 定义要调用的函数


Page_Load事件#region Page_Load事件
void Page_Loaded(object sender, RoutedEventArgs e)

{

myTimer.Start();

//加载默认初始照片
Uri uri = new Uri("./images/0.jpg", UriKind.Relative);
ImageSource imgSource = new BitmapImage(uri);
mainImage.Source = imgSource;
mainImageReflection.Source = imgSource;

//Debug.WriteLine("Loaded: " + mainImage.Source);
page_loaded = true; //设置page_loaded标志,表明已经加载了图片了

}

#endregion


Canvas_MouseLeftButtonDown事件#region Canvas_MouseLeftButtonDown事件
void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

{
Canvas c = sender as Canvas;

//c.Opacity = 0.5;

Rectangle r = c.Children[0] as Rectangle; //取得边框
Image img = c.Children[1] as Image; //取得图片
img.Opacity = 0.3; //设置图片透明度

//Debug.WriteLine("canvas down");

//加载点击选中的图片
mainImage.Source = img.Source;
mainImageReflection.Source = img.Source;
//设置边框颜色
r.Stroke = new SolidColorBrush(Colors.Red);
}

#endregion


清除所有图片#region 清除所有图片
void RemoveImages()

{
angleValues = new List<object>(); //重新new一个angleValues

for (int i = 0; i < num_images; i++) //移除所有图片

{
Canvas c = this.FindName("imageHolder_" + i) as Canvas;
if (c != null)

{
root.Children.Remove(c);
}
}

}
#endregion


加载指定数量的图片#region 加载指定数量的图片
private void BuildImages()

{
int cnt = 0; //计数

for (int i = 0; i < num_images; i++)

{

放置图片的Canvas创建与设置#region 放置图片的Canvas创建与设置
Canvas c = new Canvas();

c.Name = "imageHolder_" + i;
c.Width = 100;
c.Height = 100;

//Microsoft.Surface.Presentation.Contacts.AddPreviewContactDownHandler(c, CanvasImage_PreviewContactDown);

c.MouseLeftButtonDown += new MouseButtonEventHandler(Canvas_MouseLeftButtonDown); //加载鼠标点击事件

#endregion


图片的边框创建与设置#region 图片的边框创建与设置


创建一个边框#region 创建一个边框
Rectangle r = new Rectangle();
r.Width = 100;
r.Height = 100;
#endregion


边框填充方法一:直接填充图片所在的边框#region 边框填充方法一:直接填充图片所在的边框
// r.Fill = new SolidColorBrush(Colors.Orange);
#endregion


边框填充方法二:手工创建一个Brush,并使用此Brush来填充图片所在的边框#region 边框填充方法二:手工创建一个Brush,并使用此Brush来填充图片所在的边框
//创建一个Brush
LinearGradientBrush myLGB = new LinearGradientBrush();
myLGB.StartPoint = new Point(0, 0);
myLGB.EndPoint = new Point(0, 1);

GradientStop gs1 = new GradientStop();
gs1.Color = Colors.White;
gs1.Offset = 0.0;

GradientStop gs2 = new GradientStop();
gs2.Color = Colors.Gray;
gs2.Offset = 1.0;


myLGB.GradientStops.Add(gs1);
myLGB.GradientStops.Add(gs2);

//使用上面创建的Brush来填充图片所在的边框
r.Fill = myLGB;
#endregion

r.Stroke = new SolidColorBrush(Colors.Gray);

#endregion


图片控件创建与设置#region 图片控件创建与设置

//创建一个图片控件
Image img = new Image();
img.Stretch = Stretch.Fill;
img.Width = 80;
img.Height = 80;
img.SetValue(Canvas.LeftProperty, 10.0);
img.SetValue(Canvas.TopProperty, 10.0);


//创建一个图片源对象
Uri uri = new Uri("./images/" + cnt + ".jpg", UriKind.Relative);
ImageSource imgSource = new BitmapImage(uri);

//如果图片小于20张,则重复使用
if (cnt < 20)

{
cnt++;
}
else

{
cnt = 0;
}

img.Source = imgSource;

#endregion


在上面创建的Canvas中添加创建的边框和图片控件#region 在上面创建的Canvas中添加创建的边框和图片控件
c.Children.Add(r);
c.Children.Add(img);
#endregion


设置图片缩放比例#region 设置图片缩放比例
ScaleTransform st = new ScaleTransform();
st.CenterX = 50;
st.CenterY = 50;
st.ScaleX = 1;
st.ScaleY = 1;

c.RenderTransform = st;
#endregion


图片角度位置的计算与保存#region 图片角度位置的计算与保存
double ang = (double)i * ((Math.PI * 2) / num_images); //取得对应位置的角度值
angleValues.Add(ang); //把角度值保存到List中
#endregion

把上面创建的Canvas添加到页面上#region 把上面创建的Canvas添加到页面上
// c.SetValue(Canvas.LeftProperty, i * 100.0);
root.Children.Add(c);
#endregion
}

PositionImagesInCircle(); //放置对应的图片
}


#endregion


设置各个转盘图片在转盘中的位置以及缩放比例#region 设置各个转盘图片在转盘中的位置以及缩放比例
private void PositionImagesInCircle()

{
for (int i = 0; i < num_images; i++) //遍历所有指定数量的图片

{
//根据角度确定椭圆轨迹上的坐标值
double my_x = (double)Math.Cos((double)angleValues[i]) * radiusX + centerX; //取得X轴坐标值
double my_y = (double)Math.Sin((double)angleValues[i]) * radiusY + centerY; //取得Y轴坐标值

Canvas c = root.FindName("imageHolder_" + i) as Canvas; //找到子图片所在Canvas
c.SetValue(Canvas.LeftProperty, my_x); //设置Canvas的坐标值
c.SetValue(Canvas.TopProperty, my_y);
c.SetValue(Canvas.ZIndexProperty, (int)my_y); //设置Canvas的显示层次值,在Y坐标上值大图片的覆盖值小的图片

//缩放变换ScaleTransform允许我们对元素进行缩放,通过属性ScaleX和ScaleY来分别指定在X轴和Y轴上的缩放比例,
//同样也可以使用属性CenterX和CenterY来指定缩放中心
ScaleTransform st = c.RenderTransform as ScaleTransform;


double my_scale = (my_y - st.ScaleY) / (centerY + radiusY - st.ScaleY); //按一定方式进行缩放,原则是Y轴值大的图片大,Y轴值小的图片小

//double my_scale = (my_x - st.ScaleX) / (centerX + radiusX - st.ScaleX); //另外一种缩放公式
//double my_scale = 2 + (double)Math.Cos((double)angleValues[i] - Math.PI / 2); //另外一种缩放公式


st.ScaleX = my_scale;
st.ScaleY = my_scale;

angleValues[i] = (double)angleValues[i] + speed;

}

}
#endregion


myTimer_Tick 事件处理函数#region myTimer_Tick 事件处理函数
void myTimer_Tick(object sender, EventArgs e)

{
if (main_down)

{
PositionImagesInCircle(); //调整图片位置与缩放比例
}
}
#endregion

#endregion

public Page()

{
InitializeComponent();

ResetImagesButton.Click += new RoutedEventHandler(ResetImagesButton_Click); //按下Reset按钮时做ResetImagesButton_Click操作

myTimer = new DispatcherTimer();
myTimer.Interval = TimeSpan.FromMilliseconds(33);
myTimer.Tick += new EventHandler(myTimer_Tick);

//Console.WriteLine("call start");

//设置数量控制滑条的最大值与最小值
numPhotosSlider.Maximum = 180;
numPhotosSlider.Minimum = 10;

//设置X半径控制滑条的最大值与最小值
radiusSliderX.Maximum = 500;
radiusSliderX.Minimum = 320;

//设置Y半径控制滑条的最大值与最小值
radiusSliderY.Maximum = 200;
radiusSliderY.Minimum = 100;

Loaded += new RoutedEventHandler(Page_Loaded);

BuildImages();
}


private void numPhotosSliderChanged(object sender, RoutedPropertyChangedEventArgs<double> e)

{
Slider s = sender as Slider;

if (page_loaded) //如果页面已经加载

{
RemoveImages(); //移除所有图片

num_images = (int)s.Value; //重新设置当前应该加载的图片数量

BuildImages(); //重新加载图片
}
}

private void ResetImagesButton_Click(object sender, RoutedEventArgs e)

{
RemoveImages(); //清除所有图片


恢复变量默认初始值#region 恢复变量默认初始值
num_images = 20;
radiusX = 500;
radiusY = 110;
centerX = 460;
centerY = 260;
mainImage.SetValue(Canvas.ZIndexProperty, 169);
#endregion

BuildImages(); //重新加载指定数量的图片


重新设置滑条默认值#region 重新设置滑条默认值
numPhotosSlider.Value = 40.0;
radiusSliderX.Value = 500;
radiusSliderY.Value = 1100;
#endregion
}

private void radiusSliderChangedX(object sender, RoutedPropertyChangedEventArgs<double> e)

{
Slider s = sender as Slider;

if (page_loaded) //如果页面已经成功加载则作后续处理

{
RemoveImages(); //移除所有图片

radiusX = (int)s.Value; //取得新的半径值

if (s.Value < 450) //如果新的半径值低于我们要求的上限,则作调整,否则不作变动

{
//调整mainImage控件的ZIndexProperty属性,以免它因为radius的变动被后面的图片轮挡住
int z = (int)mainImage.GetValue(Canvas.ZIndexProperty);
z += 2;
mainImage.SetValue(Canvas.ZIndexProperty, z);
}
else

{
mainImage.SetValue(Canvas.ZIndexProperty, 169);
}

BuildImages(); //重新加载图片
}

}


private void MainContactDown(object sender, MouseButtonEventArgs e)

{
Point p = e.GetPosition(this);
Debug.WriteLine(">>" + p.Y);
if (p.Y > 500 && p.Y < 650) //判断是否在指定的区域内按下了鼠标

{
main_down = true;
}
}

private void MainContactUp(object sender, MouseButtonEventArgs e)

{
main_down = false;
}

private void root_MouseMove(object sender, MouseEventArgs e)

{
Point p = e.GetPosition(this);
speed = ((p.X - 500) / 500) * 0.0755; //调整轮盘的转速
//Debug.WriteLine("changed: " + speed);
}

private void radiusSliderChangedY(object sender, RoutedPropertyChangedEventArgs<double> e)

{
Slider s = sender as Slider;
if (page_loaded) //如果页面已经成功加载则作后续处理

{
RemoveImages(); //移除所有图片

radiusY = (int)s.Value; //取得新的半径值
if (s.Value < 200) //如果新的半径值低于我们要求的上限,则作调整,否则不作变动

{
//调整mainImage控件的ZIndexProperty属性,以免它因为radius的变动被后面的图片轮挡住
int z = (int)mainImage.GetValue(Canvas.ZIndexProperty);
z += 2;
mainImage.SetValue(Canvas.ZIndexProperty, z);
}
else

{
mainImage.SetValue(Canvas.ZIndexProperty, 169);
}

BuildImages(); //重新加载图片
}
}


autoTimer_Tick 事件处理函数#region autoTimer_Tick 事件处理函数
void autoTimer_Tick(object sender, EventArgs e)

{
PositionImagesInCircle(); //调整图片位置与缩放比例
}
#endregion

private void AutoSpinButton_Click(object sender, RoutedEventArgs e)

{
autoTimer.Start();
autoTimer.Interval = TimeSpan.FromMilliseconds(33);
autoTimer.Tick += new EventHandler(autoTimer_Tick);
}


}
}


代码说明:
1、关于轮盘上图片位置的确定
根据以下的椭圆图

在椭圆 (x2 /a)+(y2 /b ) =1 中,令 x/a=cosθ, y/b=sinθ 得 x=a cos θ y=b sin θ,这就是椭圆的参数方程,其中θ为参数(注意:θ 的几何意义是椭圆上点P对应的参数θ 应是∠AOx ,而不是 ∠POx);而 a 为椭圆的外切圆半径,b为椭圆的内切圆半径。
当我们确定椭圆轨道上放置N张图片时,我们可以计算得到图片间的夹角 θ= 360/N。此图这个椭圆正好在坐标中心,而在程序中,它可能位于于坐标(x,y)处。所以我们代码实现如下:
取得夹角
#region 图片角度位置的计算与保存
double ang = (double)i * ((Math.PI * 2) / num_images); //取得对应位置的角度值
angleValues.Add(ang); //把角度值保存到List中
#endregion
取得坐标
//根据角度确定椭圆轨迹上的坐标值
double my_x = (double)Math.Cos((double)angleValues[i]) * radiusX + centerX; //取得X轴坐标值
double my_y = (double)Math.Sin((double)angleValues[i]) * radiusY + centerY; //取得Y轴坐标值
2、关于图片大小
为了产生轨道上离我们近的图片大,离我们远的图片小的效果,我们需要根据图片放置的位置调整图片显示的大小,我们用以下方式来实现此目的
//缩放变换ScaleTransform允许我们对元素进行缩放,通过属性ScaleX和ScaleY来分别指定在X轴和Y轴上的缩放比例,
//同样也可以使用属性CenterX和CenterY来指定缩放中心
ScaleTransform st = c.RenderTransform as ScaleTransform;
double my_scale = (my_y - st.ScaleY) / (centerY + radiusY - st.ScaleY); //按一定方式进行缩放,原则是Y轴值大的图片大,Y轴值小的图片小
//double my_scale = (my_x - st.ScaleX) / (centerX + radiusX - st.ScaleX); //另外一种缩放公式
//double my_scale = 2 + (double)Math.Cos((double)angleValues[i] - Math.PI / 2); //另外一种缩放公式
st.ScaleX = my_scale;
st.ScaleY = my_scale;
3、关于轮盘转速
我们有个Speed参数来调整轮盘旋转速度,实现思路是根据鼠标当前位置相对于轮盘Y轴的相对位置来调整,实现代码如下:
Point p = e.GetPosition(this);
speed = ((p.X - 500) / 500) * 0.0755; //调整轮盘的转速
运行程序即可看到效果
本文程序在Silverlight2.0和VS2008环境中调试通过。本文参照了部分网络资料,希望能够抛砖引玉,大家共同学习。
(转载本文请注明出处)前往:Silverlight学习笔记清单
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架