wpf 自定义轮播图组件

文章介绍了一个使用WPF编写的自定义轮播图组件,该组件支持自动播放、间隔时间设置以及前后按钮控制。通过DispatcherTimer实现自动切换,同时提供了预览和下一个按钮的点击事件。此外,组件还允许用户注册事件处理程序以在特定情况下(如视频播放结束)调整自动播放行为。示例代码展示了如何在XAML中使用该组件并绑定数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用wpf自定义轮播图组件,实现轮播功能。

轮播图组件代码:

[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)]
[TemplatePart(Name = "Part_Grid",Type= typeof(Grid))]
[TemplatePart(Name = "Part_OldContentControl", Type = typeof(ContentControl))]
[TemplatePart(Name = "Part_NewContentControl", Type = typeof(ContentControl))]
[TemplatePart(Name = "Part_PreButton", Type = typeof(Button))]
[TemplatePart(Name = "Part_NextButton", Type = typeof(Button))]
[TemplatePart(Name = "Part_StackPanel", Type = typeof(StackPanel))]
public class Carousel:ItemsControl
{   
    #region Private Fields

    private const string GridTemplateName = "Part_Grid";
    private const string OldContentControlTemplateName = "Part_OldContentControl";
    private const string NewContentControlTemplateName = "Part_NewContentControl";
    private const string PreButtonTemplateName = "Part_PreButton";
    private const string NextButtonTemplateName = "Part_NextButton";
    private const string StackPanelTemplateName = "Part_StackPanel";

    private DispatcherTimer _timer;
    private Grid _grid;
    private ContentControl _oldContentControl;
    private ContentControl _newContentControl;
    private Button _prevButton;
    private Button _nextButton;
    private StackPanel _stackPanel;
    private int _pageIndex = -1;
    private bool _templateLoaded;

    #endregion
	#region Public Properties
    
    public TimeSpan Interval
    {
        get => (TimeSpan)GetValue(IntervalProperty);
        set => SetValue(IntervalProperty, value);
    }
    
    public static readonly DependencyProperty IntervalProperty =
        DependencyProperty.Register(nameof(Interval), typeof(TimeSpan), typeof(Carousel), new PropertyMetadata(TimeSpan.FromSeconds(3)));
	public bool AutoPlay
    {
        get => (bool)GetValue(AutoPlayProperty);
        set => SetValue(AutoPlayProperty, value);
    }
    
    public static readonly DependencyProperty AutoPlayProperty =
        DependencyProperty.Register(nameof(AutoPlay), typeof(bool), typeof(Carousel), new PropertyMetadata(false,
            (o, e) =>
            {
                if (o is Carousel d)
                {
                    d.TimerSwitch((bool)e.NewValue);
                }
            }));
    
    public Style PageButtonStyle
    {
        get => (Style)GetValue(PageButtonStyleProperty);
        set => SetValue(PageButtonStyleProperty, value);
    }

	public static readonly DependencyProperty PageButtonStyleProperty =
        DependencyProperty.Register(nameof(PageButtonStyle), typeof(Style), typeof(Carousel), new PropertyMetadata(default));
    
    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;

            SwitchPageButton(_pageIndex);
        }
    }

    #endregion

	 #region Events

    public event EventHandler PreButtonClick;
    public event EventHandler NextButtonClick;
    public event EventHandler PageButtonClick;

    #endregion

    #region Constructor

    static Carousel()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(Carousel),new FrameworkPropertyMetadata(typeof(Carousel)));
    }

    public Carousel()
    {
        this.Loaded += (d, e) => SwitchPageButton(-1);
    }

    #endregion

	#region Override Methods

    public override void OnApplyTemplate()
    {
        _grid?.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonBaseClick));
        _prevButton?.RemoveHandler(ButtonBase.ClickEvent,new RoutedEventHandler(ButtonPreClick));
        _nextButton?.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonNextClick));

        base.OnApplyTemplate();

        _templateLoaded = false;
        _grid = GetTemplateChild(GridTemplateName) as Grid;
        _prevButton = GetTemplateChild(PreButtonTemplateName) as Button;
        _nextButton=GetTemplateChild(NextButtonTemplateName) as Button;
        _stackPanel=GetTemplateChild(StackPanelTemplateName) as StackPanel;
        _oldContentControl = GetTemplateChild(OldContentControlTemplateName) as ContentControl;
        _newContentControl=GetTemplateChild(NewContentControlTemplateName) as ContentControl;
		_prevButton?.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonPreClick));
        _nextButton?.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonNextClick));
        _grid?.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonBaseClick));

        _templateLoaded = true;
    }

    #endregion
	#region Private Methods

    private void TimerSwitch(bool isAutoPlay)
    {
        if (_timer != null)
        {
            _timer.Tick -= _timer_Tick;
            _timer.Stop();
            _timer = null;
        }

        if(!isAutoPlay)
            return;

        _timer = new DispatcherTimer(){ Interval = Interval};
        _timer.Tick += _timer_Tick; ;
        _timer.Start();
    }

    private void _timer_Tick(object sender, EventArgs e)
    {
        if(_templateLoaded)
            PageIndex++;
    }

	private void SwitchPageButton(int index)
    {
        _stackPanel.Children.Clear();

        if(Items.Count==0)
            return;

        for (int i = 0; i < Items.Count; i++)
        {
            _stackPanel.Children.Add(new RadioButton()
            {
                Style = PageButtonStyle
            });
        }

        if (index<0)
            index = Items.Count-1;

        if (index > Items.Count - 1)
            index = 0;

        if (_stackPanel.Children[index] is RadioButton btn)
        {
            btn.IsChecked = true;
            btn.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, btn));
            SwitchPage();
        }
    }

	private void SwitchPage()
    {
        if(Items.Count==0)
            return;

        if (Items[PageIndex] is FrameworkElement currentElement)
        {
            var oldContent = _newContentControl.Content;
            _newContentControl.Content = null;

            currentElement.Loaded += async (s, e) =>
            {
                var element = s as FrameworkElement;
                await element.SlideAndFadeInAsync(AnimationSlideInDirection.Right, true,1.0f);
            };

            currentElement.Unloaded += async (s, e) =>
            {
                var element = s as FrameworkElement;
                await element.SlideAndFadeOutAsync(AnimationSlideInDirection.Left,1.0f);
            };
 			 _oldContentControl.Content = oldContent;

            Task.Delay((int)(1 * 1000)).ContinueWith((t) =>
            {
                Application.Current.Dispatcher.Invoke(() => _oldContentControl.Content = null);
            });
            _newContentControl.Content = currentElement;
        }
    }

    private void ButtonBaseClick(object sender, RoutedEventArgs e)
    {
        var _selectedButton = e.OriginalSource as RadioButton;

        var index = _stackPanel.Children.IndexOf(_selectedButton);
        if (index != -1)
        {
            PageIndex = index;
        }

        PageButtonClick?.Invoke(sender, e);
    }
 	private void ButtonPreClick(object sender, RoutedEventArgs e)
    {
        PageIndex--;
        PreButtonClick?.Invoke(sender,e);
    }

    private void ButtonNextClick(object sender, RoutedEventArgs e)
    {
        PageIndex++;
        NextButtonClick?.Invoke(sender,e);
    }

    #endregion
}

style

<Style TargetType="{x:Type controls1:Carousel}">
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls1:Carousel}">
                    <Grid
                        x:Name="Part_Grid"
                        Background="{TemplateBinding Background}"
                        ClipToBounds="True">
                        <ContentControl x:Name="Part_OldContentControl" />
                        <ContentControl x:Name="Part_NewContentControl" />

                        <Button
                            x:Name="Part_PreButton"
                            Width="60"
                            Height="100"
                            Margin="15,0,0,0"
                            Padding="15"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Background="DarkGray"
                            BorderThickness="0"
                            Opacity="0.7"
                            Visibility="Hidden">
                            <Button.Content>
                                <Path
                                    Data="M 15,50 30,18 33,20 19,50 33,80 30,82 z"
                                    Fill="White"
                                    Stretch="Uniform" />
                            </Button.Content>
                        </Button>
                        <Button
                            x:Name="Part_NextButton"
                            Width="60"
                            Height="100"
                            Margin="0,0,15,0"
                            Padding="15"
                            HorizontalAlignment="Right"
                            VerticalAlignment="Center"
                            Background="DarkGray"
                            BorderThickness="0"
                            Opacity="0.7"
                            Visibility="Hidden">
                            <Button.Content>
                                <Path
                                    Data="M 45,50 30,18 27,20 41,50 27,80 30,82 z"
                                    Fill="White"
                                    Stretch="Uniform" />
                            </Button.Content>
                        </Button>

                        <StackPanel
                            x:Name="Part_StackPanel"
                            Margin="0,0,0,10"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Bottom"
                            Orientation="Horizontal" />
                    </Grid>

                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="Part_PreButton" Property="Visibility" Value="Visible" />
                            <Setter TargetName="Part_NextButton" Property="Visibility" Value="Visible" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

使用

Border Grid.Row="0">
                <controls:Carousel
                    x:Name="myCarousel"
                    AutoPlay="True"
                    NextButtonClick="MyCarousel_OnNextButtonClick"
                    PreButtonClick="MyCarousel_OnPreButtonClick">
                    <controls:Carousel.Items>
                        <Image Source="/AutoPOS;component/Asset/Images/1.jpg" Stretch="Fill" />
                        <Image Source="/AutoPOS;component/Asset/Images/2.jpeg" Stretch="Fill" />
                        <Image Source="/AutoPOS;component/Asset/Images/3.png" Stretch="Fill" />
                        <MediaElement
                            LoadedBehavior="Play"
                            MediaEnded="MediaElement_OnMediaEnded"
                            MediaOpened="MediaElement_OnMediaOpened"
                            Source="./Asset/Vedio/单依纯-永不失联的爱.mkv"
                            Stretch="Fill" />
                    </controls:Carousel.Items>
                </controls:Carousel>
            </Border>

后台代码:

public partial class HomeView : UserControl
    {
        public HomeView()
        {
            InitializeComponent();
        }

        private void MediaElement_OnMediaOpened(object sender, RoutedEventArgs e)
        {
            myCarousel.AutoPlay=false;
        }

        private void MediaElement_OnMediaEnded(object sender, RoutedEventArgs e)
        {
            myCarousel.AutoPlay=true;
        }

        private void MyCarousel_OnPreButtonClick(object sender, EventArgs e)
        {
            myCarousel.AutoPlay = true;
        }

        private void MyCarousel_OnNextButtonClick(object sender, EventArgs e)
        {
            myCarousel.AutoPlay = true;
        }
    }

效果图:
在这里插入图片描述
在这里插入图片描述

注意的事项:
视频资源请指定内容,较新复制选项
可自实现sizechanged,visiblechanged实现变化逻辑
timer请先解除事件绑定和停止计时器,再置为null
carousel组件留出了许多接口,比如在播放视频时希望视频播放完成再去自动播放轮播图。可以注册事件处理。还留出相应的事件如下

	public event EventHandler PreButtonClick;
    public event EventHandler NextButtonClick;
    public event EventHandler PageButtonClick;

也可以使用mvvm绑定到items,autoplay等属性

over~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值