WPF自定义banner

本文介绍如何在WPF中创建自定义的Banner元素,包括前端和后端页面的设计与实现,展示了一种增强用户界面的方法。

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

前端页面:

<UserControl x:Class="QuJiao.BannerViewer"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:QuJiao"
             xmlns:quJiao="clr-namespace:QuJiao;assembly=QuJiaoManager"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <!-- board样式 -->
        <Style x:Key="boardStyle" TargetType="Canvas">
            <Setter Property="Width" Value="{Binding Path=ActualWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer}}"/>
            <Setter Property="Height" Value="{Binding Path=ActualWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer}}"/>
            <Setter Property="ClipToBounds" Value="True"/>
            <Setter Property="Margin" Value="0"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
        </Style>
        <!-- banner图片 -->
        <Style x:Key="bannerImgStyle" TargetType="Image">
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="Width" Value="{Binding Path=ActualWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer}}"/>
            <Setter Property="Height" Value="{Binding Path=ActualHeight,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer}}"/>
            <Setter Property="Stretch" Value="Fill"/>
        </Style>
        <!-- banner列表 -->
        <Style x:Key="bannerImgListStyle" TargetType="{x:Type ItemsControl}">
            <Setter Property="BorderBrush" Value="{x:Null}"/>
            <Setter Property="BorderThickness" Value="0"/>
            <!-- 可以隐藏不可禁用,否则不能滚动了 -->
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>
            <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/>
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ItemsControl}">
                        <ScrollViewer x:Name="imgScroll"
                                      Tag="imgScroll"
                                      Style="{StaticResource ScrollViewerThin}"
                                      VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
                                      HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}">
                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </ScrollViewer>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <!-- 点击广告 -->
                        <Grid Tag="{Binding redirect_url}" MouseUp="ItemImg_OnMouseUp">
                            <Image x:Name="itemImg" Style="{StaticResource bannerImgStyle}" Source="{Binding image_url,Converter={StaticResource ConvertNameToBitmapImage}}"/>
                        </Grid>
                        <DataTemplate.Triggers>
                            <!-- 有链接时鼠标变为手型 -->
                            <DataTrigger Binding="{Binding redirect_url,Converter={StaticResource BoolConvertStringIfNullOrEmpty}}" Value="False">
                                <Setter Property="Cursor" Value="Hand"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!-- 导航圆点 -->
        <Style x:Key="dot" TargetType="Ellipse">
            <Setter Property="Width" Value="8"/>
            <Setter Property="Height" Value="8"/>
            <Setter Property="Fill" Value="White"/>
        </Style>
        <!-- 导航列表 -->
        <Style x:Key="dotListStyle" TargetType="ListBox">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="ItemsPanel">
                <Setter.Value>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBox">
                        <Grid>
                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemContainerStyle">
                <Setter.Value>
                    <Style TargetType="ListBoxItem">
                        <Setter Property="Cursor" Value="Hand"/>
                        <Setter Property="BorderBrush" Value="{x:Null}"/>
                        <Setter Property="Margin" Value="10,0,0,0"/>
                        <Setter Property="Opacity" Value="0.5"/>
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="ListBoxItem">
                                    <ContentPresenter/>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter Property="Opacity" Value="1"/>
                                        </Trigger>
                                        <Trigger Property="IsSelected" Value="True">
                                            <Setter Property="Opacity" Value="1"/>
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Setter.Value>
            </Setter>
            <Setter Property="ItemTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Ellipse Style="{StaticResource dot}" Width="9" Height="9"/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!-- 导航按钮 -->
        <Style x:Key="naviBtn" TargetType="quJiao:ButtonWithIcon">
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Width" Value="38"/>
            <Setter Property="Height" Value="38"/>
            <Setter Property="IconWidth" Value="38"/>
            <Setter Property="IconHeight" Value="38"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="quJiao:ButtonWithIcon">
                        <Image x:Name="img" Source="{TemplateBinding IconPath}" Width="{TemplateBinding IconWidth}" Height="{TemplateBinding IconHeight}"/>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Source" TargetName="img" Value="{Binding Path=SecondIconPath,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=quJiao:ButtonWithIcon}}"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="True">
                                <Setter Property="Source" TargetName="img" Value="{Binding Path=SecondIconPath,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=quJiao:ButtonWithIcon}}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Grid x:Name="grid"
          MouseEnter="OnMouseEnter"
          MouseLeave="OnMouseLeave"
          Width="{Binding Path=ActualWidth,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer}}"
          Height="{Binding Path=ActualHeight,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer}}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <!-- banner图片 -->
        <Canvas x:Name="canvas" 
                Grid.Column="0" Grid.ColumnSpan="3" 
                Style="{StaticResource boardStyle}">
            <Image Name="image1" Style="{StaticResource bannerImgStyle}" MouseUp="ItemImg_OnMouseUp" Source="{Binding Path=ImageUrl1,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer},Converter={StaticResource ConvertNameToBitmapImage}}"/>
            <Image Name="image2" Style="{StaticResource bannerImgStyle}" MouseUp="ItemImg_OnMouseUp" Source="{Binding Path=ImageUrl2,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer},Converter={StaticResource ConvertNameToBitmapImage}}"/>
        </Canvas>
        <!-- 向左翻页(默认隐藏,移入显示) -->
        <quJiao:ButtonWithIcon Grid.Column="0" Margin="10,0,0,0" 
                               Visibility="Collapsed"
                               Style="{StaticResource naviBtn}"
                               Name="btnLeft" 
                               IconPath="/QuJiaoResource;component/Resource/Image/Home/QualityCourseware/CourseDetail/LeftPage.png"
                               SecondIconPath="/QuJiaoResource;component/Resource/Image/Home/QualityCourseware/CourseDetail/LeftPageClick.png"
                               Click="BtnLeft_Click"/>
        <!-- 向右翻页(默认隐藏,移入显示) -->
        <quJiao:ButtonWithIcon Grid.Column="2" Margin="0,0,10,0" 
                               Visibility="Collapsed"
                               Style="{StaticResource naviBtn}"
                               Name="btnRight" 
                               IconPath="/QuJiaoResource;component/Resource/Image/Home/QualityCourseware/CourseDetail/RightPage.png"
                               SecondIconPath="/QuJiaoResource;component/Resource/Image/Home/QualityCourseware/CourseDetail/RightPageClick.png"
                               Click="BtnRight_Click"/>
        <!-- 导航圆点 -->
        <ListBox x:Name="dotList"
                 Grid.Column="0" Grid.ColumnSpan="3"
                 Style="{StaticResource dotListStyle}"
                 HorizontalAlignment="Right"
                 VerticalAlignment="Bottom"
                 Margin="0,0,24,15"
                 SelectedIndex="{Binding Path=SelectedIndex,Mode=TwoWay,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer}}"
                 ItemsSource="{Binding Path=BannerList,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:BannerViewer}}"/>
    </Grid>
</UserControl>

后端页面:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;

// ReSharper disable MemberCanBeMadeStatic.Local

namespace QuJiao
{
    public class BannerModel
    {
        public string image_url { get; set; }
        public string redirect_url { get; set; }
    }

    /// <summary>
    /// QujiaoBanner.xaml 的交互逻辑
    /// </summary>
    public partial class BannerViewer : UserControl, INotifyPropertyChanged
    {
        readonly TimeSpan interval = new TimeSpan(0, 0, 0, 0, 300);
        readonly Timer timer = new Timer();
        bool isPlayingAnim = false;
        /// <summary>
        /// 滚动宽度
        /// </summary>
        double RollWidth => canvas.ActualWidth;

        public BannerViewer()
        {
            InitializeComponent();
            // 设置计时器
            timer.Interval = 5000;// 默认5秒轮播
            timer.Elapsed += (sender, e) => Application.Current.Dispatcher?.BeginInvoke(new Action(Next));
        }

        #region 绑定属性

        #region BannerList
        public static readonly DependencyProperty BannerListProperty = DependencyProperty.Register(
            "BannerList", typeof(IList<BannerModel>),
            typeof(BannerViewer),
            new PropertyMetadata(null, (d, e) =>
            {
                var uc = (BannerViewer)d;
                var list = (IList<BannerModel>)e.NewValue;
                if (list != null && list.Count > 1)
                {
                    uc.SelectedIndex = 0;
                    uc.timer.Start();
                }
                else
                {
                    uc.timer.Stop();
                }
            }));
        public IList<BannerModel> BannerList
        {
            get => (IList<BannerModel>)GetValue(BannerListProperty);
            set => SetValue(BannerListProperty, value);
        }
        #endregion

        #region SwitchInterval
        public static readonly DependencyProperty SwitchIntervalProperty = DependencyProperty.Register(
            "SwitchInterval", typeof(double),
            typeof(BannerViewer),
            new PropertyMetadata(5000d, null, (d, baseValue) =>
            {
                var uc = (BannerViewer)d;
                var res = (double)baseValue;
                if (res > 0)
                {
                    uc.timer.Interval = res;
                }
                return res;
            }));
        public double SwitchInterval
        {
            get => (double)GetValue(SwitchIntervalProperty);
            set => SetValue(SwitchIntervalProperty, value);
        }

        #endregion

        #endregion

        #region 界面属性

        #region RaisePropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        public virtual void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        protected void Set<T>(ref T field, T newValue, string propertyName)
        {
            field = newValue;
            RaisePropertyChanged(propertyName);
        }
        #endregion

        int selectedIndex = 0;
        /// <summary>
        /// 当前选中
        /// </summary>
        public int SelectedIndex
        {
            get => selectedIndex;
            set
            {
                Set(ref selectedIndex, value, "SelectedIndex");
                SelectedIndexChange(value);
            }
        }

        string imageUrl1 = null;
        /// <summary>
        /// 图片1
        /// </summary>
        public string ImageUrl1
        {
            get => imageUrl1;
            set => Set(ref imageUrl1, value, "ImageUrl1");
        }

        string imageUrl2 = null;
        /// <summary>
        /// 图片2
        /// </summary>
        public string ImageUrl2
        {
            get => imageUrl2;
            set => Set(ref imageUrl2, value, "ImageUrl2");
        }

        #endregion

        #region 属性变更事件

        void SelectedIndexChange(int value)
        {
            if (rollAnim != null)
            {
                rollAnim.Stop(image1);
                rollAnim.Stop(image2);
            }
            if (BannerList != null && 0 <= value && value < BannerList.Count)
            {
                var model = BannerList[selectedIndex];
                ImageUrl(model.image_url, model.image_url);
            }
        }

        #endregion

        #region 动画事件

        /// <summary>
        /// 滚动动画
        /// </summary>
        Storyboard rollAnim { get; set; }

        /// <summary>
        /// 设置图片路径
        /// </summary>
        void ImageUrl(string url1, string url2)
        {
            if (BannerList.Count <= 0 || string.IsNullOrEmpty(url1) || string.IsNullOrEmpty(url2))
            {
                ImageUrl1 = null;
                ImageUrl2 = null;
            }
            else
            {
                ImageUrl1 = url1;
                ImageUrl2 = url2;
            }
        }

        /// <summary>
        /// 播放完成位置
        /// </summary>
        void Position(double pos1, double pos2)
        {
            image1.SetValue(Canvas.LeftProperty, pos1);
            image2.SetValue(Canvas.LeftProperty, pos2);
        }

        /// <summary>
        /// 获得轮播动画
        /// </summary>
        void BeginAnim(double fpos1, double tpos1, double fpos2, double tpos2)
        {
            var keyTime1 = KeyTime.FromTimeSpan(TimeSpan.Zero);
            var keyTime2 = KeyTime.FromTimeSpan(interval);
            isPlayingAnim = true;
            // 设置动画
            if (rollAnim != null)
            {
                rollAnim.Children.Clear();
            }
            else
            {
                rollAnim = new Storyboard();
            }
            #region 图片1动画
            var keyFrames1 = new DoubleAnimationUsingKeyFrames();
            keyFrames1.KeyFrames.Add(new LinearDoubleKeyFrame(fpos1, keyTime1));
            keyFrames1.KeyFrames.Add(new LinearDoubleKeyFrame(tpos1, keyTime2));
            Storyboard.SetTarget(keyFrames1, image1);
            Storyboard.SetTargetProperty(keyFrames1, new PropertyPath("(Canvas.Left)"));
            #endregion
            #region 图片2动画
            var keyFrames2 = new DoubleAnimationUsingKeyFrames();
            keyFrames2.KeyFrames.Add(new LinearDoubleKeyFrame(fpos2, keyTime1));
            keyFrames2.KeyFrames.Add(new LinearDoubleKeyFrame(tpos2, keyTime2));
            Storyboard.SetTarget(keyFrames2, image2);
            Storyboard.SetTargetProperty(keyFrames2, new PropertyPath("(Canvas.Left)"));
            #endregion
            rollAnim.Children.Add(keyFrames1);
            rollAnim.Children.Add(keyFrames2);
            rollAnim.FillBehavior = FillBehavior.Stop;
            rollAnim.Begin();
            TimerHelper.Deley(interval.Milliseconds / 10, delegate
            {
                isPlayingAnim = false;
            });
        }

        #endregion

        #region 切换图片

        void Prev()
        {
            if (isPlayingAnim) return;
            var max = BannerList.Count - 1;
            var prevIndex = SelectedIndex;
            int thisIndex;
            // 切换位置
            if (SelectedIndex > 0)
            {
                SelectedIndex--;
            }
            else
            {
                SelectedIndex = max;
            }
            if (SelectedIndex == max)
            {
                prevIndex = 0;
                thisIndex = max;
            }
            else if (SelectedIndex == 0)
            {
                prevIndex = 1;
                thisIndex = 0;
            }
            else
            {
                thisIndex = SelectedIndex;
            }
            var url1 = BannerList[prevIndex].image_url;
            var url2 = BannerList[thisIndex].image_url;
            ImageUrl(url1, url2);
            Position(-RollWidth, 0);
            BeginAnim(0, RollWidth, -RollWidth, 0);
        }

        void Next()
        {
            if (isPlayingAnim) return;
            var max = BannerList.Count - 1;
            var prevIndex = SelectedIndex;
            // 切换位置
            if (SelectedIndex < max)
            {
                SelectedIndex++;
            }
            else
            {
                SelectedIndex = 0;
            }
            var thisIndex = SelectedIndex;
            var url1 = BannerList[prevIndex].image_url;
            var url2 = BannerList[thisIndex].image_url;
            ImageUrl(url1, url2);
            Position(-RollWidth, 0);
            BeginAnim(0, -RollWidth, RollWidth, 0);
        }

        #endregion

        /// <summary>
        /// 向左翻页
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void BtnLeft_Click(object sender, RoutedEventArgs e)
        {
            Prev();
        }

        /// <summary>
        /// 向右翻页
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void BtnRight_Click(object sender, RoutedEventArgs e)
        {
            Next();
        }

        /// <summary>
        /// 点击课件详情
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ItemImg_OnMouseUp(object sender, MouseButtonEventArgs e)
        {
            try
            {
                var model = BannerList[selectedIndex];
                var url = model.redirect_url;
                MessengerHelper.Send((ushort)QualityCoursewareEvents.NavigateToBannerDetail, url);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// 鼠标移入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnMouseEnter(object sender, MouseEventArgs e)
        {
            btnLeft.Visibility = Visibility.Visible;
            btnRight.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// 鼠标移出
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnMouseLeave(object sender, MouseEventArgs e)
        {
            btnLeft.Visibility = Visibility.Hidden;
            btnRight.Visibility = Visibility.Hidden;
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值