WPF 实现视频会议与会人员动态布局
控件名:SixGridView
作 者:WPFDevelopersOrg - 驚鏵
原文链接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers
框架使用
.NET40
;Visual Studio 2019
;接着上一篇是基于
Grid
实现的视频查看感觉有点浪费,所以修改为基于Panel
又重新实现了。在
Panel EndInit()
后绘制鼠标经过的选中效果。当鼠标移动到候选封面区时,动画从上一次鼠标的位置到当前鼠标位置做移动动画。
1)新建 SixGirdView.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 SixGirdView : Panel{public static readonly DependencyProperty SelectBrushProperty =DependencyProperty.Register("SelectBrush", typeof(Brush), typeof(SixGirdView),new PropertyMetadata(Brushes.Red));public static readonly DependencyProperty BorderThicknessProperty =DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(SixGirdView),new PropertyMetadata(new Thickness(1)));private readonly Dictionary<Rect, int> _dicRect = new Dictionary<Rect, int>();private readonly int _columns = 3;private readonly int _rows = 3;private Border _border;private int _last;private Rect _lastRect;private Storyboard _storyboard;public Brush SelectBrush{get => (Brush) GetValue(SelectBrushProperty);set => SetValue(SelectBrushProperty, value);}public Thickness BorderThickness{get => (Thickness) GetValue(BorderThicknessProperty);set => SetValue(BorderThicknessProperty, value);}public override void EndInit(){base.EndInit();var children = InternalChildren;if (_border == null && children.Count >= 1){_border = new Border{BorderThickness = BorderThickness,BorderBrush = SelectBrush};_border.RenderTransform = new TranslateTransform();children.Add(_border);}}protected override Size MeasureOverride(Size availableSize){var children = InternalChildren;int numCol = 0, numRow = 0;var isRow = false;Point point = default;Size size = default;_dicRect.Clear();double _width = availableSize.Width / _columns, _height = availableSize.Height / _rows;for (int i = 0, count = children.Count; i < count; ++i){if (i >= 6) continue;var uIElement = children[i];if (uIElement != null){uIElement.Measure(availableSize);if (i == 0){point = new Point(0, 0);size = new Size(availableSize.Width / _columns * 2, availableSize.Height / _rows * 2);numRow++;}else{var num = i - 1;var x = 0d;if (!isRow){x = _width * 2;numCol = numRow + 1;if (numCol < _columns){point = new Point(x, 0);}else{point = new Point(x, _height);isRow = true;numCol = 0;}numRow++;}else{x = _width * numCol;numCol++;x = x >= availableSize.Width ? 0 : x;point = new Point(x, _height * 2);}size = new Size(_width, _height);}uIElement.Arrange(new Rect(point, size));if (i >= 6 || i == 0) continue;var rect = new Rect(point.X, point.Y, size.Width, size.Height);_dicRect.Add(rect, i);}}_last = _last == 0 ? 1 : _last;if (_border != null){_border.Measure(availableSize);point = new Point(0, 0);size = new Size(availableSize.Width / _columns, availableSize.Height / _columns);_border.Arrange(new Rect(point, size));var _translateTransform = (TranslateTransform) _border.RenderTransform;if (_last == 1){_translateTransform.X = availableSize.Width / _columns * 2;}else{var uIElement = InternalChildren[_last];if (uIElement != null){var rect = _dicRect.FirstOrDefault(x => x.Value == _last).Key;if (rect != null){point = new Point(rect.X, rect.Y);CreateStoryboard(point);}}}}return availableSize;}protected override void OnPreviewMouseMove(MouseEventArgs e){base.OnPreviewMouseMove(e);var currentPoint = e.GetPosition(this);if (_lastRect.Contains(currentPoint))return;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));_lastRect = model;}private void CreateStoryboard(Point point){var sineEase = new SineEase {EasingMode = EasingMode.EaseOut};if (_storyboard == null){_storyboard = new Storyboard();}else{_storyboard.Stop();_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)新建 SixGirdViewExample.xaml
代码如下:
<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.SixGirdViewExample"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:SixGirdView BorderThickness="1" SelectBrush="Red"><wpfdev:SixGirdView.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:SixGirdView.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:SixGirdView><controls:CodeViewer.SourceCodes><controls:SourceCodeModel CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/SixGirdViewExample.xaml" CodeType="Xaml"/><controls:SourceCodeModel CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/SixGirdViewExample.xaml.cs" CodeType="CSharp"/></controls:CodeViewer.SourceCodes></controls:CodeViewer>
</UserControl>
3)新建 SixGirdViewExample.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 SixGirdViewExample : UserControl{public SixGirdViewExample(){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