一、前言
上回说到需要做放视频的使用向导,这两天公司里的老司机一直帮我答疑解惑,让这个任务变得挺顺的,真心感谢他们!
这次与【1】中的不同之处在于:
(1)播放和暂停按钮集成在<MediaElement>的点击事件之中,点一下是播放,再点一下是暂停
(2)加入了微软官方改写的粒子特效
(3)加上了自己琢磨的按钮旋转效果,以及按钮淡出popup效果
(4)进度条改善美观
二、代码
前台:
1 <Window 2 "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 "http://schemas.microsoft.com/winfx/2006/xaml" 4 "http://schemas.microsoft.com/expression/blend/2008" 5 "http://schemas.open" 6 "clr-namespace:WPF_Nav" 7 "http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol" "http://schemas.devexpress.com/winfx/2008/xaml/windowsui" x:Class="WPF_Nav.MainWindow" 8 mc:Ignorable="d" 9 Title="MainWindow" Height="600" Width="800" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation ="CenterScreen" Loaded="Window_Loaded"> 10 11 <Window.Resources> 12 <LinearGradientBrush x:Key="SliderBackground" StartPoint="0,0" EndPoint="0,1"> 13 <GradientStop Offset="0.5" Color="#00b3fe"/> 14 </LinearGradientBrush> 15 <LinearGradientBrush x:Key="SliderThumb" StartPoint="0,0" EndPoint="0,1"> 16 <GradientStop Offset="0" Color="#FFD9D3E8"/> 17 </LinearGradientBrush> 18 19 <Style x:Key="Slider_RepeatButton" TargetType="RepeatButton"> 20 <Setter Property="Focusable" Value="false" /> 21 <Setter Property="Height" Value="5"/> 22 <Setter Property="BorderBrush" Value="Transparent"/> 23 <Setter Property="Template"> 24 <Setter.Value> 25 <ControlTemplate TargetType="RepeatButton"> 26 <Border Background="{StaticResource SliderBackground}" /> 27 </ControlTemplate> 28 </Setter.Value> 29 </Setter> 30 </Style> 31 32 <Style x:Key="Slider_RepeatButton1" TargetType="RepeatButton"> 33 <Setter Property="Focusable" Value="false" /> 34 <Setter Property="Height" Value="5"/> 35 <Setter Property="BorderBrush" Value="Transparent"/> 36 <Setter Property="Template"> 37 <Setter.Value> 38 <ControlTemplate TargetType="RepeatButton"> 39 <Border Background="Silver" /> 40 </ControlTemplate> 41 </Setter.Value> 42 </Setter> 43 </Style> 44 45 <Style x:Key="Slider_Thumb" TargetType="Thumb"> 46 <Setter Property="Focusable" Value="false" /> 47 <Setter Property="Template"> 48 <Setter.Value> 49 <ControlTemplate TargetType="Thumb"> 50 <Ellipse Name="e" Width="15" Height="15" Fill="White" Stroke="Gray"/> 51 </ControlTemplate> 52 </Setter.Value> 53 </Setter> 54 </Style> 55 56 <Style TargetType="Slider"> 57 <Setter Property="Focusable" Value="false" /> 58 <Setter Property="Template"> 59 <Setter.Value> 60 <ControlTemplate TargetType="Slider"> 61 <Grid> 62 <Border BorderBrush="Red" BorderThickness="0" CornerRadius="0,0,0,0"> 63 <Track Name="PART_Track"> 64 <Track.DecreaseRepeatButton> 65 <RepeatButton Style="{StaticResource Slider_RepeatButton}" Command="Slider.DecreaseLarge"/> 66 </Track.DecreaseRepeatButton> 67 <Track.IncreaseRepeatButton> 68 <RepeatButton Style="{StaticResource Slider_RepeatButton1}" Command="Slider.IncreaseLarge"/> 69 </Track.IncreaseRepeatButton> 70 <Track.Thumb> 71 <Thumb Style="{StaticResource Slider_Thumb}"/> 72 </Track.Thumb> 73 </Track> 74 </Border> 75 </Grid> 76 </ControlTemplate> 77 </Setter.Value> 78 </Setter> 79 </Style> 80 81 82 <Style x:Key="Button_Close" TargetType="Button"> 83 <Setter Property="Template"> 84 <Setter.Value> 85 <ControlTemplate> 86 <Image x:Name="IMG" Source="E:\Test\WPFTest\Sources\Close.jpg"></Image> 87 </ControlTemplate> 88 </Setter.Value> 89 </Setter> 90 </Style> 91 92 <Style x:Key="Button_Forbidden" TargetType="Button"> 93 <Setter Property="Template"> 94 <Setter.Value> 95 <ControlTemplate> 96 <Image x:Name="IMG" Source="E:\Test\WPFTest\Sources\Forbidden.jpg"></Image> 97 </ControlTemplate> 98 </Setter.Value> 99 </Setter>100 </Style>101 102 103 <Style x:Key="Button_Left" TargetType="Button">104 <Setter Property="Template">105 <Setter.Value>106 <ControlTemplate>107 <Image x:Name="IMG" Source="E:\Test\WPFTest\Sources\Left.png" Stretch="Fill"></Image>108 </ControlTemplate>109 </Setter.Value>110 </Setter>111 </Style>112 113 114 <Style x:Key="Button_Right" TargetType="Button">115 <Setter Property="Template">116 <Setter.Value>117 <ControlTemplate>118 <Image x:Name="IMG" Source="E:\Test\WPFTest\Sources\Right.png" Stretch="Fill"></Image>119 </ControlTemplate>120 </Setter.Value>121 </Setter>122 </Style>123 124 </Window.Resources>125 126 <Grid Name="Main_Grid">127 <Grid.RowDefinitions>128 <RowDefinition Height="50"></RowDefinition>129 <RowDefinition Height="500"></RowDefinition>130 <RowDefinition Height="50"></RowDefinition>131 </Grid.RowDefinitions>132 133 <Border Name="title_Border" BorderBrush="#FBD3D0CD" BorderThickness="3" Grid.Row="0">134 <Grid Name="Title">135 <Grid.ColumnDefinitions>136 <ColumnDefinition Width="200"></ColumnDefinition>137 <ColumnDefinition Width="400"></ColumnDefinition>138 <ColumnDefinition Width="120"></ColumnDefinition>139 <ColumnDefinition Width="80"></ColumnDefinition>140 </Grid.ColumnDefinitions>141 142 <Grid Grid.Column="1">143 <Canvas x:Name="ParticleHost" Width="400" >144 <TextBlock Name="txtB_Step" Grid.Column="1" Width="200" Height="30" TextAlignment="Center" FontSize="20" FontFamily="Microsoft YaHei" Text="asss" Margin="100,7,0,0"/>145 </Canvas>146 </Grid>147 148 <Grid Name="grid_Cofig" Grid.Column="3">149 <Button Name="btn_Forbidden" Width="30" Click="Config_Click" Margin="2,10,48,12" HorizontalAlignment="Center" Focusable="False" Style="{StaticResource Button_Forbidden}" RenderTransformOrigin="0.5,0.5" ToolTipService.ToolTip="播放设置" ToolTipService.InitialShowDelay="1" ToolTipService.Placement="Bottom">150 <Button.RenderTransform>151 <RotateTransform x:Name="trans_forbidden" Angle="0"/>152 </Button.RenderTransform>153 <Button.Triggers>154 <EventTrigger RoutedEvent="Button.MouseEnter">155 <BeginStoryboard >156 <Storyboard>157 <DoubleAnimation From="0" To="90" Duration="0:0:0.4"158 Storyboard.TargetName="trans_forbidden"159 Storyboard.TargetProperty="Angle"/>160 </Storyboard>161 </BeginStoryboard>162 </EventTrigger>163 </Button.Triggers>164 </Button>165 </Grid>166 167 <Popup x:Name="Pop" PopupAnimation="Slide" Placement="Bottom" Width="93" Height="58" PlacementTarget="{Binding ElementName=grid_Cofig}" AllowsTransparency="True" StaysOpen="False" IsOpen="False" >168 <Border Background="White" BorderBrush="#FFC4C9CD" BorderThickness="2" Width="93">169 <StackPanel Margin="1">170 <StackPanel Name="sp_play" Orientation="Horizontal" Width="83" Height="22" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="2" MouseEnter="sp_play_enter" MouseLeave="sp_play_leave" >171 <CheckBox x:Name="ch_play" VerticalAlignment="Center" Margin="3,4" />172 <TextBlock x:Name="txb_play" Text="不再播放" Margin="5,0" VerticalAlignment="Center"/>173 </StackPanel>174 <StackPanel Name="sp_doc" Width="83" Height="22" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="2" MouseEnter="sp_doc_enter" MouseLeave="sp_doc_leave">175 <TextBlock Name="txb_Doc" Text="官方帮助文档" Margin="4,0,5,0" VerticalAlignment="Center">176 </TextBlock>177 </StackPanel>178 </StackPanel>179 </Border>180 </Popup>181 182 <Button Name="btn_Close" Grid.Column="3" Width="30" Click="Close_Click" Margin="37,10,13,12" HorizontalAlignment="Center" Focusable="False" Style="{StaticResource Button_Close}" RenderTransformOrigin="0.5,0.5" ToolTipService.ToolTip="关闭视频" ToolTipService.InitialShowDelay="1" ToolTipService.Placement="Bottom">183 <Button.RenderTransform>184 <RotateTransform x:Name="trans" Angle="0"/>185 </Button.RenderTransform>186 <Button.Triggers>187 <EventTrigger RoutedEvent="Button.MouseEnter">188 <BeginStoryboard >189 <Storyboard>190 <DoubleAnimation From="0" To="90" Duration="0:0:0.4"191 Storyboard.TargetName="trans"192 Storyboard.TargetProperty="Angle"/>193 </Storyboard>194 </BeginStoryboard>195 </EventTrigger>196 </Button.Triggers>197 </Button>198 199 </Grid>200 </Border>201 202 203 <Grid Name="Movie" Grid.Row="1">204 <MediaElement Stretch="Fill" LoadedBehavior="Manual" Name="QS_Movie" MediaOpened="Element_MediaOpened" Loaded="QS_Movie_Loaded" MouseLeftButtonDown="QS_Movie_MouseLeftButtonDown" />205 <Button Name="btn_pre" Width="30" Height="40" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Left_Click" Focusable="False" Style="{StaticResource Button_Left}"/>206 <Button Name="btn_next" Width="30" Height="40" HorizontalAlignment="Right" VerticalAlignment="Center" Click="Right_Click" Focusable="False" Style="{StaticResource Button_Right}"/>207 </Grid>208 209 210 <Border Name="Progress_Border" BorderBrush="#FBD3D0CD" BorderThickness="3" Grid.Row="2">211 <Grid Name="Control_Progress" >212 <Slider Grid.Column="0" Width="700" Name="timelineSlider" VerticalAlignment="Center" PreviewMouseLeftButtonDown="timelineMDown" PreviewMouseLeftButtonUp="timelineMUp" BorderThickness="0,6,0,0" />213 </Grid>214 </Border>215 216 </Grid>217 </Window>
后台:
1 using System.Windows.Media; 2 using System.Windows.Media.Effects; 3 using System.Windows.Shapes; 4 5 namespace WPF_Nav 6 { 7 public class Particle 8 { 9 public Point3D Position { get; set; }10 public Point3D Velocity { get; set; }11 public double Size { get; set; }12 public Ellipse Ellipse { get; set; }13 public BlurEffect Blur { get; set; }14 public Brush Brush { get; set; }15 }16 }
1 namespace WPF_Nav 2 { 3 public class Point3D 4 { 5 public double X { get; set; } 6 public double Y { get; set; } 7 public double Z { get; set; } 8 9 public Point3D(double X, double Y, double Z)10 {11 this.X = X;12 this.Y = Y;13 this.Z = Z;14 }15 }16 }
主体:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows; 7 using System.Windows.Controls; 8 using System.Windows.Controls.Primitives; 9 using System.Windows.Data; 10 using System.Windows.Documents; 11 using System.Windows.Input; 12 using System.Windows.Media; 13 using System.Windows.Media.Imaging; 14 using System.Windows.Navigation; 15 using System.Windows.Shapes; 16 using System.Windows.Threading; 17 using System.Windows.Media.Effects; 18 19 namespace WPF_Nav 20 { 21 /// <summary> 22 /// MainWindow.xaml 的交互逻辑 23 /// </summary> 24 public partial class MainWindow : Window 25 { 26 DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer(); // 定义一个DT 27 bool play_flag = true; //判断播放状态 28 int play_now = 0; //判断当前视频索引 29 int play_target; 30 List<string> Play_Video = new List<string>(); 31 List<string> Title_Video = new List<string>(); 32 33 34 35 public MainWindow() 36 { 37 InitializeComponent(); 38 Play_Video = LoadMovies(); 39 Title_Video = LoadTitles(); 40 } 41 42 private List<string> LoadTitles() 43 { 44 List<string> list_title = new List<string>(); 45 list_title.Add("Step1"); 46 list_title.Add("Step2"); 47 list_title.Add("Step3"); 48 return list_title; 49 } 50 private List<string> LoadMovies() 51 { 52 List<string> Movie_Uri = new List<string>(); 53 Movie_Uri.Add(@"E:\Test\WPFTest\Sources\preview.mp4"); 54 Movie_Uri.Add(@"E:\Test\WPFTest\Sources\preview1.mp4"); 55 Movie_Uri.Add(@"E:\Test\WPFTest\Sources\preview2.mp4"); 56 return Movie_Uri; 57 } 58 59 private void Play_Click(object sender, RoutedEventArgs e) 60 { 61 QS_Movie.Play(); 62 } 63 64 private void Pause_Click(object sender, RoutedEventArgs e) 65 { 66 QS_Movie.Pause(); 67 } 68 69 private void Element_MediaOpened(object sender, EventArgs e) 70 { 71 timelineSlider.Maximum = QS_Movie.NaturalDuration.TimeSpan.TotalMilliseconds; //设置slider最大值 72 dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick); //超过计时间隔时发生 73 dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 200); //DT间隔 74 dispatcherTimer.Start(); //DT启动 75 } 76 77 private void dispatcherTimer_Tick(object sender, EventArgs e) 78 { 79 int time = (int)QS_Movie.Position.TotalSeconds; 80 timelineSlider.ToolTip = SecToTime(time); 81 timelineSlider.Value = QS_Movie.Position.TotalMilliseconds; //slider滑动值随播放内容位置变化 82 } 83 84 private string SecToTime(int sec) 85 { 86 int min = sec / 60; 87 sec = sec - min * 60; 88 int hour = min / 60; 89 min = min - hour * 60; 90 string h = hour.ToString(); 91 string mm = ((min < 10) ? "0" : "") + min.ToString(); 92 string ss = ((sec < 10) ? "0" : "") + sec.ToString(); 93 string time = h + ":" + mm + ":" + ss; 94 return time; 95 } 96 private void timelineMDown(object sender, EventArgs e) 97 { 98 dispatcherTimer.Stop(); 99 }100 private void timelineMUp(object sender, EventArgs e)101 {102 QS_Movie.Position = new TimeSpan(0, 0, 0, 0, (int)timelineSlider.Value);103 dispatcherTimer.Start();104 QS_Movie.Play();105 }106 107 108 109 private void QS_Movie_Loaded(object sender, RoutedEventArgs e)110 {111 PreLoad(400,play_now);112 113 }114 115 private void Left_Click(object sender, RoutedEventArgs e)116 {117 play_target = (play_now + Play_Video.Count-1) % Play_Video.Count;118 PreLoad(200, play_target);119 play_now = play_target; 120 }121 122 private void Right_Click(object sender, RoutedEventArgs e)123 {124 play_target = (play_now + 1) % Play_Video.Count;125 PreLoad(200, play_target);126 play_now = play_target;127 }128 129 private void PreLoad(int interval, int index)130 {131 QS_Movie.Source = new Uri(Play_Video[index]); 132 QS_Movie.Play();133 System.Threading.Thread.Sleep(interval);134 QS_Movie.Pause();135 txtB_Step.Text = Title_Video[index];136 }137 138 139 140 141 142 private void QS_Movie_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)143 {144 145 if(play_flag==true)146 {147 QS_Movie.Play();148 btn_pre.Visibility = System.Windows.Visibility.Hidden; 149 btn_next.Visibility = btn_pre.Visibility = System.Windows.Visibility.Hidden; 150 }151 if(play_flag==false)152 {153 QS_Movie.Pause();154 btn_pre.Visibility = System.Windows.Visibility.Visible;155 btn_next.Visibility = System.Windows.Visibility.Visible;156 157 }158 play_flag = !play_flag;159 }160 161 private void Close_Click(object sender, RoutedEventArgs e)162 {163 this.Close();164 }165 166 private void Config_Click(object sender, RoutedEventArgs e)167 {168 this.Pop.IsOpen = true;169 }170 171 private void sp_play_enter(object sender, MouseEventArgs e)172 {173 Brush br_bg = new SolidColorBrush(Color.FromRgb(0, 162, 232));174 Brush br_white = new SolidColorBrush(Color.FromRgb(255, 255, 255));175 txb_play.Foreground = br_white;176 sp_play.Background = br_bg;177 }178 179 private void sp_play_leave(object sender, MouseEventArgs e)180 {181 Brush br_white = new SolidColorBrush(Color.FromRgb(255, 255, 255));182 Brush br_black = new SolidColorBrush(Color.FromRgb(0, 0, 0));183 txb_play.Foreground = br_black;184 sp_play.Background = br_white;185 }186 187 private void sp_doc_enter(object sender, MouseEventArgs e)188 {189 Brush br_bg = new SolidColorBrush(Color.FromRgb(0, 162, 232));190 Brush br_white = new SolidColorBrush(Color.FromRgb(255, 255, 255));191 txb_Doc.Foreground = br_white;192 sp_doc.Background = br_bg;193 }194 195 private void sp_doc_leave(object sender, MouseEventArgs e)196 {197 Brush br_white = new SolidColorBrush(Color.FromRgb(255, 255, 255));198 Brush br_black = new SolidColorBrush(Color.FromRgb(0, 0, 0));199 txb_Doc.Foreground = br_black;200 sp_doc.Background = br_white;201 }202 203 204 205 206 207 208 /// <summary>209 /// 特效部分210 /// </summary>211 212 List<Particle> particles = new List<Particle>();213 List<Particle> deadList = new List<Particle>();214 Random random = new Random();215 216 217 private void Window_Loaded(object sender, RoutedEventArgs e)218 {219 timer.Interval = TimeSpan.FromMilliseconds(10);220 timer.Tick += new EventHandler(timer_Tick);221 timer.Start();222 }223 224 void timer_Tick(object sender, EventArgs e)225 {226 UpdateParticules();227 }228 229 DispatcherTimer timer = new DispatcherTimer();230 231 double elapsed = 0.1;232 private void UpdateParticules()233 {234 //更新粒子信息235 deadList.Clear();236 foreach (Particle p in this.particles)237 {238 if (p.Position.Y < -p.Size || p.Position.X < -p.Size || p.Position.X > Width + p.Size)239 {240 deadList.Add(p);241 }242 else243 {244 //更新位置245 p.Position.X += p.Velocity.X * elapsed;246 p.Position.Y += p.Velocity.Y * elapsed;247 p.Position.Z += p.Velocity.Z * elapsed;248 TranslateTransform t = (p.Ellipse.RenderTransform as TranslateTransform);249 t.X = p.Position.X;250 t.Y = p.Position.Y;251 252 //更新颜色信息253 p.Ellipse.Fill = p.Brush;254 p.Ellipse.Effect = p.Blur;255 }256 }257 258 //创建新的粒子259 for (int i = 0; i < 10 && this.particles.Count < 40; i++)260 {261 //尝试循环使用已有例子262 if (deadList.Count - 1 >= i)263 {264 SpawnParticle(deadList[i].Ellipse);265 deadList[i].Ellipse = null;266 }267 else268 {269 SpawnParticle(null);270 }271 }272 273 foreach (Particle p in deadList)274 {275 if (p.Ellipse != null) ParticleHost.Children.Remove(p.Ellipse);276 this.particles.Remove(p);277 }278 }279 280 private void SpawnParticle(Ellipse e)281 {282 double x = RandomWithVariance(Width / 2, Width / 2);283 double y = Height;284 double z = 10 * (random.NextDouble() * 100);285 double speed = RandomWithVariance(10, 10);286 double size = RandomWithVariance(10,10);287 288 Particle p = new Particle();289 p.Position = new Point3D(x, y, z);290 p.Size = size;291 292 //模糊293 var blur = new BlurEffect();294 blur.RenderingBias = RenderingBias.Performance;295 blur.Radius = RandomWithVariance(5, 5);296 p.Blur = blur;297 //颜色298 var brush = (Brush)Brushes.Lime.Clone();299 brush.Opacity = RandomWithVariance(0.6, 0.5);300 p.Brush = brush;301 302 TranslateTransform t;303 304 if (e != null)305 {306 e.Fill = null;307 e.Width = e.Height = size;308 p.Ellipse = e;309 310 t = e.RenderTransform as TranslateTransform;311 }312 else313 {314 p.Ellipse = new Ellipse();315 p.Ellipse.Width = p.Ellipse.Height = size;316 this.ParticleHost.Children.Add(p.Ellipse);317 318 t = new TranslateTransform();319 p.Ellipse.RenderTransform = t;320 p.Ellipse.RenderTransformOrigin = new Point(0.5, 0.5);321 322 }323 324 t.X = p.Position.X;325 t.Y = p.Position.Y;326 327 double velocityMultiplier = (random.NextDouble() + 0.25) * speed;328 double vX = (1.0 - (random.NextDouble() * 2.0)) * velocityMultiplier;329 double vY = -Math.Abs((1.0 - (random.NextDouble() * 2.0)) * velocityMultiplier);330 331 p.Velocity = new Point3D(vX, vY, 0);332 333 this.particles.Add(p);334 }335 336 private double RandomWithVariance(double midvalue, double variance)337 {338 double min = Math.Max(midvalue - (variance / 2), 0);339 double max = midvalue + (variance / 2);340 double value = min + ((max - min) * random.NextDouble());341 return value;342 }343 344 }345 346 347 348 }
三、效果
四、小结
WPF挺好玩的!前台就能解决一些基本的动画效果,加上后台简直爽。最后,感谢单位的老司机们!
原标题:基于MediaElement的WPF视频播放器(带部分特效)【2】
关键词:wpf