From fef47cd1e23827d72fc90cc8d6724baa11d0c94d Mon Sep 17 00:00:00 2001 From: Jdbye Date: Fri, 18 Jul 2025 19:36:39 +0200 Subject: [PATCH] Allow attaching overlay window to specified background element Allow attaching overlay window to specified background element rather than it being constrained to the video dimensions. This means the foreground window overlay is a predictable position and size and makes overlaying controls on top of the video much more intuitive. --- src/LibVLCSharp.WPF/ForegroundWindow.cs | 55 ++++++++++++++----------- src/LibVLCSharp.WPF/VideoView.cs | 7 +++- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/LibVLCSharp.WPF/ForegroundWindow.cs b/src/LibVLCSharp.WPF/ForegroundWindow.cs index 8c7b33fa5..437eaed53 100644 --- a/src/LibVLCSharp.WPF/ForegroundWindow.cs +++ b/src/LibVLCSharp.WPF/ForegroundWindow.cs @@ -11,7 +11,8 @@ namespace LibVLCSharp.WPF internal class ForegroundWindow : Window { Window? _wndhost; - readonly FrameworkElement _bckgnd; + readonly FrameworkElement _videoHost; + readonly FrameworkElement _backgroundElement; readonly Point _zeroPoint = new Point(0, 0); private readonly Grid _grid = new Grid(); @@ -30,7 +31,7 @@ internal UIElement? OverlayContent } } - internal ForegroundWindow(FrameworkElement background) + internal ForegroundWindow(FrameworkElement videoHost, FrameworkElement? background) { Title = "LibVLCSharp.WPF"; Height = 300; @@ -42,23 +43,27 @@ internal ForegroundWindow(FrameworkElement background) ShowInTaskbar = false; Content = _grid; - DataContext = background.DataContext; + DataContext = videoHost.DataContext; - _bckgnd = background; - _bckgnd.DataContextChanged += Background_DataContextChanged; - _bckgnd.Loaded += Background_Loaded; - _bckgnd.Unloaded += Background_Unloaded; + if (background == null) + background = videoHost; + + _backgroundElement = background; + _videoHost = videoHost; + _videoHost.DataContextChanged += VideoHost_DataContextChanged; + _videoHost.Loaded += VideoHost_Loaded; + _videoHost.Unloaded += VideoHost_Unloaded; } - void Background_DataContextChanged(object? sender, DependencyPropertyChangedEventArgs e) + void VideoHost_DataContextChanged(object? sender, DependencyPropertyChangedEventArgs e) { DataContext = e.NewValue; } - void Background_Unloaded(object? sender, RoutedEventArgs e) + void VideoHost_Unloaded(object? sender, RoutedEventArgs e) { - _bckgnd.SizeChanged -= Bckgnd_SizeChanged; - _bckgnd.LayoutUpdated -= Bckgnd_LayoutUpdated; + _backgroundElement.SizeChanged -= Bckgnd_SizeChanged; + _backgroundElement.LayoutUpdated -= Bckgnd_LayoutUpdated; if (_wndhost != null) { _wndhost.Closing -= Wndhost_Closing; @@ -68,14 +73,14 @@ void Background_Unloaded(object? sender, RoutedEventArgs e) Hide(); } - void Background_Loaded(object? sender, RoutedEventArgs e) + void VideoHost_Loaded(object? sender, RoutedEventArgs e) { if (_wndhost != null && IsVisible) { return; } - _wndhost = GetWindow(_bckgnd); + _wndhost = GetWindow(_videoHost); Trace.Assert(_wndhost != null); if (_wndhost == null) { @@ -86,8 +91,8 @@ void Background_Loaded(object? sender, RoutedEventArgs e) _wndhost.Closing += Wndhost_Closing; _wndhost.LocationChanged += Wndhost_LocationChanged; - _bckgnd.LayoutUpdated += Bckgnd_LayoutUpdated; - _bckgnd.SizeChanged += Bckgnd_SizeChanged; + _backgroundElement.LayoutUpdated += Bckgnd_LayoutUpdated; + _backgroundElement.SizeChanged += Bckgnd_SizeChanged; try { @@ -131,13 +136,13 @@ void AlignWithBackground() return; } - if (PresentationSource.FromVisual(_bckgnd) == null) + if (PresentationSource.FromVisual(_backgroundElement) == null) { return; } - if (double.IsNaN(_bckgnd.ActualWidth) || double.IsNaN(_bckgnd.ActualHeight) || - _bckgnd.ActualWidth == 0 || _bckgnd.ActualHeight == 0) + if (double.IsNaN(_backgroundElement.ActualWidth) || double.IsNaN(_backgroundElement.ActualHeight) || + _backgroundElement.ActualWidth == 0 || _backgroundElement.ActualHeight == 0) { return; } @@ -162,9 +167,9 @@ void AlignWithBackground() * The video view itself natively supports scaling. */ - var startLocationFromScreen = _bckgnd.PointToScreen(_zeroPoint); + var startLocationFromScreen = _backgroundElement.PointToScreen(_zeroPoint); var startLocationPoint = source.CompositionTarget.TransformFromDevice.Transform(startLocationFromScreen); - var endLocationFromScreen = _bckgnd.PointToScreen(new Point(_bckgnd.ActualWidth, _bckgnd.ActualHeight)); + var endLocationFromScreen = _backgroundElement.PointToScreen(new Point(_backgroundElement.ActualWidth, _backgroundElement.ActualHeight)); var endLocationPoint = source.CompositionTarget.TransformFromDevice.Transform(endLocationFromScreen); Left = Math.Min(startLocationPoint.X, endLocationPoint.X); @@ -172,9 +177,9 @@ void AlignWithBackground() Width = Math.Abs(endLocationPoint.X - startLocationPoint.X); Height = Math.Abs(endLocationPoint.Y - startLocationPoint.Y); - if (Math.Abs(Width - _bckgnd.ActualWidth) + Math.Abs(Height - _bckgnd.ActualHeight) > 0.5) + if (Math.Abs(Width - _backgroundElement.ActualWidth) + Math.Abs(Height - _backgroundElement.ActualHeight) > 0.5) { - ScaleWindowContent(Width / _bckgnd.ActualWidth, Height / _bckgnd.ActualHeight); + ScaleWindowContent(Width / _backgroundElement.ActualWidth, Height / _backgroundElement.ActualHeight); } } @@ -208,9 +213,9 @@ void Wndhost_Closing(object? sender, System.ComponentModel.CancelEventArgs e) Close(); - _bckgnd.DataContextChanged -= Background_DataContextChanged; - _bckgnd.Loaded -= Background_Loaded; - _bckgnd.Unloaded -= Background_Unloaded; + _videoHost.DataContextChanged -= VideoHost_DataContextChanged; + _videoHost.Loaded -= VideoHost_Loaded; + _videoHost.Unloaded -= VideoHost_Unloaded; } protected override void OnKeyDown(KeyEventArgs e) diff --git a/src/LibVLCSharp.WPF/VideoView.cs b/src/LibVLCSharp.WPF/VideoView.cs index 557dc730c..e3d548560 100644 --- a/src/LibVLCSharp.WPF/VideoView.cs +++ b/src/LibVLCSharp.WPF/VideoView.cs @@ -57,6 +57,11 @@ private static void OnMediaPlayerChanged(DependencyObject d, DependencyPropertyC private ForegroundWindow? ForegroundWindow { get; set; } private bool IsUpdatingContent { get; set; } private UIElement? ViewContent { get; set; } + + /// + /// Background element for this VideoView + /// + public FrameworkElement? BackgroundElement { get; set; } = null; /// /// ForegroundWindow management and MediaPlayer setup. @@ -78,7 +83,7 @@ public override void OnApplyTemplate() _videoHwndHost = controlHost; - ForegroundWindow = new ForegroundWindow(_videoHwndHost) + ForegroundWindow = new ForegroundWindow(_videoHwndHost, BackgroundElement) { OverlayContent = ViewContent };