Expose DispatcherExtensions so it could be used outside of Maui as well#30488
Expose DispatcherExtensions so it could be used outside of Maui as well#30488rmarinho merged 6 commits intodotnet:net10.0from
Conversation
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
There was a problem hiding this comment.
Pull Request Overview
This PR exposes new DispatcherExtensions methods so they can be used outside of .NET MAUI and updates the Public API accordingly.
- Adds
DispatchIfRequiredand multipleDispatchIfRequiredAsyncoverloads toDispatcherExtensions - Updates unit tests to cover all new extension overloads
- Removes duplicate dispatcher helpers in
Controlsand adds necessary using directives
Reviewed Changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/Core/src/Dispatching/DispatcherExtensions.cs | Added new DispatchIfRequired and various DispatchIfRequiredAsync extension methods |
| src/Core/tests/UnitTests/Dispatching/DispatcherTests.cs | Added 14 new facts to verify each overload of the new extension methods |
| src/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt | Registered the new extension methods in the Public API |
| src/Core/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt | Registered the new extension methods in the Public API |
| src/Core/src/PublicAPI/net/PublicAPI.Unshipped.txt | Registered the new extension methods in the Public API |
| src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt | Registered the new extension methods in the Public API |
| src/Core/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt | Registered the new extension methods in the Public API |
| src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt | Registered the new extension methods in the Public API |
| src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt | Registered the new extension methods in the Public API |
| src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt | Registered the new extension methods in the Public API |
| src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableItemsSource.cs | Added using Microsoft.Maui.Dispatching; to use shared dispatcher extensions |
| src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs | Added using Microsoft.Maui.Dispatching; to use shared dispatcher extensions |
| src/Controls/src/Core/DispatcherExtensions.cs | Removed local DispatchIfRequired* implementations in favor of the shared ones in Core |
| src/Controls/src/Core/BindingExpression.cs | Added using Microsoft.Maui.Dispatching; |
| src/Controls/src/Core/AppThemeBinding.cs | Added using Microsoft.Maui.Dispatching; |
Comments suppressed due to low confidence (4)
src/Core/src/Dispatching/DispatcherExtensions.cs:97
- New public extension methods require corresponding updates to the official documentation (under /docs) to describe DispatchIfRequired and its overloads.
public static void DispatchIfRequired(this IDispatcher dispatcher, Action action)
src/Core/src/Dispatching/DispatcherExtensions.cs:112
- Fill in the
<param>and<returns>XML comments for the DispatchIfRequiredAsync overloads to clearly document parameters and return values.
/// <param name="dispatcher"></param>
src/Controls/src/Core/BindingExpression.cs:13
- [nitpick] This using directive appears unused in this file; consider removing it to keep imports clean.
using Microsoft.Maui.Dispatching;
src/Controls/src/Core/AppThemeBinding.cs:6
- [nitpick] This using directive appears unused in this file; consider removing it to keep imports clean.
using Microsoft.Maui.Dispatching;
jsuarezruiz
left a comment
There was a problem hiding this comment.
There are some build errors:
C:\a\_work\1\s\src\Controls\src\Core\Platform\Windows\CollectionView\GroupedItemTemplateCollection.cs(83,26): error CS1061: 'IDispatcher' does not contain a definition for 'DispatchIfRequired' and no accessible extension method 'DispatchIfRequired' accepting a first argument of type 'IDispatcher' could be found (are you missing a using directive or an assembly reference?) [C:\a\_work\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
C:\a\_work\1\s\src\Controls\src\Core\Platform\Windows\CollectionView\ObservableItemTemplateCollection.cs(146,26): error CS1061: 'IDispatcher' does not contain a definition for 'DispatchIfRequired' and no accessible extension method 'DispatchIfRequired' accepting a first argument of type 'IDispatcher' could be found (are you missing a using directive or an assembly reference?) [C:\a\_work\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
C:\Users\cloudtest\.nuget\packages\microsoft.windowsappsdk\1.7.250606001\buildTransitive\Microsoft.UI.Xaml.Markup.Compiler.interop.targets(845,9): error MSB3073: The command ""C:\Users\cloudtest\.nuget\packages\microsoft.windowsappsdk\1.7.250606001\buildTransitive\..\tools\net6.0\..\net472\XamlCompiler.exe" "C:\a\_work\1\s\artifacts\obj\Controls.Core\Release\net10.0-windows10.0.19041.0\\input.json" "C:\a\_work\1\s\artifacts\obj\Controls.Core\Release\net10.0-windows10.0.19041.0\\output.json"" exited with code 1. [C:\a\_work\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
C:\a\_work\1\s\src\Compatibility\Core\src\iOS\VisualElementTracker.cs(347,21): error CS1061: 'IDispatcher' does not contain a definition for 'DispatchIfRequired' and no accessible extension method 'DispatchIfRequired' accepting a first argument of type 'IDispatcher' could be found (are you missing a using directive or an assembly reference?) [C:\a\_work\1\s\src\Compatibility\Core\src\Compatibility.csproj::TargetFramework=net10.0-ios18.5]
C:\a\_work\1\s\src\Compatibility\Core\src\iOS\VisualElementTracker.cs(347,21): error CS1061: 'IDispatcher' does not contain a definition for 'DispatchIfRequired' and no accessible extension method 'DispatchIfRequired' accepting a first argument of type 'IDispatcher' could be found (are you missing a using directive or an assembly reference?) [C:\a\_work\1\s\src\Compatibility\Core\src\Compatibility.csproj::TargetFramework=net10.0-maccatalyst18.5]
C:\a\_work\1\s\src\Controls\src\Core\Platform\Windows\CollectionView\ObservableItemTemplateCollection.cs(146,26): error CS1061: 'IDispatcher' does not contain a definition for 'DispatchIfRequired' and no accessible extension method 'DispatchIfRequired' accepting a first argument of type 'IDispatcher' could be found (are you missing a using directive or an assembly reference?) [C:\a\_work\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.20348.0]
C:\a\_work\1\s\src\Controls\src\Core\Platform\Windows\CollectionView\GroupedItemTemplateCollection.cs(83,26): error CS1061: 'IDispatcher' does not contain a definition for 'DispatchIfRequired' and no accessible extension method 'DispatchIfRequired' accepting a first argument of type 'IDispatcher' could be found (are you missing a using directive or an assembly reference?) [C:\a\_work\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.20348.0]
C:\Users\cloudtest\.nuget\packages\microsoft.windowsappsdk\1.7.250606001\buildTransitive\Microsoft.UI.Xaml.Markup.Compiler.interop.targets(845,9): error MSB3073: The command ""C:\Users\cloudtest\.nuget\packages\microsoft.windowsappsdk\1.7.250606001\buildTransitive\..\tools\net6.0\..\net472\XamlCompiler.exe" "C:\a\_work\1\s\artifacts\obj\Controls.Core\Release\net10.0-windows10.0.20348.0\\input.json" "C:\a\_work\1\s\artifacts\obj\Controls.Core\Release\net10.0-windows10.0.20348.0\\output.json"" exited with code 1. [C:\a\_work\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.20348.0]
C:\a\_work\1\s\src\Compatibility\Core\src\Android\CollectionView\ObservableGroupedSource.cs(235,35): error CS1061: 'IDispatcher' does not contain a definition for 'DispatchIfRequired' and no accessible extension method 'DispatchIfRequired' accepting a first argument of type 'IDispatcher' could be found (are you missing a using directive or an assembly reference?) [C:\a\_work\1\s\src\Compatibility\Core\src\Compatibility.csproj::TargetFramework=net10.0-android36.0]
C:\a\_work\1\s\src\Compatibility\Core\src\Android\CollectionView\ObservableItemsSource.cs(94,26): error CS1061: 'IDispatcher' does not contain a definition for 'DispatchIfRequired' and no accessible extension method 'DispatchIfRequired' accepting a first argument of type 'IDispatcher' could be found (are you missing a using directive or an assembly reference?) [C:\a\_work\1\s\src\Compatibility\Core\src\Compatibility.csproj::TargetFramework=net10.0-android36.0]
10 Error(s)
Could you review it?
@jsuarezruiz I’ve resolved the build errors. Please let me know if any further changes are required. |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
02435a8 to
2af8944
Compare
2af8944 to
b5856bc
Compare
@PureWeen Rebased the branch and resolved the conflicts. |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
PureWeen
left a comment
There was a problem hiding this comment.
RADLE : warning : We recommend using a newer Android Gradle plugin to use compileSdk = 34 [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(201,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequired(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Action! action) -> void' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(202,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequiredAsync(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Action! action) -> System.Threading.Tasks.Task!' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(203,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequiredAsync(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Func<System.Threading.Tasks.Task!>! action) -> System.Threading.Tasks.Task!' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(204,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequiredAsync(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Func! func) -> System.Threading.Tasks.Task!' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(205,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequiredAsync(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Func<System.Threading.Tasks.Task!>! funcTask) -> System.Threading.Tasks.Task!' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
Build FAILED.
GRADLE : warning : We recommend using a newer Android Gradle plugin to use compileSdk = 34 [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(201,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequired(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Action! action) -> void' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(202,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequiredAsync(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Action! action) -> System.Threading.Tasks.Task!' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(203,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequiredAsync(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Func<System.Threading.Tasks.Task!>! action) -> System.Threading.Tasks.Task!' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(204,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequiredAsync(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Func! func) -> System.Threading.Tasks.Task!' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
/mnt/vss/_work/1/s/src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt(205,1): error RS0025: The symbol 'static Microsoft.Maui.Dispatching.DispatcherExtensions.DispatchIfRequiredAsync(this Microsoft.Maui.Dispatching.IDispatcher! dispatcher, System.Func<System.Threading.Tasks.Task!>! funcTask) -> System.Threading.Tasks.Task!' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/mnt/vss/_work/1/s/src/Core/src/Core.csproj::TargetFramework=net10.0-android36.0]
1 Warning(s)
5 Error(s)
6872307 to
bb5571d
Compare
@PureWeen I have resolved the build errors and conflicts. |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
@PureWeen The failed UI and device tests are passing locally. |
| if (dispatcher is null) | ||
| { | ||
| var currentThreadDispatcher = Dispatcher.GetForCurrentThread(); | ||
| if (currentThreadDispatcher?.IsDispatchRequired == true) | ||
| { | ||
| currentThreadDispatcher.Dispatch(action); | ||
| return; | ||
| } | ||
| } |
There was a problem hiding this comment.
What is the reason for this additional bit of code? If dispatcher is null, then it probably should throw. You are asking it to run on the specified dispatcher, and it may give random results if there is no dispatcher.
There was a problem hiding this comment.
@mattleibow When the dispatcher is null, the method currently executes the action directly, which may not align with expectations in certain scenarios—such as in the PropertyChangeBindingsOccurThroughMainThread test, where InvokeOnMainThread is expected to be called when isOnBackgroundThread is true.
To address this, the additional logic uses Dispatcher.GetForCurrentThread() as a fallback to ensure the action is dispatched appropriately. This approach helps maintain thread-safety and ensures consistent behavior even when an explicit dispatcher reference is not available. It also allows mock dispatchers in test environments to be respected, avoiding unintended test failures or bypassed execution paths.
This change preserves expected behavior without altering the existing public contract and provides a safer fallback in cases where the dispatcher is not explicitly passed. Please let me know if you have any suggestions.
There was a problem hiding this comment.
I think is fine is just an extra check, it will not throw and fallback to the previous way of just calling the action() if IsDispatchRequired on the new found dispatcher is false.
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description of Change
Exposing the DispatcherExtensions so it could be used outside of Maui as well.
Issues Fixed
Fixes #29258