From 853ccb58fe750a31ac4b2cf31a225189df5c34fc Mon Sep 17 00:00:00 2001 From: Zhitao Pan Date: Mon, 23 Mar 2026 10:42:46 +1100 Subject: [PATCH 1/3] Handle cancellation in CameraViewHandler --- .../CameraManager.windows.cs | 3 ++ .../CameraViewExtensions.windows.cs | 1 + .../Handlers/CameraViewHandler.shared.cs | 46 +++++++++++++++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/CommunityToolkit.Maui.Camera/CameraManager.windows.cs b/src/CommunityToolkit.Maui.Camera/CameraManager.windows.cs index 5b211ea31e..291b4ac078 100644 --- a/src/CommunityToolkit.Maui.Camera/CameraManager.windows.cs +++ b/src/CommunityToolkit.Maui.Camera/CameraManager.windows.cs @@ -115,6 +115,7 @@ private async partial Task PlatformConnectCamera(CancellationToken token) private async partial Task PlatformStartCameraPreview(CancellationToken token) { + token.ThrowIfCancellationRequested(); if (mediaElement is null) { return; @@ -157,6 +158,8 @@ private partial void PlatformStopCameraPreview() async Task PlatformUpdateResolution(Size resolution, CancellationToken token) { + token.ThrowIfCancellationRequested(); + if (cameraView.SelectedCamera is null || !IsInitialized || mediaCapture is null) { return; diff --git a/src/CommunityToolkit.Maui.Camera/Extensions/CameraViewExtensions.windows.cs b/src/CommunityToolkit.Maui.Camera/Extensions/CameraViewExtensions.windows.cs index 8d525b70df..57143d0bb9 100644 --- a/src/CommunityToolkit.Maui.Camera/Extensions/CameraViewExtensions.windows.cs +++ b/src/CommunityToolkit.Maui.Camera/Extensions/CameraViewExtensions.windows.cs @@ -15,6 +15,7 @@ public static async Task UpdateAvailability(this ICameraView cameraView, Cancell public static Task InitializeCameraForCameraView(this MediaCapture mediaCapture, string deviceId, CancellationToken token) { + token.ThrowIfCancellationRequested(); try { return mediaCapture.InitializeAsync(new MediaCaptureInitializationSettings diff --git a/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs b/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs index a8e76ca734..944bd10966 100644 --- a/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs +++ b/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs @@ -1,4 +1,5 @@ -using CommunityToolkit.Maui.Extensions; +using System.Diagnostics; +using CommunityToolkit.Maui.Extensions; using Microsoft.Maui.Handlers; namespace CommunityToolkit.Maui.Core.Handlers; @@ -8,6 +9,9 @@ namespace CommunityToolkit.Maui.Core.Handlers; /// public partial class CameraViewHandler : ViewHandler, IDisposable { + CancellationTokenSource? cts; + bool isDisposed; + /// /// The currently defined mappings between properties on the and /// properties on the . @@ -74,6 +78,7 @@ protected override NativePlatformCameraPreviewView CreatePlatformView() // reset the zoom factor to 1 void Init(ICameraView view) { + Trace.WriteLine("- - - - - Camera view initialized - - - - -"); MapCameraFlashMode(this, view); view.ZoomFactor = 1.0f; } @@ -82,9 +87,34 @@ void Init(ICameraView view) /// protected override async void ConnectHandler(NativePlatformCameraPreviewView platformView) { - base.ConnectHandler(platformView); + cts?.Cancel(); + cts = new(); + try + { + //Trace.WriteLine($"> > > > > ConnectHandler {this.GetHashCode()}"); + base.ConnectHandler(platformView); + await CameraManager.ConnectCamera(cts.Token); + } + catch (OperationCanceledException) + { + // If the operation was canceled, we should dispose the camera manager + // and set it to null to avoid further operations on it. + //cameraManager?.Dispose(); + //cameraManager = null; + //return; + + Trace.WriteLine($"< < < < < OperationCanceledException {this.GetHashCode()}"); + } + catch (Exception ex) //when (ex is not OperationCanceledException && !isDisposed) + { + //// If the connection fails, we should dispose the camera manager + //// and set it to null to avoid further operations on it. + //cameraManager?.Dispose(); + //cameraManager = null; + //// Rethrow the exception to notify the user of the failure. + //throw new CameraException("Failed to connect to camera", ex); + } - await CameraManager.ConnectCamera(CancellationToken.None); } /// @@ -103,11 +133,21 @@ protected override void DisconnectHandler(NativePlatformCameraPreviewView platfo /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { + if (isDisposed) + { + return; + } if (disposing) { + cts?.Cancel(); + cts?.Dispose(); + cts = null; + cameraManager?.Dispose(); cameraManager = null; } + + isDisposed = true; } #if WINDOWS From 1949212c40956ae6f91b2eb175f9b621b5468f4d Mon Sep 17 00:00:00 2001 From: Zhitao Pan Date: Tue, 24 Mar 2026 13:33:01 +1100 Subject: [PATCH 2/3] clean up --- .../CameraManager.windows.cs | 3 -- .../CameraViewExtensions.windows.cs | 1 - .../Handlers/CameraViewHandler.shared.cs | 28 ++++++------------- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/CommunityToolkit.Maui.Camera/CameraManager.windows.cs b/src/CommunityToolkit.Maui.Camera/CameraManager.windows.cs index 291b4ac078..5b211ea31e 100644 --- a/src/CommunityToolkit.Maui.Camera/CameraManager.windows.cs +++ b/src/CommunityToolkit.Maui.Camera/CameraManager.windows.cs @@ -115,7 +115,6 @@ private async partial Task PlatformConnectCamera(CancellationToken token) private async partial Task PlatformStartCameraPreview(CancellationToken token) { - token.ThrowIfCancellationRequested(); if (mediaElement is null) { return; @@ -158,8 +157,6 @@ private partial void PlatformStopCameraPreview() async Task PlatformUpdateResolution(Size resolution, CancellationToken token) { - token.ThrowIfCancellationRequested(); - if (cameraView.SelectedCamera is null || !IsInitialized || mediaCapture is null) { return; diff --git a/src/CommunityToolkit.Maui.Camera/Extensions/CameraViewExtensions.windows.cs b/src/CommunityToolkit.Maui.Camera/Extensions/CameraViewExtensions.windows.cs index 57143d0bb9..8d525b70df 100644 --- a/src/CommunityToolkit.Maui.Camera/Extensions/CameraViewExtensions.windows.cs +++ b/src/CommunityToolkit.Maui.Camera/Extensions/CameraViewExtensions.windows.cs @@ -15,7 +15,6 @@ public static async Task UpdateAvailability(this ICameraView cameraView, Cancell public static Task InitializeCameraForCameraView(this MediaCapture mediaCapture, string deviceId, CancellationToken token) { - token.ThrowIfCancellationRequested(); try { return mediaCapture.InitializeAsync(new MediaCaptureInitializationSettings diff --git a/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs b/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs index 944bd10966..608ea47227 100644 --- a/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs +++ b/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using CommunityToolkit.Maui.Extensions; +using CommunityToolkit.Maui.Extensions; using Microsoft.Maui.Handlers; namespace CommunityToolkit.Maui.Core.Handlers; @@ -78,7 +77,6 @@ protected override NativePlatformCameraPreviewView CreatePlatformView() // reset the zoom factor to 1 void Init(ICameraView view) { - Trace.WriteLine("- - - - - Camera view initialized - - - - -"); MapCameraFlashMode(this, view); view.ZoomFactor = 1.0f; } @@ -88,33 +86,23 @@ void Init(ICameraView view) protected override async void ConnectHandler(NativePlatformCameraPreviewView platformView) { cts?.Cancel(); + cts?.Dispose(); cts = new(); try { - //Trace.WriteLine($"> > > > > ConnectHandler {this.GetHashCode()}"); base.ConnectHandler(platformView); await CameraManager.ConnectCamera(cts.Token); } - catch (OperationCanceledException) + catch (OperationCanceledException) when (cts.IsCancellationRequested) { - // If the operation was canceled, we should dispose the camera manager - // and set it to null to avoid further operations on it. - //cameraManager?.Dispose(); - //cameraManager = null; - //return; - - Trace.WriteLine($"< < < < < OperationCanceledException {this.GetHashCode()}"); + // Catch and ignore the OperationCanceledException if it was caused by our own cancellation token, + // e.g. the handler is disconnected before the camera connection process completes. } - catch (Exception ex) //when (ex is not OperationCanceledException && !isDisposed) + catch (Exception) { - //// If the connection fails, we should dispose the camera manager - //// and set it to null to avoid further operations on it. - //cameraManager?.Dispose(); - //cameraManager = null; - //// Rethrow the exception to notify the user of the failure. - //throw new CameraException("Failed to connect to camera", ex); + DisconnectHandler(platformView); + throw; } - } /// From 05044c39109a8f4cc76084d89e9d2c2402c479a8 Mon Sep 17 00:00:00 2001 From: Zhitao Pan Date: Tue, 24 Mar 2026 23:04:21 +1100 Subject: [PATCH 3/3] Address Copilot review --- .../Handlers/CameraViewHandler.shared.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs b/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs index 608ea47227..007f3b1880 100644 --- a/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs +++ b/src/CommunityToolkit.Maui.Camera/Handlers/CameraViewHandler.shared.cs @@ -86,14 +86,13 @@ void Init(ICameraView view) protected override async void ConnectHandler(NativePlatformCameraPreviewView platformView) { cts?.Cancel(); - cts?.Dispose(); - cts = new(); + using var newCts = cts = new(); try { base.ConnectHandler(platformView); - await CameraManager.ConnectCamera(cts.Token); + await CameraManager.ConnectCamera(newCts.Token); } - catch (OperationCanceledException) when (cts.IsCancellationRequested) + catch (OperationCanceledException) when (newCts.IsCancellationRequested) { // Catch and ignore the OperationCanceledException if it was caused by our own cancellation token, // e.g. the handler is disconnected before the camera connection process completes. @@ -101,7 +100,13 @@ protected override async void ConnectHandler(NativePlatformCameraPreviewView pla catch (Exception) { DisconnectHandler(platformView); - throw; + } + finally + { + if (cts == newCts) + { + cts = null; + } } } @@ -128,7 +133,6 @@ protected virtual void Dispose(bool disposing) if (disposing) { cts?.Cancel(); - cts?.Dispose(); cts = null; cameraManager?.Dispose();