WPF 实现视频会议与会人员动态布局
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF 实现视频会议与会人员动态布局相关的知识,希望对你有一定的参考价值。
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
以上是关于WPF 实现视频会议与会人员动态布局的主要内容,如果未能解决你的问题,请参考以下文章
Office 365 轻松上手指南 - Exchange Online