WPF自定义控件一:StackPanel 控件轮播
实现效果
带定时器的轮播图
using引用
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
自定义Control
/// <summary> /// 轮播控件 /// </summary> [ContentProperty(nameof(Children))] public class CarRoundPlay:Control { private StackPanel stackPanel; private DispatcherTimer _DtAutoPlay; static CarRoundPlay() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CarRoundPlay), new FrameworkPropertyMetadata(typeof(CarRoundPlay))); } /// <summary> /// 构造方法 /// </summary> public CarRoundPlay() { Children = new ObservableCollection<UIElement>(); Loaded += CarRoundPlay_Load; SizeChanged += CarRoundPlay_Changed; } /// <summary> /// 大小发生改变时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void CarRoundPlay_Changed(object sender, SizeChangedEventArgs e) { foreach (FrameworkElement child in Children) { child.Width = ActualWidth; child.Height = ActualHeight; } } /// <summary> /// 控件初始化 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void CarRoundPlay_Load(object sender, RoutedEventArgs e) { //判断子集合是否为空 if (Children == null) return; //便利子集布局并将当前容器宽高赋予子集布局控件 foreach (FrameworkElement child in Children) { child.Width = ActualWidth; child.Height = ActualHeight; } } public override void OnApplyTemplate() { base.OnApplyTemplate(); //获取当前显示布局控件 stackPanel = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(this, 0), 0) as StackPanel; } /// <summary> /// 图片大小发生改变时 /// </summary> /// <param name="d"></param> /// <param name="e"></param> private static void OnIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var carousel = d as CarRoundPlay; if (!carousel.IsLoaded) return; var targetIndex = 0; if (!carousel.Recyclable) targetIndex = carousel.Index > (carousel.Children.Count - 1) ? carousel.Children.Count - 1 : (carousel.Index < 0 ? 0 : carousel.Index); else targetIndex = carousel.Index > (carousel.Children.Count - 1) ? 0 : (carousel.Index < 0 ? carousel.Children.Count - 1 : carousel.Index); if (targetIndex != carousel.Index) { carousel.Index = targetIndex; return; } carousel.ResetAutoPlayTimer(); ///判断控件布局触发动画 if (carousel.Orientation == Orientation.Vertical) { carousel.stackPanel.BeginAnimation(StackPanel.MarginProperty, new ThicknessAnimation() { To = new Thickness(0, -1 * carousel.ActualHeight * carousel.Index, 0, 0), Duration = carousel.AnimateDuration, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } }); } else { carousel.stackPanel.BeginAnimation(StackPanel.MarginProperty, new ThicknessAnimation() { To = new Thickness(-1 * carousel.ActualWidth * carousel.Index, 0, 0, 0), Duration = carousel.AnimateDuration, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } }); } carousel.RaiseIndexChanged(targetIndex); } private static void OnAutoPlayIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var carousel = d as CarRoundPlay; carousel.RestartAutoPlayTimer(); } /// <summary> /// 定时切换 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DispatcherTimerAutoPlay_Tick(object sender, EventArgs e) { Index++; } public T FindFirstVisualChild<T>(DependencyObject obj, string childName) where T : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is T && child.GetValue(NameProperty).ToString() == childName) { return (T)child; } else { T childOfChild = FindFirstVisualChild<T>(child, childName); if (childOfChild != null) { return childOfChild; } } } return null; } #region 公共 /// <summary> /// 子集合控件 /// </summary> public ObservableCollection<UIElement> Children { get { return (ObservableCollection<UIElement>)GetValue(ChildrenProperty); } private set { SetValue(ChildrenProperty, value); } } public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register("Children", typeof(ObservableCollection<UIElement>), typeof(CarRoundPlay)); /// <summary> /// 布局方向 /// </summary> public Orientation Orientation { get { return (Orientation)GetValue( OrientationProperty); } set { SetValue( OrientationProperty, value); } } // Using a DependencyProperty as the backing store for Orientation. This enables animation, styling, binding, etc... public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(CarRoundPlay), new PropertyMetadata(Orientation.Horizontal)); /// <summary> /// /// </summary> public int Index { get { return (int)GetValue(IndexProperty); } set { SetValue(IndexProperty, value); } } public static readonly DependencyProperty IndexProperty = DependencyProperty.Register("Index", typeof(int), typeof(CarRoundPlay), new PropertyMetadata(0, OnIndexChanged)); /// <summary> /// 动画耗时 /// </summary> public TimeSpan AnimateDuration { get { return (TimeSpan)GetValue(AnimateDurationProperty); } set { SetValue(AnimateDurationProperty, value); } } public static readonly DependencyProperty AnimateDurationProperty = DependencyProperty.Register("AnimateDuration", typeof(TimeSpan), typeof(CarRoundPlay), new PropertyMetadata(TimeSpan.FromSeconds(0.5))); /// <summary> /// 设置获取 /// </summary> public bool Recyclable { get { return (bool)GetValue(RecyclableProperty); } set { SetValue(RecyclableProperty, value); } } public static readonly DependencyProperty RecyclableProperty = DependencyProperty.Register("Recyclable", typeof(bool), typeof(CarRoundPlay), new PropertyMetadata(false)); /// <summary> /// 自动动画 /// </summary> public TimeSpan AutoPlayInterval { get { return (TimeSpan)GetValue(AutoPlayIntervalProperty); } set { SetValue(AutoPlayIntervalProperty, value); } } public static readonly DependencyProperty AutoPlayIntervalProperty = DependencyProperty.Register("AutoPlayInterval", typeof(TimeSpan), typeof(CarRoundPlay), new PropertyMetadata(OnAutoPlayIntervalChanged)); #endregion #region RoutedEvent(路由事件) /// <summary> /// 轮播index /// </summary> public static readonly RoutedEvent IndexChangedEvent = EventManager.RegisterRoutedEvent("IndexChanged", RoutingStrategy.Bubble, typeof(IndexChangedEventHandler), typeof(CarRoundPlay)); 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 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);
样式
<Style TargetType="{x:Type Modules:CarRoundPlay}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Modules:CarRoundPlay}"> <Grid Background="{TemplateBinding Background}" ClipToBounds="True"> <StackPanel Orientation="{TemplateBinding Orientation}"> <ItemsControl ItemsSource="{TemplateBinding Children}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="{Binding Orientation,RelativeSource={RelativeSource AncestorType=Modules:CarRoundPlay}}" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <ContentControl Content="{Binding}" /> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </StackPanel> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
调用方法
方法一
xmlns:Zt="clr-namespace:RoundPlay.Modules" <Grid> <Zt:CarRoundPlay BorderBrush="LightGray" BorderThickness="1"
Recyclable="True" AutoPlayInterval="0:0:1" Height="300" Index="3" Width="300"> <Grid Background="#F15D26"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" Text="Page1/44" /> </Grid> <Grid Background="Beige"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" Text="Page1/44" /> </Grid> <Grid Background="Blue"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" Text="Page1/44" /> </Grid> </Zt:CarRoundPlay> </Grid>
方法二
SolidColorBrush[] brushes = new SolidColorBrush[4] { Brushes.White, Brushes.Red, Brushes.Green, Brushes.Yellow }; Random rnd = new Random(); MainViewModel viewmodel = (this.DataContext as MainViewModel); for (int i = 0; i < viewmodel.ComWorldLists.Count; i++) { Grid grid = new Grid(); int random = rnd.Next(0, brushes.Length - 1); grid.Background = brushes[random]; TextBlock text = new TextBlock() { HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, FontSize = 24, FontWeight = FontWeights.Bold }; text.SetBinding(TextBlock.TextProperty, new Binding($"ComWorldLists[{i}].Name")); grid.Children.Add(text); Caruousel.Children.Add(grid); }