简易的AutoPlayCarousel 轮播控件
原理是使用StackPanel 的margin属性的偏移来实现轮播的效果
废话不多说直接上代码
AutoPlayCarousel核心代码
[ContentProperty(nameof(Children))] [TemplatePart(Name = "PART_StackPanel", Type = typeof(StackPanel))] public class AutoPlayCarousel : Control { #region Identifier /// <summary> /// 视图区域 /// </summary> private StackPanel _stkMain; /// <summary> /// /// </summary> private DispatcherTimer _dtAutoPlay; #endregion #region Constructor static AutoPlayCarousel() { DefaultStyleKeyProperty.OverrideMetadata(typeof(AutoPlayCarousel), new FrameworkPropertyMetadata(typeof(AutoPlayCarousel))); } public AutoPlayCarousel() { Loaded += AutoScrollCarousel_Loaded; SizeChanged += AutoScrollCarousel_SizeChanged; } #endregion #region RoutedEvent public static readonly RoutedEvent IndexChangedEvent = EventManager.RegisterRoutedEvent("IndexChanged", RoutingStrategy.Bubble, typeof(IndexChangedEventHandler), typeof(AutoPlayCarousel)); public event IndexChangedEventHandler IndexChanged { add => AddHandler(IndexChangedEvent, value); remove => RemoveHandler(IndexChangedEvent, value); } void RaiseIndexChanged(int newValue) { var arg = new IndexChangedEventArgs(newValue, IndexChangedEvent); RaiseEvent(arg); } #endregion #region Property /// <summary> /// get the children collection. /// </summary> public ObservableCollection<FrameworkElement> Children { get => (ObservableCollection<FrameworkElement>)GetValue(ChildrenProperty); private set => SetValue(ChildrenProperty, value); } public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register("Children", typeof(ObservableCollection<FrameworkElement>), typeof(AutoPlayCarousel), new PropertyMetadata(new ObservableCollection<FrameworkElement>())); /// <summary> /// get or set orientation /// </summary> public Orientation Orientation { get => (Orientation)GetValue(OrientationProperty); set => SetValue(OrientationProperty, value); } public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(AutoPlayCarousel), new PropertyMetadata(Orientation.Horizontal)); /// <summary> /// get or set index /// </summary> public int Index { get => (int)GetValue(IndexProperty); set => SetValue(IndexProperty, value); } public static readonly DependencyProperty IndexProperty = DependencyProperty.Register("Index", typeof(int), typeof(AutoPlayCarousel), new PropertyMetadata(0, OnIndexChanged)); /// <summary> /// Gets or sets animation duration. /// </summary> public TimeSpan AnimateDuration { get => (TimeSpan)GetValue(AnimateDurationProperty); set => SetValue(AnimateDurationProperty, value); } public static readonly DependencyProperty AnimateDurationProperty = DependencyProperty.Register("AnimateDuration", typeof(TimeSpan), typeof(AutoPlayCarousel), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); /// <summary> /// Gets or sets recyclable. /// </summary> public bool Recyclable { get => (bool)GetValue(RecyclableProperty); set => SetValue(RecyclableProperty, value); } public static readonly DependencyProperty RecyclableProperty = DependencyProperty.Register("Recyclable", typeof(bool), typeof(AutoPlayCarousel), new PropertyMetadata(false)); public TimeSpan AutoPlayInterval { get => (TimeSpan)GetValue(AutoPlayIntervalProperty); set => SetValue(AutoPlayIntervalProperty, value); } public static readonly DependencyProperty AutoPlayIntervalProperty = DependencyProperty.Register("AutoPlayInterval", typeof(TimeSpan), typeof(AutoPlayCarousel), new PropertyMetadata(OnAutoPlayIntervalChanged)); #endregion #region Event Handler public override void OnApplyTemplate() { base.OnApplyTemplate(); _stkMain = GetTemplateChild("PART_StackPanel") as StackPanel; } private void AutoScrollCarousel_SizeChanged(object sender, SizeChangedEventArgs e) { foreach (FrameworkElement children in Children) { children.Width = ActualWidth; children.Height = ActualHeight; } } private void AutoScrollCarousel_Loaded(object sender, RoutedEventArgs e) { if (Children == null) return; Loaded -= AutoScrollCarousel_Loaded; foreach (FrameworkElement child in Children) { child.Width = ActualWidth; child.Height = ActualHeight; } } private static void OnAutoPlayIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var autoScrollCarousel = d as AutoPlayCarousel; autoScrollCarousel?.RestartAutoPlayTimer(); } private void DispatcherTimerAutoPlay_Tick(object sender, EventArgs e) { Index++; } private static void OnIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var autoScrollCarousel = d as AutoPlayCarousel; if (autoScrollCarousel == null || !autoScrollCarousel.IsLoaded) return; var targetIndex = 0; if (!autoScrollCarousel.Recyclable) targetIndex = autoScrollCarousel.Index > (autoScrollCarousel.Children.Count - 1) ? autoScrollCarousel.Children.Count - 1 : (autoScrollCarousel.Index < 0 ? 0 : autoScrollCarousel.Index); else targetIndex = autoScrollCarousel.Index > (autoScrollCarousel.Children.Count - 1) ? 0 : (autoScrollCarousel.Index < 0 ? autoScrollCarousel.Children.Count - 1 : autoScrollCarousel.Index); if (targetIndex != autoScrollCarousel.Index) { autoScrollCarousel.Index = targetIndex; return; } autoScrollCarousel.ResetAutoPlayTimer(); if (autoScrollCarousel.Orientation == Orientation.Vertical) { autoScrollCarousel._stkMain.BeginAnimation(StackPanel.MarginProperty, new ThicknessAnimation() { To = new Thickness(0, -1 * autoScrollCarousel.ActualHeight * autoScrollCarousel.Index, 0, 0), Duration = autoScrollCarousel.AnimateDuration, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } }); } else { autoScrollCarousel._stkMain.BeginAnimation(StackPanel.MarginProperty, new ThicknessAnimation() { To = new Thickness(-1 * autoScrollCarousel.ActualWidth * autoScrollCarousel.Index, 0, 0, 0), Duration = autoScrollCarousel.AnimateDuration, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } }); } autoScrollCarousel.RaiseIndexChanged(targetIndex); } #endregion #region Function private void RestartAutoPlayTimer() { if (_dtAutoPlay != null) { _dtAutoPlay.Stop(); } if (AutoPlayInterval.TotalSeconds != 0) { _dtAutoPlay = new DispatcherTimer() { Interval = AutoPlayInterval, }; _dtAutoPlay.Tick += DispatcherTimerAutoPlay_Tick; _dtAutoPlay.Start(); } } private void ResetAutoPlayTimer() { if (_dtAutoPlay != null) { _dtAutoPlay.Stop(); _dtAutoPlay.Start(); } } #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);
AutoPlayCarousel默认的样式
<sys:Double x:Key="DefaultFontSize">14</sys:Double>
<sys:Boolean x:Key="DefaultSnapsToDevicePixels">false</sys:Boolean>
<Style TargetType="{x:Type local:AutoPlayCarousel}"> <Setter Property="SnapsToDevicePixels" Value="{StaticResource DefaultSnapsToDevicePixels}" /> <Setter Property="FontSize" Value="{StaticResource DefaultFontSize}" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:AutoPlayCarousel}"> <StackPanel x:Name="PART_StackPanel" Orientation="{TemplateBinding Orientation}"> <ItemsControl x:Name="PART_ItemsControl" ItemsSource="{TemplateBinding Children}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Orientation="{Binding Orientation,RelativeSource={RelativeSource AncestorType=local:AutoPlayCarousel}}"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style>
页面使用
<customControl:AutoPlayCarousel x:Name="Carousel" AutoPlayInterval="0:0:3" Recyclable="True" Height="1080"> <Grid Background="Red" > <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" > <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" > <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" > <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:AutoPlayCarousel>
效果如下: