如何实现 WPF 视频封面查看器
控件名:NineGridView
作 者:WPFDevelopersOrg - 驚鏵
原文链接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers
框架使用
.NET40
;Visual Studio 2019
;实现视频封面查看器
NineGridView
基于Grid
实现三
行三
列,使用两
行两
列做主封面展示视频播放(也可以做rtsp
视频流播放),还剩下五
个做候选封面区展示,当点击封面区某个封面时进行替换主封面区视频(流)播放。当鼠标移动到候选封面区时,动画从上一次鼠标的位置到当前鼠标位置做移动动画。
1)新建 NineGridView.cs
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;namespace WPFDevelopers.Controls
{public class NineGridView : Grid{private int _rows = 3;private int _columns = 3;private Dictionary<Rect, int> _dicRect = new Dictionary<Rect, int>();private Border _border;private Storyboard _storyboard;private Rect _lastRect;private int _last;public static readonly DependencyProperty SelectBrushProperty =DependencyProperty.Register("SelectBrush", typeof(Brush), typeof(NineGridView),new PropertyMetadata(Brushes.Red));public static readonly DependencyProperty BorderThicknessProperty =DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(NineGridView),new PropertyMetadata(new Thickness(1)));public NineGridView(){Loaded += NineGridView_Loaded;SizeChanged += NineGridView_SizeChanged;}public Brush SelectBrush{get => (Brush)GetValue(SelectBrushProperty);set => SetValue(SelectBrushProperty, value);}public Thickness BorderThickness{get => (Thickness)GetValue(BorderThicknessProperty);set => SetValue(BorderThicknessProperty, value);}private void NineGridView_SizeChanged(object sender, SizeChangedEventArgs e){if (_border == null || _last == 0) return;var frameworkElement = InternalChildren[_last] as FrameworkElement;if (frameworkElement == null) return;_border.Width = frameworkElement.ActualWidth;_border.Height = frameworkElement.ActualHeight;var point = frameworkElement.TranslatePoint(new Point(0, 0), this);CreateStoryboard(point);}private void NineGridView_Loaded(object sender, RoutedEventArgs e){RowDefinitions.Clear();for (int i = 0; i < _rows; i++){var row1 = new RowDefinition();RowDefinitions.Add(row1);}ColumnDefinitions.Clear();for (int i = 0; i < _columns; i++){var col1 = new ColumnDefinition();ColumnDefinitions.Add(col1);}UIElementCollection children = InternalChildren;int numCol = 0, numRow = 0;for (int i = 0, count = children.Count; i < count; ++i){if (i > 6) return;UIElement child = children[i];if (child != null){if (i == 0){SetRowSpan(child, 2);SetColumnSpan(child, 2);}else{var num = i - 1;var col = GetColumnSpan(children[num]);col = col == 1 ? GetColumn(children[num]) : col;if (i + 1 <= _columns){SetColumn(child, col);SetRow(child, numRow);numRow++;}else{var row = GetRowSpan(children[0]);SetColumn(child, numCol);SetRow(child, row);numCol++;}}}}if(_border != null)Children.Remove(_border);_border = new Border{BorderThickness = BorderThickness,BorderBrush = SelectBrush,VerticalAlignment = VerticalAlignment.Top,HorizontalAlignment = HorizontalAlignment.Left};_border.Name = "PART_Adorner";_border.RenderTransform = new TranslateTransform();SetRowSpan(_border, _rows);SetColumnSpan(_border, _columns);_border.Width = ActualWidth / _columns - 2;_border.Height = ActualHeight / _rows - 2;var _translateTransform = (TranslateTransform)_border.RenderTransform;_translateTransform.X = _border.Width * 2 + 4;Children.Add(_border);_last = 1;}protected override void OnPreviewMouseMove(MouseEventArgs e){base.OnPreviewMouseMove(e);var currentPoint = e.GetPosition(this);if (_lastRect.Contains(currentPoint))return;_dicRect.Clear();UIElementCollection children = InternalChildren;for (int i = 0, count = children.Count; i < count; ++i){if (i >= 6 || i == 0) continue;var child = children[i] as FrameworkElement;if (child != null){var point = child.TranslatePoint(new Point(0, 0), this);var rect = new Rect(point.X, point.Y, child.ActualWidth, child.ActualHeight);_dicRect.Add(rect, i);}}var model = _dicRect.Keys.FirstOrDefault(x => x.Contains(currentPoint));if (model == default) return;_dicRect.TryGetValue(model, out _last);if (_border == null) return;CreateStoryboard(new Point(model.X, model.Y));_border.Width = model.Width;_border.Height = model.Height;_lastRect = model;}void CreateStoryboard(Point point){var sineEase = new SineEase() { EasingMode = EasingMode.EaseOut };if (_storyboard == null)_storyboard = new Storyboard();else_storyboard.Children.Clear();var animationX = new DoubleAnimation{Duration = TimeSpan.FromMilliseconds(500),To = point.X,EasingFunction = sineEase};Storyboard.SetTargetProperty(animationX, new PropertyPath("(Border.RenderTransform).(TranslateTransform.X)"));_storyboard.Children.Add(animationX);var animationY = new DoubleAnimation{Duration = TimeSpan.FromMilliseconds(500),To = point.Y,EasingFunction = sineEase};Storyboard.SetTargetProperty(animationY, new PropertyPath("(Border.RenderTransform).(TranslateTransform.Y)"));_storyboard.Children.Add(animationY);_storyboard.Begin(_border);}}
}
2)新建 NineGridViewExample.xaml
代码如下:
<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.NineGridViewExample"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:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"xmlns:controls="clr-namespace:WPFDevelopers.Samples.Controls"xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"><controls:CodeViewer><wpfdev:NineGridView BorderThickness="1" SelectBrush="Red"><wpfdev:NineGridView.Resources><Style TargetType="TextBlock"><Setter Property="Foreground" Value="White"/><Setter Property="VerticalAlignment" Value="Center"/><Setter Property="HorizontalAlignment" Value="Center"/></Style><Style TargetType="Border"><Setter Property="Margin" Value="1"/></Style></wpfdev:NineGridView.Resources><MediaElement x:Name="MyMediaElement" Loaded="MyMediaElement_Loaded"MediaEnded="MyMediaElement_MediaEnded"/><Border Background="#282C34"><wpfdev:SmallPanel><TextBlock Text="信号源1"/><Border Background="{DynamicResource PrimaryNormalSolidColorBrush}"VerticalAlignment="Top"HorizontalAlignment="Right"Padding="10,4"CornerRadius="3"><TextBlock Text="HD"/></Border></wpfdev:SmallPanel></Border><Border Background="#282C34"><TextBlock Text="无信号"/></Border><Border Background="#282C34"><TextBlock Text="无信号"/></Border><Border Background="#282C34"><TextBlock Text="无信号"/></Border><Border Background="#282C34"><TextBlock Text="无信号"/></Border></wpfdev:NineGridView><controls:CodeViewer.SourceCodes><controls:SourceCodeModel CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/NineGridViewExample.xaml" CodeType="Xaml"/><controls:SourceCodeModel CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/NineGridViewExample.xaml.cs" CodeType="CSharp"/></controls:CodeViewer.SourceCodes></controls:CodeViewer>
</UserControl>
3)新建 NineGridViewExample.xaml.cs
代码如下:
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;namespace WPFDevelopers.Samples.ExampleViews
{/// <summary>/// NineGridViewExample.xaml 的交互逻辑/// </summary>public partial class NineGridViewExample : UserControl{public NineGridViewExample(){InitializeComponent();}private void MyMediaElement_Loaded(object sender, RoutedEventArgs e){var path = "E:\\DCLI6K5UIAEmH9R.mp4";if (File.Exists(path))MyMediaElement.Source = new Uri("path");}private void MyMediaElement_MediaEnded(object sender, RoutedEventArgs e){MyMediaElement.Position = new TimeSpan(0);}}
}
参考资料[1]
原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers