Fork me on GitHub

简易的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>

  效果如下:

 

posted @ 2022-08-01 19:37  黄高林  阅读(143)  评论(0编辑  收藏  举报