自定义控件

自定义控件是我比较陌生的一个主题。我好久没练习过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元素的WidthHeight属性改为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;
}
}
}
}

WPF【无限滚动图片浏览】自定义控件的更多相关文章

  1. 详细分析Android viewpager 无限循环滚动图片

    由于最近在忙于项目,就没时间更新博客了,于是趁着周日在房间把最近的在项目中遇到的技术总结下.最近在项目中要做一个在viewpager无限滚动图片的需求,其实百度一下有好多的例子,但是大部分虽然实现了, ...

  2. 基于HTML5+CSS3的图片旋转、无限滚动、文字跳动特效

    本文分享几种基于HTML5+CSS3实现的一些动画特效:图片旋转.无限滚动.文字跳动;实现起来均比较容易,动手来试试! 一.图片旋转 效果图如下: 这个效果实现起来其实并不困难.代码清单如下: < ...

  3. WPF 图片浏览 伪3D效果

    原文:WPF 图片浏览 伪3D效果 首先上效果图: 因项目要求,需要把图片以"好看"."炫"的效果展示出来,特地研究了一下WPF关于3D方面的制作,奈何最终成果 ...

  4. UIScrollView实现图片轮播器的无限滚动

    简介 在现在的一些App中常常见到图片轮播器,一般用于展示广告.新闻等数据,在iOS内并没有现成的控件直接实现这种功能,但是通过UIScrollView的允许分页设置,可以实现滚动轮播的功能. 轮播原 ...

  5. 10款无限滚动自动翻页jquery插件

    2012年3月29日 无限滚动自动翻页可以说是web2.0时代的一项堪称伟大的技术,它让我们在浏览页面的时候只需要把滚动条拉到网页底部就能自动显示下一页的 结果,改变了一直以来只能通过点击下一页来翻页 ...

  6. 【转】超酷的 mip-infinitescroll 无限滚动(无限下拉)

    写在前面 无限滚动技术(又叫做无限下拉技术)被广泛应用于新闻类,图片预览类网站.对用户来讲,使用无限滚动的页面有源源不断的信息可以预览,增加用户在页面的停留时长.技术上原理也很简单,在页面加载时加载一 ...

  7. Kinect 开发 —— 图片浏览

    总体思路 首先运用WPF编写一个简单的支持多点触控的图片浏览程序,这方面您可以参看MSDN上的这篇文章,上面有代码,可能需要FQ才能下载.中文的话,您可以参考Gnie同学关于在WPF上面多点触屏(Mu ...

  8. js鼠标滚轮滚动图片切换效果

    效果体验网址:http://keleyi.com/keleyi/phtml/image/12.htm HTML文件代码: <!DOCTYPE html PUBLIC "-//W3C// ...

  9. Infinite Scroll - jQuery & WP 无限滚动插件

    无限滚动(Infinite Scroll)也称为自动分页.滚动分页和无限分页.常用在图片.文章或其它列表形式的网页中,用来在滚动网页的时候自动加载下一页的内容.Infinite Scroll  这款  ...

  10. [Unity3D插件]2dtoolkit系列二 动画精灵的创建以及背景图的无限滚动

    经过昨天2dtoolkit系列教程一的推出,感觉对新手还有有一定的启发作用,引导学习使用unity 2dToolKit插件的使用过程,今天继续系列二——动画精灵的创建,以及背景图的无限循环滚动,在群里 ...

随机推荐

  1. vue侦听器 - watch

    使用watch来侦听data中数据的变化,watch中的属性一定是data 中已经存在的数据. 当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,只有data中的数据才能够 ...

  2. 童年神机小霸王(七) Mapper

    首发公号:Rand_cs,求关注支持 Mapper mapper,这个概念来源于 memory mapping,又叫做 Memory Management Circuit,它是解决地址映射的一种电路, ...

  3. Easysearch 压缩功能的显著提升:从 8.7GB 到 1.4GB

    引言 在海量数据的存储和处理中,索引膨胀率是一个不可忽视的关键指标.它直接影响了存储成本和查询性能.近期,Easysearch 在这方面取得了显著的进展,其压缩功能的效果远超过了之前的版本.本文将详细 ...

  4. The model backing the 'MainDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).

    The model backing the 'MainDbContext' context has changed since the database was created. Consider u ...

  5. redis数据持久化篇

    为什么需要持久化 Redis是个基于内存的数据库. 那服务一旦宕机,内存中的数据将全部丢失. 通常的解决方案是从后端数据库恢复这些数据,但后端数据库有性能瓶颈 如果是大数据量的恢复,1.会对数据库带来 ...

  6. golang reflect 反射机制的使用场景

    Go语言中的 reflect 包提供了运行时反射机制,允许程序在运行时检查和操作任意对象的数据类型和值. 以下是 reflect 包的一些典型使用场景: 1. 动态类型判断与转换:当需要处理多种类型的 ...

  7. org.springframework.beans.BeanUtils属性赋值 Date类型处理转换为LocalDateTime, Date不能直接赋值给LocalDateTime

    Date createTime = book.getCreateTime(); Date updateTime = book.getUpdateTime(); //属性值处理 BeanUtils.co ...

  8. ZynqMP PL固件通过U-BOOT从指定位置加载FPGA BIT

    原因 PL固件可能经常修改,而BOOT.BIN和文件系统.内核实际上基本不会变,在一个平台上可以用同一份.如果每次修改都要重新打包PL 固件到BOOT.BIN,操作起来非常麻烦.所以希望PL 的固件可 ...

  9. 【iOS】Class对构造简洁代码很有帮助

    (这到底取的是什么标题啊) 首先先看这段代码(有删减) @property (nonatomic, copy)NSMutableArray <NSMutableArray *>*datas ...

  10. 07-Linux文件权限管理

    文件的类型 Linux的哲学思想:一切皆文件. Linux的文件分为多种类型. 可以通过ll命令查看文件的类型: ll #输出: -rw-------. 1 root root 1266 2月 29 ...