使用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~