123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.ComponentModel;
- using System.Linq;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Controls.Primitives;
- using System.Windows.Input;
- using System.Windows.Markup;
- using System.Windows.Threading;
- using HandyControl.Data;
- using HandyControl.Interactivity;
- using HandyControl.Tools;
- namespace HandyControl.Controls;
- /// <summary>
- /// 轮播控件
- /// </summary>
- [DefaultProperty("Items")]
- [ContentProperty("Items")]
- [TemplatePart(Name = ElementPanelPage, Type = typeof(Panel))]
- public class Carousel : SimpleItemsControl, IDisposable
- {
- #region Constants
- private const string ElementPanelPage = "PART_PanelPage";
- #endregion Constants
- #region Data
- private bool _isDisposed;
- private Panel _panelPage;
- private bool _appliedTemplate;
- private int _pageIndex = -1;
- private RadioButton _selectedButton;
- private DispatcherTimer _updateTimer;
- private readonly List<double> _widthList = new();
- private readonly Dictionary<object, CarouselItem> _entryDic = new();
- private bool _isRefresh;
- private IEnumerable _itemsSourceInternal;
- #endregion Data
- public override void OnApplyTemplate()
- {
- _appliedTemplate = false;
- _panelPage?.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonPages_OnClick));
- base.OnApplyTemplate();
- _panelPage = GetTemplateChild(ElementPanelPage) as Panel;
- if (!CheckNull()) return;
- _panelPage.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonPages_OnClick));
- _appliedTemplate = true;
- Update();
- }
- private void Update()
- {
- TimerSwitch(AutoRun);
- UpdatePageButtons(_pageIndex);
- }
- private bool CheckNull() => _panelPage != null;
- public static readonly DependencyProperty AutoRunProperty = DependencyProperty.Register(
- nameof(AutoRun), typeof(bool), typeof(Carousel), new PropertyMetadata(ValueBoxes.FalseBox, (o, args) =>
- {
- var ctl = (Carousel) o;
- ctl.TimerSwitch((bool) args.NewValue);
- }));
- public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register(
- nameof(Interval), typeof(TimeSpan), typeof(Carousel), new PropertyMetadata(TimeSpan.FromSeconds(2)));
- public static readonly DependencyProperty ExtendWidthProperty = DependencyProperty.Register(
- nameof(ExtendWidth), typeof(double), typeof(Carousel), new PropertyMetadata(ValueBoxes.Double0Box));
- public double ExtendWidth
- {
- get => (double) GetValue(ExtendWidthProperty);
- set => SetValue(ExtendWidthProperty, value);
- }
- public static readonly DependencyProperty IsCenterProperty = DependencyProperty.Register(
- nameof(IsCenter), typeof(bool), typeof(Carousel), new PropertyMetadata(ValueBoxes.FalseBox));
- public bool IsCenter
- {
- get => (bool) GetValue(IsCenterProperty);
- set => SetValue(IsCenterProperty, ValueBoxes.BooleanBox(value));
- }
- public static readonly DependencyProperty PageButtonStyleProperty = DependencyProperty.Register(
- nameof(PageButtonStyle), typeof(Style), typeof(Carousel), new PropertyMetadata(default(Style)));
- public Style PageButtonStyle
- {
- get => (Style) GetValue(PageButtonStyleProperty);
- set => SetValue(PageButtonStyleProperty, value);
- }
- public Carousel()
- {
- CommandBindings.Add(new CommandBinding(ControlCommands.Prev, ButtonPrev_OnClick));
- CommandBindings.Add(new CommandBinding(ControlCommands.Next, ButtonNext_OnClick));
- CommandBindings.Add(new CommandBinding(ControlCommands.Selected, ButtonPages_OnClick));
- Loaded += (s, e) => UpdatePageButtons();
- IsVisibleChanged += Carousel_IsVisibleChanged;
- }
- ~Carousel() => Dispose();
- public void Dispose()
- {
- if (_isDisposed) return;
- IsVisibleChanged -= Carousel_IsVisibleChanged;
- _updateTimer?.Stop();
- _isDisposed = true;
- GC.SuppressFinalize(this);
- }
- private void Carousel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
- {
- if (_updateTimer == null) return;
- if (IsVisible)
- {
- _updateTimer.Tick += UpdateTimer_Tick;
- _updateTimer.Start();
- }
- else
- {
- _updateTimer.Stop();
- _updateTimer.Tick -= UpdateTimer_Tick;
- }
- }
- /// <summary>
- /// 是否自动跳转
- /// </summary>
- public bool AutoRun
- {
- get => (bool) GetValue(AutoRunProperty);
- set => SetValue(AutoRunProperty, ValueBoxes.BooleanBox(value));
- }
- /// <summary>
- /// 跳转时间间隔
- /// </summary>
- public TimeSpan Interval
- {
- get => (TimeSpan) GetValue(IntervalProperty);
- set => SetValue(IntervalProperty, value);
- }
- /// <summary>
- /// 页码
- /// </summary>
- 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);
- }
- }
- /// <summary>
- /// 计时器开关
- /// </summary>
- private void TimerSwitch(bool run)
- {
- if (!_appliedTemplate) return;
- if (_updateTimer != null)
- {
- _updateTimer.Tick -= UpdateTimer_Tick;
- _updateTimer.Stop();
- _updateTimer = null;
- }
- if (!run) return;
- _updateTimer = new DispatcherTimer
- {
- Interval = Interval
- };
- _updateTimer.Tick += UpdateTimer_Tick;
- _updateTimer.Start();
- }
- private void UpdateTimer_Tick(object sender, EventArgs e)
- {
- if (IsMouseOver) return;
- PageIndex++;
- }
- /// <summary>
- /// 更新页按钮
- /// </summary>
- public void UpdatePageButtons(int index = -1)
- {
- if (!CheckNull()) return;
- if (!_appliedTemplate) return;
- var count = Items.Count;
- _widthList.Clear();
- _widthList.Add(0);
- var width = .0;
- foreach (FrameworkElement item in ItemsHost.Children)
- {
- item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
- width += item.DesiredSize.Width;
- _widthList.Add(width);
- }
- ItemsHost.Width = _widthList.Last() + ExtendWidth;
- _panelPage.Children.Clear();
- for (var i = 0; i < count; i++)
- {
- _panelPage.Children.Add(new RadioButton
- {
- Style = PageButtonStyle
- });
- }
- if (index == -1 && count > 0) index = 0;
- if (index >= 0 && index < count)
- {
- if (_panelPage.Children[index] is RadioButton button)
- {
- button.IsChecked = true;
- button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button));
- UpdateItemsPosition();
- }
- }
- }
- /// <summary>
- /// 更新项的位置
- /// </summary>
- private void UpdateItemsPosition()
- {
- if (!CheckNull()) return;
- if (!_appliedTemplate) return;
- if (Items.Count == 0) return;
- if (!IsCenter)
- {
- ItemsHost.BeginAnimation(MarginProperty,
- AnimationHelper.CreateAnimation(new Thickness(-_widthList[PageIndex], 0, 0, 0)));
- }
- else
- {
- var ctl = (FrameworkElement) ItemsHost.Children[PageIndex];
- var ctlWidth = ctl.DesiredSize.Width;
- ItemsHost.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 void ButtonPages_OnClick(object sender, RoutedEventArgs e)
- {
- if (!CheckNull()) return;
- _selectedButton = e.OriginalSource as RadioButton;
- 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++;
- protected override DependencyObject GetContainerForItemOverride() => new CarouselItem();
- protected override bool IsItemItsOwnContainerOverride(object item) => item is CarouselItem;
- private void ClearItems()
- {
- ItemsHost?.Children.Clear();
- _entryDic.Clear();
- }
- private void RemoveItem(object item)
- {
- if (_entryDic.TryGetValue(item, out var entry))
- {
- ItemsHost.Children.Remove(entry);
- Items.Remove(item);
- _entryDic.Remove(item);
- }
- }
- protected override void Refresh()
- {
- if (ItemsHost == null) return;
- _entryDic.Clear();
- _isRefresh = true;
- foreach (var item in Items)
- {
- AddItem(item);
- }
- _isRefresh = false;
- }
- private void AddItem(object item) => InsertItem(_entryDic.Count, item);
- private void InsertItem(int index, object item)
- {
- if (ItemsHost == null)
- {
- Items.Insert(index, item);
- _entryDic.Add(item, null);
- }
- else
- {
- DependencyObject container;
- if (IsItemItsOwnContainerOverride(item))
- {
- container = item as DependencyObject;
- }
- else
- {
- container = GetContainerForItemOverride();
- PrepareContainerForItemOverride(container, item);
- }
- if (container is CarouselItem element)
- {
- element.Style = ItemContainerStyle;
- _entryDic[item] = element;
- ItemsHost.Children.Insert(index, element);
- if (IsLoaded && !_isRefresh && _itemsSourceInternal != null)
- {
- Items.Insert(index, item);
- }
- }
- }
- }
- protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
- {
- if (_itemsSourceInternal != null)
- {
- if (_itemsSourceInternal is INotifyCollectionChanged s)
- {
- s.CollectionChanged -= InternalCollectionChanged;
- }
- Items.Clear();
- ClearItems();
- }
- _itemsSourceInternal = newValue;
- if (_itemsSourceInternal != null)
- {
- if (_itemsSourceInternal is INotifyCollectionChanged s)
- {
- s.CollectionChanged += InternalCollectionChanged;
- }
- foreach (var item in _itemsSourceInternal)
- {
- AddItem(item);
- }
- }
- }
- private void InternalCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
- {
- if (ItemsHost == null) return;
- if (e.Action == NotifyCollectionChangedAction.Reset)
- {
- if (_entryDic.Count == 0) return;
- ClearItems();
- Items.Clear();
- return;
- }
- if (e.OldItems != null)
- {
- foreach (var item in e.OldItems)
- {
- RemoveItem(item);
- }
- }
- if (e.NewItems != null)
- {
- var count = 0;
- foreach (var item in e.NewItems)
- {
- var index = e.NewStartingIndex + count++;
- InsertItem(index, item);
- }
- }
- }
- protected override void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
- {
- if (_itemsSourceInternal != null) return;
- InternalCollectionChanged(sender, e);
- }
- protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
- {
- }
- protected override void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e)
- {
- }
- }
|