自定义控件
自定义控件是我比较陌生的一个主题。我好久没练习过wpf了,需要巩固记忆。我想了一会儿,打开动漫之家,忽然觉得这个看漫画的图片浏览控件有意思。于是特地花了一天做了这个图片控件。我原本以为很容易,但实际上并不简单。这个图片浏览控件比我想象中要难许多,有技术上的难题,也有逻辑上的难题。好在最后都解决了。
这个自定义控件算是比较好的练习。里面涉及了依赖属性
Binding
虚拟化加载
VisualState
动态资源
过渡效果
部件
光标
这些东西。
光标制作
对于电脑端来说,一个好看的光标是必不可少的。于是我查询了资料,了解到一种比较简洁的自定义光标方式。就是制作光标文件,然后在XAML中直接使用。
<Button x:Name="P_Bleft" Opacity="0" Grid.Column="0" Cursor="/程序集;component/路径/left.cur">上一页</Button> <Button x:Name="P_Bright" Opacity="0" Grid.Column="1" Cursor="/程序集;component/路径/right.cur">下一页</Button>
到哪里找图标呢呢?我想起了FontAwsome.V5,这个非常方便,并且美观。于是我到官网上找到了两个箭头图标。下一步是把图标转化成光标文件。FontAwsome.V5提供了svg格式的图标复制,然后我在这个网站发现了-->在线转换器<--可以将svg转化为cur光标文件,它提供了每天10次的免费额度。但要注意,需要设定光标尺寸。可以设置另存为文件之后,把svg元素的Width和Height属性改为16。
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16">
最后把光标文件放到项目中,以生成资源的形式编译。这里就能在XAML中直接使用了。如前面所示,是一个黑色箭头。
控制面板
作为一个图片浏览控件,控制面板必不可少。比较经典的做法是点击中间显示面板帮助。点击两边切换图片。面板帮助我就暂且省略,就做了切换图片的功能。整个自定义控件的是一个Grid
,分为2列,然后两个按钮各占1列,再把按钮隐藏起来。幸好,透明度为0时还能捕获事件。下方再重叠几个宽度为2列的Image
控件作为视口。
控件结构
经过多次更改,最终我把这个自定义控件设计为了一个分为4层Z-Index的控件。
- Z-Index=4 上一页、下一页
- Z-Index=3 当前页
- Z-Index=2 根据滑动方向确定的下一页
- Z-Index=1 上一页和下一页Z-Index之间的分隔板
- Z-Index=0 根据滑动方向确定的上一页
- 底层容器Grid
这是我为了实现正常显示效果,再调试过程中一层层加上去的。但是XAML中没法给非Canvas
子控件设置这个附加属性,所以这个附加属性只能在逻辑控件的代码中进行动态设置。这些设置代码与控件交互逻辑交织,本身就是控制逻辑的一部分。
var P_Bleft = (Button)GetTemplateChild("P_Bleft"); P_Bleft.SetValue(Canvas.ZIndexProperty, 4); P_Bright.SetValue(Canvas.ZIndexProperty, 4); P_Bulkhead.SetValue(Canvas.ZIndexProperty, 1);
虚拟化加载
作为一个图片浏览器,把全部图片一次性加载出来是一个方便的选择,但不是一个明智的选择。所以我考虑了虚拟化加载。最开始我想到了集合控件ListView
。但是这个我需要过渡效果。集合控件似乎没有实现这个。并且对内容大小控制也不是很完美。我还是自己从0开始做。
我走了很多弯路,最后确定使用3个水平排列的Image
控件进行图片的呈现。使用Storyboard
时间线控制过渡效果。我把图片URL集合组织成一个环形,然后让这3个Image在环上移动,这样实现虚拟化加载。
首先是预加载当前页和下一页。点击下一页之后,图片控件向左移动,同时最左边的、移动到视口之外的Image控件加载下下一页。第二次点击时,把首次预加载的图片移动到视口内,再把第二次预加载的图片移动到下一页位置。第三次点击时,这个控件移动到视口内,每个控件都有类似的过程,循环往复。同时还要考虑方向,即点击上一页。所以这要在逻辑代码中判断处理。
我用简单的取模运算符将数据组织成环形
//当前页 int state = (Current) % 3;
然后在一个switch
结构中完成控制逻辑
switch (state) { case 0: //很多判断 break; case 1: //很多判断 break; case 2: //很多判断 break; }
在swich结构中的几个重要工作是
- 确定图片那3个Image控件要从哪里移动到哪里,我选择高效的
RenderTransform
。位置一个有3个-width、0、width。每个控件在每个时刻,个占据一个位置 - 计算不同时间,每个控件Z-Index的正确值。在前面已经指出了层级了。
- 找到需要加载的图片,在合适的时机切换某个Image控件的图片源。经过我的碰壁,发现同一时间全部替换,是一个错误的做法。最后换成了每次点击值加载一张图片。即根据滑动方向,加载下下张。但要注意,在必要时时判断是否超出数组索引。
//左移 P_Cimage.Source = new BitmapImage(new Uri(Urls[Current - 1])); //右移 if(Current + 1 < Urls.Count) { P_Cimage.Source = new BitmapImage(new Uri(Urls[Current + 1])); }
使用VisualState
可以简化这种管理。我把这种切换设计为3个状态Normal
R_Translate
L_Translate
。在不同状态时,3个图片控件分别占据不同位置,然后根据数据组织的环形结构进行状态切换。
这里比较麻烦的地方在于,状态中的动画没法没法绑定到依赖属性,其次也没法用动态资源进行切换属性值。还是得到逻辑代码中进行控制。
P_BL.RenderTransform.Value.Translate(-P_BC.ActualWidth, 0); P_BC.RenderTransform.Value.Translate(0, 0); P_BR.RenderTransform.Value.Translate(P_BC.ActualWidth, 0); d4_.To = -P_BC.ActualWidth; d5_.To = 0; d6_.To = P_BC.ActualWidth; d4.From = 0; d5.From = P_BC.ActualWidth; d6.From = -P_BC.ActualWidth;
为了响应控件大小变化后,动画元素还能够精确到达所需偏移位置,就要每次点击前微调控件位置。要让Image元素的RenderTransform使用DynamicResource
,才能在代码中进行更新位置。全屏后点击下一页,下一页Image控件的偏移量是1920,但求出全屏后,需要的偏移量就小多了,需要根据控件大小动态计算。主要原因是因为这里要使用动画,不然的话,使用布局计算可能是是一个比较好的方式。
<Border x:Name="P_BL" Grid.Column="0" Grid.ColumnSpan="2" Background="LightPink"> <Border.RenderTransform> <TranslateTransform X="{DynamicResource left}"/> </Border.RenderTransform> <Image x:Name="P_Limage"/> </Border>
完整代码
Generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Pictures" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:sys2="http://schemas.microsoft.com/netfx/2009/xaml/presentation"> <!--<local:NegativeValueConverter x:Key="NegativeValueConverter"/>--> <sys:Double x:Key="left">-1200</sys:Double> <sys:Double x:Key="right">1200</sys:Double> <sys2:Duration x:Key="dur">0:0:0.2</sys2:Duration> <sys2:PowerEase EasingMode="EaseOut" Power="0.3" x:Key="ea"/> <Style TargetType="{x:Type local:CustomControl2}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:CustomControl2}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Border x:Name="P_Bulkhead" Grid.Column="0" Grid.ColumnSpan="2" Background="White"></Border> <Border x:Name="P_BC" Grid.Column="0" Grid.ColumnSpan="2" Background="LightGreen"> <Border.RenderTransform> <!--这个始终为零就不需要使用资源了--> <TranslateTransform X="0"/> </Border.RenderTransform> <Image x:Name="P_Cimage"/> </Border> <Border x:Name="P_BL" Grid.Column="0" Grid.ColumnSpan="2" Background="LightPink"> <Border.RenderTransform> <TranslateTransform X="{DynamicResource left}"/> </Border.RenderTransform> <Image x:Name="P_Limage"/> </Border> <Border x:Name="P_BR" Grid.Column="0" Grid.ColumnSpan="2" Background="LightBlue"> <Border.RenderTransform> <TranslateTransform X="{DynamicResource right}"/> </Border.RenderTransform> <Image x:Name="P_Rimage"/> </Border> <TextBlock Grid.Column="2" Text="{Binding Current, RelativeSource={RelativeSource TemplatedParent}, StringFormat='{}{0}'}"/> <Button x:Name="P_Bleft" Opacity="0" Grid.Column="0" Cursor="/Pictures;component/images/left.cur">上一页</Button> <Button x:Name="P_Bright" Opacity="0" Grid.Column="1" Cursor="/Pictures;component/images/right.cur">下一页</Button> </Grid> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="group1"> <VisualState x:Name="Normal"> <Storyboard> <DoubleAnimation x:Name="d1_" Storyboard.TargetName="P_BL" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="0:0:0" To="{DynamicResource left}"/> <DoubleAnimation x:Name="d2_" Storyboard.TargetName="P_BC" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="0:0:0" To="0"/> <DoubleAnimation x:Name="d3_" Storyboard.TargetName="P_BR" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="0:0:0" To="{DynamicResource right}"/> </Storyboard> </VisualState> <VisualState x:Name="R_Translate"> <Storyboard> <DoubleAnimation x:Name="d4_" Storyboard.TargetName="P_BC" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="0:0:0" To="{DynamicResource left}"/> <DoubleAnimation x:Name="d5_" Storyboard.TargetName="P_BR" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="0:0:0" To="0"/> <DoubleAnimation x:Name="d6_" Storyboard.TargetName="P_BL" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="0:0:0" To="{DynamicResource right}"/> </Storyboard> </VisualState> <VisualState x:Name="L_Translate"> <Storyboard> <DoubleAnimation x:Name="d7_" Storyboard.TargetName="P_BR" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="0:0:0" To="{DynamicResource left}"/> <DoubleAnimation x:Name="d8_" Storyboard.TargetName="P_BL" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="0:0:0" To="0"/> <DoubleAnimation x:Name="d9_" Storyboard.TargetName="P_BC" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="0:0:0" To="{DynamicResource right}"/> </Storyboard> </VisualState> <VisualStateGroup.Transitions> <VisualTransition To="Normal"> <Storyboard> <DoubleAnimation x:Name="d1" Storyboard.TargetName="P_BL" EasingFunction="{StaticResource ea}" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="{StaticResource dur}" To="{DynamicResource left}"/> <DoubleAnimation x:Name="d2" Storyboard.TargetName="P_BC" EasingFunction="{StaticResource ea}" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="{StaticResource dur}" To="0"/> <DoubleAnimation x:Name="d3" Storyboard.TargetName="P_BR" EasingFunction="{StaticResource ea}" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="{StaticResource dur}" To="{DynamicResource right}"/> </Storyboard> </VisualTransition> <VisualTransition To="R_Translate"> <Storyboard> <DoubleAnimation x:Name="d4" Storyboard.TargetName="P_BC" EasingFunction="{StaticResource ea}" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="{StaticResource dur}" To="{DynamicResource left}"/> <DoubleAnimation x:Name="d5" Storyboard.TargetName="P_BR" EasingFunction="{StaticResource ea}" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="{StaticResource dur}" To="0"/> <DoubleAnimation x:Name="d6" Storyboard.TargetName="P_BL" EasingFunction="{StaticResource ea}" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="{StaticResource dur}" To="{DynamicResource right}"/> </Storyboard> </VisualTransition> <VisualTransition To="L_Translate"> <Storyboard> <DoubleAnimation x:Name="d7" Storyboard.TargetName="P_BR" EasingFunction="{StaticResource ea}" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="{StaticResource dur}" To="{DynamicResource left}"/> <DoubleAnimation x:Name="d8" Storyboard.TargetName="P_BL" EasingFunction="{StaticResource ea}" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="{StaticResource dur}" To="0"/> <DoubleAnimation x:Name="d9" Storyboard.TargetName="P_BC" EasingFunction="{StaticResource ea}" Storyboard.TargetProperty="(Border.RenderTransform).(TranslateTransform.X)" Duration="{StaticResource dur}" To="{DynamicResource right}"/> </Storyboard> </VisualTransition> </VisualStateGroup.Transitions> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
后台代码
namespace Pictures { [TemplatePart(Name = "P_Bleft",Type = typeof(Button))] [TemplatePart(Name = "P_Bright", Type = typeof(Button))] [TemplatePart(Name = "P_Limage", Type = typeof(Image))] [TemplatePart(Name = "P_Cimage", Type = typeof(Image))] [TemplatePart(Name = "P_Rimage", Type = typeof(Image))] [TemplatePart(Name = "P_BC", Type = typeof(Border))] [TemplatePart(Name = "P_BL", Type = typeof(Border))] [TemplatePart(Name = "P_BR", Type = typeof(Border))] [TemplatePart(Name = "P_Bulkhead",Type =typeof(Border))] [TemplatePart(Name = "P_BC", Type = typeof(Border))] [TemplatePart(Name = "P_BL", Type = typeof(Border))] [TemplatePart(Name = "P_BR", Type = typeof(Border))] [TemplateVisualState(GroupName = "group1",Name = "L_Translate")] [TemplateVisualState(GroupName = "group1", Name = "R_Translate")] [TemplateVisualState(GroupName = "group1", Name = "Normal")] [TemplatePart(Name = "d1", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d2", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d3", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d4", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d5", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d6", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d7", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d8", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d9", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d1_", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d2_", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d3_", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d4_", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d5_", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d6_", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d7_", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d8_", Type = typeof(DoubleAnimation))] [TemplatePart(Name = "d9_", Type = typeof(DoubleAnimation))] public class CustomControl2 : Control { public const string L_Translate = "L_Translate"; public const string R_Translate = "R_Translate"; public const string Normal = "Normal"; static CustomControl2() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl2), new FrameworkPropertyMetadata(typeof(CustomControl2))); } /// <summary> /// 当前页 /// </summary> public int Current { get { return (int)GetValue(CurrentProperty); } set { SetValue(CurrentProperty, value); } } public static readonly DependencyProperty CurrentProperty = DependencyProperty.Register("Current", typeof(int), typeof(CustomControl2), new PropertyMetadata(0)); /// <summary> /// 图片 /// </summary> public ObservableCollection<string> Urls { get { return (ObservableCollection<string>)GetValue(UrlsProperty); } set { SetValue(UrlsProperty, value); } } public static readonly DependencyProperty UrlsProperty = DependencyProperty.Register("Urls", typeof(ObservableCollection<string>), typeof(CustomControl2), new PropertyMetadata(null, OnUrlsChanged)); private static void OnUrlsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { CustomControl2 control = d as CustomControl2; if (control != null) { control.OnUrlsChanged((ObservableCollection<string>)e.OldValue, (ObservableCollection<string>)e.NewValue); } } private void OnUrlsChanged(ObservableCollection<string> oldValue, ObservableCollection<string> newValue) { Current = 0; BeginStoryBoard(0, true); } public override void OnApplyTemplate() { base.OnApplyTemplate(); Button P_Bleft = (Button)GetTemplateChild("P_Bleft"); Button P_Bright = (Button)GetTemplateChild("P_Bright"); //隔板 Border P_Bulkhead = (Border)GetTemplateChild("P_Bulkhead"); P_Bleft.SetValue(Canvas.ZIndexProperty, 4); P_Bright.SetValue(Canvas.ZIndexProperty, 4); P_Bulkhead.SetValue(Canvas.ZIndexProperty, 1); P_Bleft.Click += (s, e) => ToPage(-1); P_Bright.Click += (s, e) => ToPage(1); } private void ToPage(int offset) { if (Current==0 && offset<0) { return; } if (Current>=Urls.Count-1 && offset>0) { return; } Current += offset; BeginStoryBoard(offset); } private void BeginStoryBoard(int offset,bool init=false) { Image P_Cimage = (Image)GetTemplateChild("P_Cimage"); Image P_Limage = (Image)GetTemplateChild("P_Limage"); Image P_Rimage = (Image)GetTemplateChild("P_Rimage"); Border P_BC = (Border)GetTemplateChild("P_BC"); Border P_BL = (Border)GetTemplateChild("P_BL"); Border P_BR = (Border)GetTemplateChild("P_BR"); Application.Current.Resources["left"]= -P_BC.ActualWidth; Application.Current.Resources["right"] = P_BC.ActualWidth; //当前页 int state = (Current) % 3; //由于动画不能绑定To属性,只能在代码中动态调整 #region 调整状态 DoubleAnimation d1 = (DoubleAnimation)GetTemplateChild("d1"); DoubleAnimation d2 = (DoubleAnimation)GetTemplateChild("d2"); DoubleAnimation d3 = (DoubleAnimation)GetTemplateChild("d3"); DoubleAnimation d4 = (DoubleAnimation)GetTemplateChild("d4"); DoubleAnimation d5 = (DoubleAnimation)GetTemplateChild("d5"); DoubleAnimation d6 = (DoubleAnimation)GetTemplateChild("d6"); DoubleAnimation d7 = (DoubleAnimation)GetTemplateChild("d7"); DoubleAnimation d8 = (DoubleAnimation)GetTemplateChild("d8"); DoubleAnimation d9 = (DoubleAnimation)GetTemplateChild("d9"); DoubleAnimation d1_ = (DoubleAnimation)GetTemplateChild("d1_"); DoubleAnimation d2_ = (DoubleAnimation)GetTemplateChild("d2_"); DoubleAnimation d3_ = (DoubleAnimation)GetTemplateChild("d3_"); DoubleAnimation d4_ = (DoubleAnimation)GetTemplateChild("d4_"); DoubleAnimation d5_ = (DoubleAnimation)GetTemplateChild("d5_"); DoubleAnimation d6_ = (DoubleAnimation)GetTemplateChild("d6_"); DoubleAnimation d7_ = (DoubleAnimation)GetTemplateChild("d7_"); DoubleAnimation d8_ = (DoubleAnimation)GetTemplateChild("d8_"); DoubleAnimation d9_ = (DoubleAnimation)GetTemplateChild("d9_"); d1.To = -P_BC.ActualWidth; d2.To = 0; d3.To = P_BC.ActualWidth; d4.To = -P_BC.ActualWidth; d5.To = 0; d6.To = P_BC.ActualWidth; d7.To = -P_BC.ActualWidth; d8.To = 0; d9.To = P_BC.ActualWidth; d1_.To = -P_BC.ActualWidth; d2_.To = 0; d3_.To = P_BC.ActualWidth; d4_.To = -P_BC.ActualWidth; d5_.To = 0; d6_.To = P_BC.ActualWidth; d7_.To = -P_BC.ActualWidth; d8_.To = 0; d9_.To = P_BC.ActualWidth; #endregion switch (state) { case 0: P_BC.SetValue(Canvas.ZIndexProperty, 3); if (init) { if (Urls.Count>0) { P_Cimage.Source = new BitmapImage(new Uri(Urls[0])); } if (Urls.Count > 1) { P_Rimage.Source = new BitmapImage(new Uri(Urls[1])); } P_BL.RenderTransform.Value.Translate(-P_BC.ActualWidth, 0); P_BC.RenderTransform.Value.Translate(0, 0); P_BR.RenderTransform.Value.Translate(P_BC.ActualWidth, 0); } else { if (offset==1) { //右移 if(Current + 1 < Urls.Count) { P_Rimage.Source = new BitmapImage(new Uri(Urls[Current + 1])); } P_BL.SetValue(Canvas.ZIndexProperty, 2); P_BR.SetValue(Canvas.ZIndexProperty, 0); d1.From = 0; d2.From = P_BC.ActualWidth; d3.From = -P_BC.ActualWidth; } if (offset==-1) { //左移 if (Current>0) { P_Limage.Source = new BitmapImage(new Uri(Urls[Current - 1])); } P_BL.SetValue(Canvas.ZIndexProperty, 0); P_BR.SetValue(Canvas.ZIndexProperty, 2); d1.From = P_BC.ActualWidth; d2.From = -P_BC.ActualWidth; d3.From = 0; } } VisualStateManager.GoToState(this, Normal, true); break; case 1: P_BR.SetValue(Canvas.ZIndexProperty, 3); if (offset == 1) { //右移 if (Current+1<Urls.Count) { P_Limage.Source = new BitmapImage(new Uri(Urls[Current + 1])); } P_BC.SetValue(Canvas.ZIndexProperty, 2); P_BL.SetValue(Canvas.ZIndexProperty, 0); d4.From = 0; d5.From = P_BC.ActualWidth; d6.From = -P_BC.ActualWidth; } if (offset == -1) { //左移 P_Cimage.Source = new BitmapImage(new Uri(Urls[Current - 1])); P_BC.SetValue(Canvas.ZIndexProperty, 0); P_BL.SetValue(Canvas.ZIndexProperty, 2); d4.From = P_BC.ActualWidth; d5.From = -P_BC.ActualWidth; d6.From = 0; } VisualStateManager.GoToState(this, R_Translate, true); break; case 2: P_BL.SetValue(Canvas.ZIndexProperty, 3); if (offset == 1) { //右移 if(Current + 1 < Urls.Count) { P_Cimage.Source = new BitmapImage(new Uri(Urls[Current + 1])); } P_BC.SetValue(Canvas.ZIndexProperty, 0); P_BR.SetValue(Canvas.ZIndexProperty, 2); d7.From=0; d8.From = P_BC.ActualWidth; d9.From=-P_BC.ActualWidth; } if (offset == -1) { //左移 P_Rimage.Source = new BitmapImage(new Uri(Urls[Current - 1])); P_BC.SetValue(Canvas.ZIndexProperty, 2); P_BR.SetValue(Canvas.ZIndexProperty, 0); d7.From = P_BC.ActualWidth; d8.From = -P_BC.ActualWidth; d9.From = 0; } VisualStateManager.GoToState(this, L_Translate, true); break; } } } }