上一篇文章有写到 自动轮播的控件 简易的AutoPlayCarousel 轮播控件 - 黄高林 - 博客园 (cnblogs.com)
本章是基于自动轮播的一种衍生,通过拖拽鼠标进切换
直接上代码
DragDropCarousel核心代码
[ContentProperty(nameof(ItemSources))]
[TemplatePart(Name = "PART_StackPanel", Type = typeof(StackPanel))]
public class DragDropCarousel : Control
{
#region 字段
private StackPanel _stkMain;
///
private bool _isMouseDown;
///
private bool _isAnimating;
///
private Point _lastDownPoint;
///
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);
///
public class MoveElementAttach : DependencyObject
{
///
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);
}
默认样式
<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>
界面的使用
<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" 来标记那些控件支持拖拽,哪些不支持拖拽,这种只是临时解决方案,其实还有其他的解决方案。后续我如果要用到再持续更新
手机扫一扫
移动阅读更方便
你可能感兴趣的文章