From 7d879cf6febe500a6f5f7fcda786b88d075abbf0 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Fri, 10 Feb 2023 13:00:25 +0100 Subject: [PATCH] Added Win32PlatformOptions.ShouldRenderOnUIThread Fixes WPF interop --- samples/interop/WindowsInteropTest/Program.cs | 11 +++++++-- .../Rendering/DefaultRenderTimer.cs | 5 ++-- src/Avalonia.Base/Rendering/IRenderLoop.cs | 5 +++- src/Avalonia.Base/Rendering/RenderLoop.cs | 1 + .../Rendering/UiThreadRenderTimer.cs | 9 ++++++- .../Wpf/WpfTopLevelImpl.cs | 9 +------ src/Windows/Avalonia.Win32/Win32Platform.cs | 24 +++++++++++++------ 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/samples/interop/WindowsInteropTest/Program.cs b/samples/interop/WindowsInteropTest/Program.cs index fac06d74b0..c2d30c67bb 100644 --- a/samples/interop/WindowsInteropTest/Program.cs +++ b/samples/interop/WindowsInteropTest/Program.cs @@ -1,5 +1,4 @@ using System; -using Avalonia.Controls; using ControlCatalog; using Avalonia; @@ -15,7 +14,15 @@ namespace WindowsInteropTest { System.Windows.Forms.Application.EnableVisualStyles(); System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); - AppBuilder.Configure().UseWin32().UseDirect2D1().SetupWithoutStarting(); + AppBuilder.Configure() + .UseWin32() + .UseDirect2D1() + .With(new Win32PlatformOptions + { + UseWindowsUIComposition = false, + ShouldRenderOnUIThread = true // necessary for WPF + }) + .SetupWithoutStarting(); System.Windows.Forms.Application.Run(new SelectorForm()); } } diff --git a/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs b/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs index d0d3dd9715..7b0fecf675 100644 --- a/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs +++ b/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs @@ -1,6 +1,4 @@ using System; -using System.Diagnostics; -using System.Threading.Tasks; using Avalonia.Platform; namespace Avalonia.Rendering @@ -59,7 +57,8 @@ namespace Avalonia.Rendering } } - public bool RunsInBackground => true; + /// + public virtual bool RunsInBackground => true; /// /// Starts the timer. diff --git a/src/Avalonia.Base/Rendering/IRenderLoop.cs b/src/Avalonia.Base/Rendering/IRenderLoop.cs index e500ecdf8b..ebe683949d 100644 --- a/src/Avalonia.Base/Rendering/IRenderLoop.cs +++ b/src/Avalonia.Base/Rendering/IRenderLoop.cs @@ -27,7 +27,10 @@ namespace Avalonia.Rendering /// /// The update task. void Remove(IRenderLoopTask i); - + + /// + /// Indicates if the rendering is done on a non-UI thread. + /// bool RunsInBackground { get; } } } diff --git a/src/Avalonia.Base/Rendering/RenderLoop.cs b/src/Avalonia.Base/Rendering/RenderLoop.cs index 1f58ca3827..185f44d29a 100644 --- a/src/Avalonia.Base/Rendering/RenderLoop.cs +++ b/src/Avalonia.Base/Rendering/RenderLoop.cs @@ -87,6 +87,7 @@ namespace Avalonia.Rendering } } + /// public bool RunsInBackground => Timer.RunsInBackground; private void TimerTick(TimeSpan time) diff --git a/src/Avalonia.Base/Rendering/UiThreadRenderTimer.cs b/src/Avalonia.Base/Rendering/UiThreadRenderTimer.cs index 1bbf804b5f..7f2eedc98c 100644 --- a/src/Avalonia.Base/Rendering/UiThreadRenderTimer.cs +++ b/src/Avalonia.Base/Rendering/UiThreadRenderTimer.cs @@ -8,13 +8,20 @@ namespace Avalonia.Rendering /// /// Render timer that ticks on UI thread. Useful for debugging or bootstrapping on new platforms /// - public class UiThreadRenderTimer : DefaultRenderTimer { + /// + /// Initializes a new instance of the class. + /// + /// The number of frames per second at which the loop should run. public UiThreadRenderTimer(int framesPerSecond) : base(framesPerSecond) { } + /// + public override bool RunsInBackground => false; + + /// protected override IDisposable StartCore(Action tick) { bool cancelled = false; diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 2d0f351d58..13eae1992c 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -45,13 +45,6 @@ namespace Avalonia.Win32.Interop.Wpf ((FrameworkElement)PlatformImpl)?.InvalidateMeasure(); } - protected override void HandleResized(Size clientSize, PlatformResizeReason reason) - { - ClientSize = clientSize; - LayoutManager.ExecuteLayoutPass(); - Renderer?.Resized(clientSize); - } - public Size AllocatedSize => ClientSize; } @@ -223,7 +216,7 @@ namespace Avalonia.Win32.Interop.Wpf (Key)e.Key, GetModifiers(null))); - protected override void OnTextInput(TextCompositionEventArgs e) + protected override void OnTextInput(TextCompositionEventArgs e) => _ttl.Input?.Invoke(new RawTextInputEventArgs(_keyboard, (uint) e.Timestamp, _inputRoot, e.Text)); void ITopLevelImpl.SetCursor(ICursorImpl cursor) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index f16a1ca8cf..c34be9008a 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -6,12 +6,10 @@ using System.IO; using Avalonia.Reactive; using System.Runtime.InteropServices; using System.Threading; -using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Platform; -using Avalonia.Media; using Avalonia.OpenGL; using Avalonia.Platform; using Avalonia.Rendering; @@ -20,7 +18,6 @@ using Avalonia.Threading; using Avalonia.Utilities; using Avalonia.Win32.Input; using Avalonia.Win32.Interop; -using Avalonia.Win32.WinRT; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia @@ -68,6 +65,7 @@ namespace Avalonia /// /// Render Avalonia to a Texture inside the Windows.UI.Composition tree. + /// This setting is true by default. /// /// /// Supported on Windows 10 build 16299 and above. Ignored on other versions. @@ -88,9 +86,19 @@ namespace Avalonia /// This is only recommended if low input latency is desirable, and there is no need for the transparency /// and stylings / blurrings offered by
/// This is mutually exclusive with - /// which if active will override this setting. + /// which if active will override this setting. + /// This setting is false by default. /// - public bool UseLowLatencyDxgiSwapChain { get; set; } = false; + public bool UseLowLatencyDxgiSwapChain { get; set; } + + /// + /// Render directly on the UI thread instead of using a dedicated render thread. + /// Only applicable if both and + /// are false. + /// This setting is only recommended for interop with systems that must render on the UI thread, such as WPF. + /// This setting is false by default. + /// + public bool ShouldRenderOnUIThread { get; set; } /// /// Provides a way to use a custom-implemented graphics context such as a custom ISkiaGpu @@ -128,7 +136,7 @@ namespace Avalonia.Win32 internal static bool UseOverlayPopups => Options.OverlayPopups; public static Win32PlatformOptions Options { get; private set; } - + internal static Compositor Compositor { get; private set; } public static void Initialize() @@ -139,6 +147,8 @@ namespace Avalonia.Win32 public static void Initialize(Win32PlatformOptions options) { Options = options; + var renderTimer = options.ShouldRenderOnUIThread ? new UiThreadRenderTimer(60) : new DefaultRenderTimer(60); + AvaloniaLocator.CurrentMutable .Bind().ToSingleton() .Bind().ToConstant(CursorFactory.Instance) @@ -146,7 +156,7 @@ namespace Avalonia.Win32 .Bind().ToSingleton() .Bind().ToConstant(s_instance) .Bind().ToConstant(new RenderLoop()) - .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(renderTimer) .Bind().ToConstant(s_instance) .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Control) {