简易的DragDropCarousel 拖拽轮播控件
上一篇文章有写到 自动轮播的控件 简易的AutoPlayCarousel 轮播控件 - 黄高林 - 博客园 (cnblogs.com)
本章是基于自动轮播的一种衍生,通过拖拽鼠标进切换
直接上代码
DragDropCarousel核心代码
[ContentProperty(nameof(ItemSources))]
[TemplatePart(Name = "PART_StackPanel", Type = typeof(StackPanel))]
public class DragDropCarousel : Control
{
#region 字段
private StackPanel _stkMain;
/// <summary>
/// 标记是否点击
/// </summary>
private bool _isMouseDown;
/// <summary>
/// 标记是否动画中,动画过程中不允许操作
/// </summary>
private bool _isAnimating;
/// <summary>
/// 最后一次点击的位置
/// </summary>
private Point _lastDownPoint;
/// <summary>
/// 移动中
/// </summary>
private bool _isMoving;
#endregion #region 构造
static DragDropCarousel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DragDropCarousel), new FrameworkPropertyMetadata(typeof(DragDropCarousel)));
}
public DragDropCarousel()
{
Loaded += Carousel_Loaded;
SizeChanged += Carousel_SizeChanged;
}
#endregion #region 属性
/// <summary>
/// 子控件集合
/// </summary>
public ObservableCollection<FrameworkElement> ItemSources
{
get => (ObservableCollection<FrameworkElement>)GetValue(ItemSourcesProperty);
private set => SetValue(ItemSourcesProperty, value);
} public static readonly DependencyProperty ItemSourcesProperty =
DependencyProperty.Register("ItemSources", typeof(ObservableCollection<FrameworkElement>), typeof(DragDropCarousel), new PropertyMetadata(new ObservableCollection<FrameworkElement>()));
/// <summary>
/// 方向
/// </summary>
public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
} public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(DragDropCarousel), new PropertyMetadata(Orientation.Horizontal)); /// <summary>
/// 下标
/// </summary>
public int Index
{
get => (int)GetValue(IndexProperty);
set => SetValue(IndexProperty, value);
} public static readonly DependencyProperty IndexProperty =
DependencyProperty.Register("Index", typeof(int), typeof(DragDropCarousel), new PropertyMetadata(0)); /// <summary>
/// 抬起手时,滑动切页动画持续时间
/// </summary>
public TimeSpan AnimateDuration
{
get => (TimeSpan)GetValue(AnimateDurationProperty);
set => SetValue(AnimateDurationProperty, value);
} public static readonly DependencyProperty AnimateDurationProperty =
DependencyProperty.Register("AnimateDuration", typeof(TimeSpan), typeof(DragDropCarousel), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); /// <summary>
/// 移动阈值
/// </summary>
public double MoveThreshold
{
get => (double)GetValue(MoveThresholdProperty);
set => SetValue(MoveThresholdProperty, value);
} // Using a DependencyProperty as the backing store for MoveThreshold. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MoveThresholdProperty =
DependencyProperty.Register("MoveThreshold", typeof(double), typeof(DragDropCarousel), new PropertyMetadata(10d)); #endregion #region 对外方法
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_stkMain = GetTemplateChild("PART_StackPanel") as StackPanel;
}
#endregion #region Event Handler private void Carousel_SizeChanged(object sender, SizeChangedEventArgs e)
{
foreach (FrameworkElement children in ItemSources)
{
children.Width = ActualWidth;
children.Height = ActualHeight;
UnRegisterMouseEvent(children);
RegisterMouseEvent(children);
}
}
private void UnRegisterMouseEvent(FrameworkElement children)
{
children.MouseDown -= Children_MouseDown;
children.MouseMove -= Children_MouseMove;
children.MouseUp -= Children_MouseUp;
}
private void RegisterMouseEvent(FrameworkElement children)
{
children.MouseDown += Children_MouseDown;
children.MouseMove += Children_MouseMove;
children.MouseUp += Children_MouseUp;
} private void Children_MouseUp(object sender, MouseButtonEventArgs e)
{
if (!_isMouseDown || _isAnimating||!_isMoving)
{
_isMoving = false;
_isMouseDown = false;
return;
}
_isMoving=false;
_isMouseDown = false;
var targetIndex = GetTargetIndex(e, _lastDownPoint);
Index = targetIndex;
var newThickness = new Thickness(-1 * ActualWidth * targetIndex, 0, 0, 0);
var oldThickness = _stkMain.Margin;
var thicknessAnimation = new ThicknessAnimation()
{
From = oldThickness,
To = newThickness,
Duration = AnimateDuration,
EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut },
FillBehavior = FillBehavior.Stop,
};
thicknessAnimation.Completed += (s, args) =>
{
_isAnimating = false;
_stkMain.Margin = newThickness;
};
_isAnimating = true;
_stkMain.BeginAnimation(MarginProperty, thicknessAnimation);
} private int GetTargetIndex(MouseButtonEventArgs e, Point lastDownPoint)
{
var position = e.GetPosition(this);
var vector = position - lastDownPoint;
int targetIndex;
if (vector.X < 0)
{
targetIndex = Index + 1 > (ItemSources.Count - 1) ? Index : Index + 1;
}
else
{
targetIndex = Index - 1 >= 0 ? Index - 1 : 0;
}
return targetIndex;
} private void Children_MouseMove(object sender, MouseEventArgs e)
{ var position = e.GetPosition(this);
if (!IsCanMove(_lastDownPoint, position))
{
_stkMain.Margin = new Thickness((-1 * ActualWidth * Index), 0, 0, 0);
return;
}
var moveLength = _lastDownPoint.X - position.X;
_stkMain.Margin = new Thickness((-1 * ActualWidth * Index - moveLength), 0, 0, 0);
} private bool IsCanMove(Point lastPoint, Point currentPoint)
{
if (!_isMouseDown)
{
return false;
} //以下控制不允许超出边界,想要回弹效果,可以去掉这部分代码
var vector = currentPoint - lastPoint;
if (!_isMoving && Math.Abs(vector.X) <= MoveThreshold)
{
return false;
} _isMoving = true;
if (vector.X < 0)
{
return Index != ItemSources.Count - 1;
} return Index != 0; } private void Children_MouseDown(object sender, MouseButtonEventArgs e)
{
if (!(MoveElementAttach.GetAllowMove(e.Source as UIElement) ?? false) || _isAnimating)
{
_isMouseDown = false;
return;
}
_isMouseDown = true;
_lastDownPoint = e.GetPosition(this);
} private void Carousel_Loaded(object sender, RoutedEventArgs e)
{
if (ItemSources == null)
return;
Loaded -= Carousel_Loaded;
foreach (FrameworkElement child in ItemSources)
{
child.Width = ActualWidth;
child.Height = ActualHeight;
}
}
#endregion }
一些辅助的代码
public class IndexChangedEventArgs : RoutedEventArgs
{
public IndexChangedEventArgs(int currentIndex, RoutedEvent routedEvent) : base(routedEvent)
{
CurrentIndex = currentIndex;
} public int CurrentIndex { get; set; }
} public delegate void IndexChangedEventHandler(object sender, IndexChangedEventArgs e);
/// <summary>
/// 移动附加属性
/// </summary>
public class MoveElementAttach : DependencyObject
{
/// <summary>允许操作</summary>
public static readonly DependencyProperty AllowMoveProperty = DependencyProperty.RegisterAttached("AllowMove", typeof(bool), typeof(MoveElementAttach), new PropertyMetadata((PropertyChangedCallback)null)); public static bool? GetAllowMove(DependencyObject obj) => (bool?)obj?.GetValue(MoveElementAttach.AllowMoveProperty); public static void SetAllowMove(DependencyObject obj, bool? value) => obj.SetValue(MoveElementAttach.AllowMoveProperty, (object)value);
}
默认样式
<sys:Double x:Key="DefaultFontSize">14</sys:Double>
<sys:Boolean x:Key="DefaultSnapsToDevicePixels">false</sys:Boolean> <Style TargetType="{x:Type local:DragDropCarousel}">
<Setter Property="SnapsToDevicePixels" Value="{StaticResource DefaultSnapsToDevicePixels}" />
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DragDropCarousel}">
<StackPanel x:Name="PART_StackPanel" Orientation="Horizontal">
<ItemsControl x:Name="PART_ItemsControl" ItemsSource="{TemplateBinding ItemSources}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Orientation="{Binding Orientation,RelativeSource={RelativeSource AncestorType=local:DragDropCarousel}}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
界面的使用
<customControl:DragDropCarousel x:Name="Carousel"
Height="1080">
<Grid Background="Red" customControl:MoveElementAttach.AllowMove="True" >
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="500" Width="500" Background="Black"> <TextBlock Text="1" FontSize="20" Foreground="Wheat" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
</Grid>
<Grid Background="Green" customControl:MoveElementAttach.AllowMove="True" > <Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="500" Width="500" Background="Black"> <TextBlock Text="2" FontSize="20" Foreground="Wheat" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid> </Grid>
<Grid Background="Yellow" customControl:MoveElementAttach.AllowMove="True" >
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="500" Width="500" Background="Black"> <TextBlock Text="3" FontSize="20" Foreground="Wheat" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
</Grid>
<Grid Background="Blue" customControl:MoveElementAttach.AllowMove="True" >
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="500" Width="500" Background="Black"> <TextBlock Text="4" FontSize="20" Foreground="Wheat" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
</Grid>
</Grid>
</customControl:DragDropCarousel>
通过 customControl:MoveElementAttach.AllowMove="True" 来标记那些控件支持拖拽,哪些不支持拖拽,这种只是临时解决方案,其实还有其他的解决方案。后续我如果要用到再持续更新
简易的DragDropCarousel 拖拽轮播控件的更多相关文章
- WPF 控件库——轮播控件
WPF 控件库系列博文地址: WPF 控件库——仿制Chrome的ColorPicker WPF 控件库——仿制Windows10的进度条 WPF 控件库——轮播控件 WPF 控件库——带有惯性的Sc ...
- 第四十六篇、UICollectionView广告轮播控件
这是利用人的视觉错觉来实现无限轮播,UICollectionView 有很好的重用机制,这只是部分核心代码,后期还要继续完善和代码重构. #import <UIKit/UIKit.h> # ...
- Android之仿京东淘宝的自动无限轮播控件
在App的开发中,很多的时候都需要实现类似京东淘宝一样的自动无限轮播的广告栏,所以就自己写了一个,下面是我自定义控件的思路和过程. 一.自定义控件属性 新建自定义控件SliderLayout继承于Re ...
- Android图片轮播控件
Android广告图片轮播控件,支持无限循环和多种主题,可以灵活设置轮播样式.动画.轮播和切换时间.位置.图片加载框架等! 使用步骤 Step 1.依赖banner Gradle dependenci ...
- jquery轮播控件
网上找了一个轮播控件,效果不错,而且很容易改,需要的同学去下载咯 地址是:http://download.csdn.net/detail/jine515073/7704143
- Android-----------广告图片轮播控件
Banner广告图片轮播控件,支持无限循环和多种主题,可以灵活设置轮播样式.动画.轮播和切换时间.位置.图片加载框架等! 很多Android APP中都有广告栏,我也用过很多次了,特来写一篇博文. 先 ...
- 一起写一个Android图片轮播控件
注:本文提到的Android轮播控件Demo地址: Android图片轮播控件 1. 轮播控件的组成部分 我们以知乎日报Android客户端的轮播控件为例,分析一下轮播控件的主要组成: 首先我们要有用 ...
- Android 开发最牛的图片轮播控件,基本什么都包含了。
Android图片轮播控件 源码下载地址: Android 图片轮播 现在的绝大数app都有banner界面,实现循环播放多个广告图片和手动滑动循环等功能.因为ViewPager并不支持循环翻页, ...
- Android高仿京东淘宝自动无限循环轮播控件的实现思路和过程
在App的开发中,很多的时候都需要实现类似京东淘宝一样的自动无限轮播的广告栏,所以就自己写了一个,下面是我自定义控件的实现思路和过程. 一.自定义控件属性 新建自定义控件SliderLayout继承于 ...
随机推荐
- 腾讯QQ快捷登陆
腾讯QQ快捷 相关各语言对接qq快捷登录教程 [C#]QQ开放平台(QQ站外登录)_流程和源码示例 j2ee中实现QQ第三方登陆 web实现QQ第三方登录 asp.net网站接入QQ登录 php实现q ...
- 12.web基础与HTTP协议
web基础与HTTP协议 目录 web基础与HTTP协议 web基础 域名概述 HTML概述 HTML基本标签 HTML语法规则 HTML文件结构 头标签中常用标签 内容标签中常用标签 静态网页与动态 ...
- java中的方法重载(overload)
什么时候方法重载:当两个方法的功能是相似的,可以考虑使用方法重载.若两个方法根本没有关系,无必要使用方法重载. 什么时候代码会发生方法重载:三个条件:1,在同一个类中.2,方法名相同.3,参数列表相同 ...
- SAP创建XML 文件
TYPES: BEGIN OF xml_line_type, data(256) TYPE x, END OF xml_line_type, xml_tab_type TYPE TABLE OF xm ...
- 8.3 如何在Windows电脑安装Java开发环境(JDK)
下载 来到JDK官方下载界面,找到Java SE 8(简称JDK 8)后面的JDK下载,来到该界面,先同意协议,然后下载对应平台的JDK,我们这里下载Windows x64. 安装 双击安装就行了了, ...
- NC16618 [NOIP2008]排座椅
NC16618 [NOIP2008]排座椅 题目 题目描述 上课的时候总有一些同学和前后左右的人交头接耳,这是令小学班主任十分头疼的一件事情.不过,班主任小雪发现了一些有趣的现象,当同学们的座次确定下 ...
- .Net之延迟队列
介绍 具有队列的特性,再给它附加一个延迟消费队列消息的功能,也就是说可以指定队列中的消息在哪个时间点被消费. 使用场景 延时队列在项目中的应用还是比较多的,尤其像电商类平台: 订单成功后,在30分钟内 ...
- 强化学习-学习笔记9 | Multi-Step-TD-Target
这篇笔记依然属于TD算法的范畴.Multi-Step-TD-Target 是对 TD算法的改进. 9. Multi-Step-TD-Target 9.1 Review Sarsa & Q-Le ...
- Python 数据科学手册:读书笔记概论
为防止遗忘,在空闲时间将读书的笔记开始按照章节进行概括总结(2022.1.1): 第二章:NumPy 入门 第三章:Pandas 数据处理 第四章:Matplotlib 数据可视化 第五章:机器学习 ...
- 静态static关键字概述和静态static关键字修饰成员变量
static关键字 概述 关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属 于某个对象的.也就是说,既然属于类,就可以不靠创建对象来调用了 ...