diff --git a/HandyControl/Controls/Carousel.xaml b/HandyControl/Controls/Carousel.xaml
new file mode 100644
index 00000000..1281770d
--- /dev/null
+++ b/HandyControl/Controls/Carousel.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HandyControl/Controls/Carousel.xaml.cs b/HandyControl/Controls/Carousel.xaml.cs
new file mode 100644
index 00000000..c1cbde80
--- /dev/null
+++ b/HandyControl/Controls/Carousel.xaml.cs
@@ -0,0 +1,258 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Markup;
+using System.Windows.Media;
+using System.Windows.Threading;
+using HandyControl.Tools;
+
+namespace HandyControl.Controls
+{
+ ///
+ /// Carousel.xaml 的交互逻辑
+ ///
+ [DefaultProperty("Items")]
+ [ContentProperty("Items")]
+ public partial class Carousel
+ {
+ public static readonly DependencyProperty AutoRunProperty = DependencyProperty.Register(
+ "AutoRun", typeof(bool), typeof(Carousel), new PropertyMetadata(default(bool), (o, args) =>
+ {
+ var ctl = (Carousel)o;
+ ctl.TimerSwitch((bool)args.NewValue);
+ }));
+
+ public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register(
+ "Interval", typeof(TimeSpan), typeof(Carousel), new PropertyMetadata(TimeSpan.FromSeconds(2)));
+
+ public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
+ "ItemsSource", typeof(IEnumerable), typeof(Carousel), new PropertyMetadata(default(IEnumerable),
+ (o, args) =>
+ {
+ var ctl = (Carousel)o;
+ ctl.UpdatePageButtons();
+ }));
+
+ public static readonly DependencyProperty ExtendWidthProperty = DependencyProperty.Register(
+ "ExtendWidth", typeof(double), typeof(Carousel), new PropertyMetadata(default(double)));
+
+ public double ExtendWidth
+ {
+ get => (double)GetValue(ExtendWidthProperty);
+ set => SetValue(ExtendWidthProperty, value);
+ }
+
+ public static readonly DependencyProperty IsCenterProperty = DependencyProperty.Register(
+ "IsCenter", typeof(bool), typeof(Carousel), new PropertyMetadata(default(bool)));
+
+ public bool IsCenter
+ {
+ get => (bool)GetValue(IsCenterProperty);
+ set => SetValue(IsCenterProperty, value);
+ }
+
+ private int _pageIndex = -1;
+
+ private Button _selectedButton;
+
+ private DispatcherTimer _updateTimer;
+
+ private readonly List _widthList = new List();
+
+ public Carousel()
+ {
+ InitializeComponent();
+
+ Loaded += (s, e) => UpdatePageButtons();
+ }
+
+ ///
+ /// 是否自动跳转
+ ///
+ public bool AutoRun
+ {
+ get => (bool)GetValue(AutoRunProperty);
+ set => SetValue(AutoRunProperty, value);
+ }
+
+ ///
+ /// 跳转时间间隔
+ ///
+ public TimeSpan Interval
+ {
+ get => (TimeSpan)GetValue(IntervalProperty);
+ set => SetValue(IntervalProperty, value);
+ }
+
+ ///
+ /// 页码
+ ///
+ public int PageIndex
+ {
+ get => _pageIndex;
+ set
+ {
+ if (Items.Count == 0) return;
+ if (_pageIndex == value) return;
+ if (value < 0)
+ _pageIndex = Items.Count - 1;
+ else if (value >= Items.Count)
+ _pageIndex = 0;
+ else
+ _pageIndex = value;
+ UpdatePageButtons(_pageIndex);
+ }
+ }
+
+ public IEnumerable ItemsSource
+ {
+ get => (IEnumerable)GetValue(ItemsSourceProperty);
+ set => SetValue(ItemsSourceProperty, value);
+ }
+
+ public ItemCollection Items => ListBoxMain.Items;
+
+ ///
+ /// 计时器开关
+ ///
+ private void TimerSwitch(bool run)
+ {
+ if (run)
+ {
+ _updateTimer = new DispatcherTimer
+ {
+ Interval = Interval
+ };
+ _updateTimer.Tick += UpdateTimer_Tick;
+ _updateTimer.Start();
+ }
+ else
+ {
+ _updateTimer.Tick -= UpdateTimer_Tick;
+ _updateTimer.Stop();
+ _updateTimer = null;
+ }
+ }
+
+ private void UpdateTimer_Tick(object sender, EventArgs e)
+ {
+ if (IsMouseOver) return;
+ PageIndex++;
+ }
+
+ ///
+ /// 更新页按钮
+ ///
+ public void UpdatePageButtons(int? index = null)
+ {
+ if (index == null)
+ {
+ var count = Items.Count;
+ _widthList.Clear();
+ _widthList.Add(0);
+ var width = .0;
+ foreach (FrameworkElement item in ListBoxMain.Items)
+ {
+ width += item.Width + item.Margin.Left + item.Margin.Right;
+ _widthList.Add(width);
+ }
+
+ ListBoxMain.Width = _widthList.Last() + ExtendWidth;
+ PanelPage.Children.Clear();
+ for (var i = 0; i < count; i++)
+ PanelPage.Children.Add(CreatePateButton());
+ if (count > 0)
+ {
+ var button = PanelPage.Children[0];
+ button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button));
+ }
+ }
+ else
+ {
+ if (index >= 0 && index < PanelPage.Children.Count)
+ {
+ var button = PanelPage.Children[(int)index];
+ button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button));
+ UpdateItemsPosition();
+ }
+ }
+ }
+
+ ///
+ /// 更新项的位置
+ ///
+ private void UpdateItemsPosition()
+ {
+ if (Items.Count == 0 || PageIndex == -1) return;
+ if (!IsCenter)
+ {
+ ListBoxMain.BeginAnimation(MarginProperty,
+ AnimationHelper.CreateAnimation(new Thickness(-_widthList[PageIndex], 0, 0, 0)));
+ }
+ else
+ {
+ var ctl = (FrameworkElement)Items[PageIndex];
+ var ctlWidth = ctl.Width + ctl.Margin.Left + ctl.Margin.Right;
+ ListBoxMain.BeginAnimation(MarginProperty,
+ AnimationHelper.CreateAnimation(
+ new Thickness(-_widthList[PageIndex] + (ActualWidth - ctlWidth) / 2, 0, 0, 0)));
+ }
+ }
+
+ protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
+ {
+ base.OnRenderSizeChanged(sizeInfo);
+ UpdateItemsPosition();
+ }
+
+ ///
+ /// 创建页按钮
+ ///
+ ///
+ private Button CreatePateButton()
+ {
+ var button = new Button
+ {
+ Style = TryFindResource("ButtonOpacityStyle") as Style,
+ Content = new Border
+ {
+ Width = 10,
+ Height = 10,
+ CornerRadius = new CornerRadius(5),
+ Background = Brushes.White,
+ Margin = new Thickness(5, 0, 5, 0),
+ BorderThickness = new Thickness(1),
+ BorderBrush = TryFindResource("PrimaryBrush") as Brush
+ }
+ };
+ return button;
+ }
+
+ private void ButtonPages_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (_selectedButton != null && _selectedButton.Content is Border borderOri)
+ borderOri.Background = Brushes.White;
+ _selectedButton = e.OriginalSource as Button;
+ if (_selectedButton != null && _selectedButton.Content is Border border)
+ border.Background = TryFindResource("PrimaryBrush") as Brush;
+ var index = PanelPage.Children.IndexOf(_selectedButton);
+ if (index != -1)
+ PageIndex = index;
+ }
+
+ private void ButtonPrev_OnClick(object sender, RoutedEventArgs e)
+ {
+ PageIndex--;
+ }
+
+ private void ButtonNext_OnClick(object sender, RoutedEventArgs e)
+ {
+ PageIndex++;
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandyControl/HandyControl.csproj b/HandyControl/HandyControl.csproj
index d8a19cdc..bec70150 100644
--- a/HandyControl/HandyControl.csproj
+++ b/HandyControl/HandyControl.csproj
@@ -31,6 +31,9 @@
4
+
+ Carousel.xaml
+
ColorPicker.xaml
@@ -102,6 +105,10 @@
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
@@ -162,6 +169,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
MSBuild:Compile
Designer
@@ -186,6 +197,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/HandyControl/Themes/Styles/Base/ListBoxBaseStyle.xaml b/HandyControl/Themes/Styles/Base/ListBoxBaseStyle.xaml
new file mode 100644
index 00000000..ba965654
--- /dev/null
+++ b/HandyControl/Themes/Styles/Base/ListBoxBaseStyle.xaml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HandyControl/Themes/Styles/ListBox.xaml b/HandyControl/Themes/Styles/ListBox.xaml
new file mode 100644
index 00000000..6fab421d
--- /dev/null
+++ b/HandyControl/Themes/Styles/ListBox.xaml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HandyControl/Themes/Styles/Style.xaml b/HandyControl/Themes/Styles/Style.xaml
index 42d4f76e..46b6e18e 100644
--- a/HandyControl/Themes/Styles/Style.xaml
+++ b/HandyControl/Themes/Styles/Style.xaml
@@ -20,11 +20,11 @@
-
-->
+
diff --git a/HandyControlDemo/HandyControlDemo.csproj b/HandyControlDemo/HandyControlDemo.csproj
index ee7f0f79..e50567a1 100644
--- a/HandyControlDemo/HandyControlDemo.csproj
+++ b/HandyControlDemo/HandyControlDemo.csproj
@@ -73,5 +73,12 @@
HandyControl
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HandyControlDemo/MainWindow.xaml b/HandyControlDemo/MainWindow.xaml
index 8da76a79..a15f8e15 100644
--- a/HandyControlDemo/MainWindow.xaml
+++ b/HandyControlDemo/MainWindow.xaml
@@ -376,7 +376,7 @@
-
+