如何实现 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);
}
}
}
![35b69a34bc0a86fef4e9052730403b90.gif](https://i-blog.csdnimg.cn/blog_migrate/5b051c79636940484ce1c68d861bd0f8.gif)
[1]
原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers