Repository navigation
v16.0.0 Bug Fix-A-Palooza #3384
TheCodeTraveler
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
v16.0.0 Bug Fix-A-Palooza 🎉
We have FINALLY implemented Device Tests! These are platform-specific unit tests that run on iOS, Android, MacCatalyst and Windows. This allows us to unit test all platform-specific code and has MASSIVELY increased our Unit Test Code Coverage:
CommunityToolkit.MauiCommunityToolkit.Maui.CameraCommunityToolkit.Maui.CoreAnd thanks to the new Device Tests (along with excellent contributions from the community), this Release contains fixes for a whopping 58 Issues. 💪
Breaking Changes
To fix certain bugs, and re-align features to ensure they behave the same across all platforms, we did have to modify some APIs and the associated underlying behavior. Please find the features you use, below, and update your apps accordingly.
All packages
ArgumentNullExceptionat the call. Public and protected members of the five libraries now guard the non-nullable reference parameters they cannot handle (about 275 guards).NullReferenceExceptionlater (or was stored silently) fails at the call, with aParamName. What to do: fix the caller.IProgress<double>or aPaintbackground of null.BasePlatformBehaviorguards onlybindable. A null platform view passes through, because the AndroidIconTintColorBehavioraccepts it.<queries>entry for the launcher (MAIN/HOME) to the app's merged manifest through the package's library manifest (src/CommunityToolkit.Maui.Core/AndroidManifest.xml). Badge needs it on Android 11 and later. If your app or Play listing reviews<queries>, check the merged manifest.System.Speech10.0.11 (it was 10.0.1). No action is needed unless you pin the package. This comes from Update Unit Tests Packages #3288 on main, not from this PR.New APIs In This Release
Popupnow declaresIPaddingElement, which it already inherited fromContentViewGravatarImageSourcedeclaresIStreamImageSource, which it already inherited fromStreamImageSource, and overridesCancel()(see GravatarImageSource, after that ApiCompat run);[SupportedOSPlatform("android23.0")]on the AndroidStatusBarBehavior(see StatusBar and StatusBarBehavior).DefaultPopupSettings.BackgroundColorDarkandOnInitializing(AddPopupsupport forAppTheme, IntroduceDefaultPopupSettings.OnInitializing#3345)NumericValidationBehavior.Interval(Add interval validation to NumericValidationBehavior #3329)AvatarView
Behavior
HorizontalOptions,VerticalOptions,HeightRequest,WidthRequest,Padding,Stroke,StrokeThicknessandStrokeShapeare no longer local values, so an implicit AvatarView Style applies (and aBorderStyle withApplyToDerivedTypes).Texton every platform; iOS, MacCatalyst and Windows had previously showed an empty circle. SetTexttostring.Emptyor""to display a blank circle.GravatarImageSourceasks for its picture only after its image has a size. So:Emailchanges or a new load succeeds.Badge
Setup
<queries>entry for the launcher (MAIN/HOME) to Android apps; Badge needs it on Android 11 and later (see All packages).Behavior
Badge.SetCountwrites one trace line and returns, instead of throwing aCOMExceptionthat ended the app. No badge appears, so package the app if you need the taskbar badge.ColorAnimationExtensions
Behavior
BackgroundColorToandTextColorTowith arateof 0 throwArgumentOutOfRangeExceptionfrom the call. Before, the error came later on the animation ticker and ended the app. Pass a rate above 0.DialogFragment (Android)
Behavior
DialogFragmentServicelays the dialog of a modal page out like the activity's window, for every status bar color.DrawingView
Behavior
GetImageStreamwith no lines returnsStream.Nullinstead of throwingInvalidOperationException, and a NaN image size, or one that is infinite on both sides, throwsArgumentException. HandleStream.Nullwhere you caught the exception.ImageOptions.DesiredSizehas its pixel size rounded half away from zero on Android, iOS and Mac Catalyst, as Windows already did. A 1920 x 1080 target of a 411 DIP canvas gives 1920 x 934 on Android, where it gave 1919 x 934.DrawingLineCompleted, a lost capture raisesDrawingLineCancelled, and a second pointer no longer starts or changes a line. No action is needed unless you worked around the old behavior.Smaller changes
DrawActiondraws in a canvas state of its own.Expander
Setup
UseMauiCommunityToolkitregisters the Expander layout handler (see Behavior below), so keep calling it at startup. That handler keeps its own platform view even when the app setsLayoutHandler.PlatformViewFactory.Behavior
ExpanderLayoutHandler, so a handler an app registers forGridno longer applies to the Expander's internal Grid;LayoutHandler.Mapperchanges still apply.LayoutHandler.PlatformViewFactoryno longer creates the platform view of that layout, so the layout keeps the platform view that gives the header this support. The factory still creates the platform views of the app's other layouts, including the ones in the Expander'sHeaderandContent, and on Android also the platform view of that layout.Smaller changes
Directioncan be changed again by bindings and Styles.FileSaver and FolderPicker
API
ArgumentNullExceptionat the call;FileSaverandFolderPickerstill return the exception in the result. (see All packages)Behavior
progressreports percentages from 0 to 100. It reported fractions from 0 to 1 during the copy, then a final 100, so remove any* 100you applied.FilePathis itscontent://URI. It used to fail withFileSaveException. Open such a path with the ContentResolver.fileNameorstream(and a nullinitialPathfor FolderPicker) comes back as anArgumentNullExceptionin the result, without showing the picker.GravatarImageSource
Behavior
HttpClient.Timeoutlimits the whole download, and a body that fails halfway givesStream.Null.CancellationToken.Nonenever throws. Only a token that you pass and cancel makesGetStreamAsyncand the defaultStreamfunction throwOperationCanceledException, instead of returningStream.Null. .NET MAUI catches it for the loads that it cancels itself, so a view keeps the picture of the load that replaced it.Cancel()ends only the loads started without a cancelable token, which then returnStream.Null. Its task always completes withtrue.StreamorGetStreamAsyncwith your own token.WidthRequestandHeightRequestthat the Button sets, or at Gravatar's default 80 pixels, and again when those requests change. It was requested at the Button's laid-out size, so on iOS and Mac Catalyst each picture made the Button larger until it was as wide as the screen (older than this PR).Image,ImageButtonandAvatarViewstill request the size they are laid out at.Buttonwithout size requests now gets an 80-pixel picture instead of one the size of the laid-out Button: on a 420 dpi Android screen, a 30 dp icon instead of 15 dp. SetHeightRequeston the Button for a smaller picture.ContentLayoutTop or Bottom) needs aHeightRequestthat leaves room for the picture.Textneeds aHeightRequestorMinimumHeightRequest. The .NET MAUI app template's Button style setsMinimumHeightRequestto 44.ImageOpenedevent, which a picture decoded from a stream has already raised.KeyboardExtensions
Smaller changes
IsSoftKeyboardShowingcalled from another thread blocks until the UI thread answers. The tasks ofHideKeyboardAsyncandShowKeyboardAsynccomplete once the UI thread has done the work.MaxLengthReachedBehavior
API
MaxLengthReachedEventArgsrejects null for its non-nullable members.Behavior
MaxLengthReachedBehaviorhides the keyboard only while its own InputView has the focus, and through the view's own window. So Android no longer hides the keyboard of the view the user moved to, and the keyboard goes away in a modal page or a Popup too. No action is needed.NavigationBar (Android)
Behavior
Coloris still transparent, but it is now a plain default value, so reading the color no longer makesIsSettrue. As a result:ColororStylegives the window back its original color.Popup
API
Options.SetPopupDefaults(null)andSetPopupOptionsDefaults(null)no longer store null.async voidPopupExtensions.ShowPopupoverloads throw to the caller instead of posting the exception to theSynchronizationContext.Behavior
BackgroundColoris null by default, and Styles now apply.Margin,Padding,HorizontalOptions,VerticalOptions,CanBeDismissedByTappingOutsideOfPopupandBackgroundColorare now default values. 15.0.1 set them as local values in the constructor.Popup.BackgroundColorreads null until something sets it.DefaultPopupSettings.BackgroundColor, or in the dark themeBackgroundColorDark: black with AddPopupsupport forAppTheme, IntroduceDefaultPopupSettings.OnInitializing#3345 in this release, where 15.0.1 showed white.BackgroundColorwhere a fixed color is needed, and check popups in the dark theme.ShowPopupAsync. When navigation removes the popup page withoutCloseAsync,ShowPopupAsynccompletes andClosedis raised exactly once. Before, neither happened, and code that awaited it stayed suspended.PopModalAsyncorPopAsynccalled by other code,Window.Pagebeing replaced, or Shell popping the popup out of a modalNavigationPage.Closedhandler can show another popup.WasDismissedByTappingOutsideOfPopupis true. It now means "closed without a result: by tapping outside the popup, or because navigation removed it"; it is false only afterCloseAsync.ShowPopupAsync<TResult>,Resultis null for a reference or nullable type. Reading it for a value type such asintthrowsPopupResultException, as after a tap outside.WasDismissedByTappingOutsideOfPopupfirst. Subscribe toClosedif you must tell a tap from a navigation removal.Closedis raised without a precedingOpened.CancellationTokenused as a timeout keeps working.CloseAsyncandClosePopupAsyncof a popup shown on a modalNavigationPageclose only the popup.CloseAsyncused to pop the whole page and never complete, andClosePopupAsyncthrewPopupBlockedException.PopupBlockedExceptionand pops nothing:CloseAsync,Popup.CloseAsyncandClosePopupAsync(also throughIPopupService) wait for earlier popup operations. When the close finally runs, they check again that the popup is still the page on top.ShowPopupAsyncwas showing, or a modal page your code pushed. They already threwPopupBlockedExceptionand popped nothing when that page was on top at the call.ClosePopupAsyncstill targets the popup that was the most recent when you called it, not the newer one. Call it again to close the newer one.async void) when an earlier tap, Back or navigation has already closed the popup;NavigationPagecancels aPopAsyncthat waited behind a navigation that failed). The popup stays open, and a later tap closes it.ShowPopupAsync,CloseAsyncandClosePopupAsyncthrowOperationCanceledExceptionwhen the app cancels the navigation.Window.ModalPoppingorApplication.ModalPoppinghandler that setse.Cancel, or aShell.Navigatinghandler that cancels),Popup.CloseAsync,Popup<T>.CloseAsync,ClosePopupAsyncandIPopupService.ClosePopupAsyncthrowOperationCanceledException. The popup stays open and usable,Closedis not raised, and the navigation lock is released.Shell.Navigatinghandler cancels the navigation that shows a popup (ShowPopupAsyncof a Shell, or of a page orINavigationunder a Shell root), everyShowPopupAsyncoverload, also throughIPopupService, throwsOperationCanceledExceptionand nothing is shown.ShowPopupand the tap outside or Android Back show nothing, or do nothing, and throw nothing.OperationCanceledException when (!token.IsCancellationRequested).ModalPoppinghandler that cancels makes .NET MAUI retry the pop in a loop, soCloseAsynccannot return. Cancel inShell.Navigatingthere instead.Smaller changes
OnDisappearingof a modal page that is closing appears on the page below.WidthRequestorHeightRequestlarger than the window is limited to the window.Openedis raised once per show.PopupClosedandClosedare raised after the navigation lock is released, in the same order as before.RatingView
Smaller changes
FillOptionchanges.SelectAllTextBehavior
API
SelectAllTextBehavioron Windows throwsArgumentNullExceptioninstead ofInvalidOperationExceptionfor a null platform view (only a derived class can reach it).SemanticOrderView
Behavior
ViewOrderthat the container holds are listed first in its tab focus children, followed by the rest of its content.TabIndexthat the app set is no longer overwritten. The exception: views ofViewOrderthat a control within the container (aScrollView, for example) holds, or that are outside the container, still get aTabIndexfrom the order.Smaller changes
ContentViewHandler.PlatformViewFactoryno longer creates the SemanticOrderView's platform view, soViewOrderapplies.CreatePlatformViewin a handler derived fromSemanticOrderViewHandler, and register it afterUseMauiCommunityToolkit.Snackbar and Toast
Behavior
Toast.ShoworToast.Dismissafter an earlier toast, and aSnackbar.ShoworSnackbar.Dismisswhile a snackbar was shown, removed every notification of the app (AppNotificationManager.RemoveAllAsync). That included the other alert and the notifications the app shows itself, on screen and in the Action Center.Showno longer clears the app's other notifications, a Toast or Snackbar shown while another notification banner of the app is on screen waits behind that banner, as Windows queues banners.Shownow returns once Windows lists the new notification. That takes up to 1 s longer when Windows lists nothing for the app, for example while its notifications are off. A Snackbar whoseDurationis zero or less does not wait.ShowandDismisscalls now take effect in call order. A call whose token is cancelled before its turn removes nothing and throwsOperationCanceledException.ShoworDismissTask inside aShownorDismissedhandler:.Wait()and.Resultdeadlock there.Smaller changes
Duration, raisesDismissedonce and runs its action on the UI thread.Showcan take up to 1 s longer while the app's notifications are off (see Behavior above).Showno longer throws for aDurationsuch asTimeSpan.MaxValueorTimeSpan.MinValue.Durationbelow 1 ms shows it for 1 ms, andDismissedis raised once.Duration.SpeechToText
Setup
System.Speech10.0.11 on Windows (see All packages).Smaller changes
StartListenAsyncwith null options throwsArgumentNullExceptionfrom the call (see All packages: null arguments). With valid options, a canceled token still returns a canceled Task, so awaiting it throwsOperationCanceledException.StartListenAsynccompletes without an exception and without listening, even if its token is canceled afterwards.StopListenAsyncorDisposeAsync(on Windows, also a newerStartListenAsync) is called while the start waits for the platform.StateContainer
Behavior
StateKeythat is not defined throwsStateContainerExceptionwith its own message and no inner exception. It used to throw the "assigned to multiple StateViews" message, so change any code that matches the message text.StatusBar and StatusBarBehavior
API
StatusBarBehaviorrequires Android 6.0 (API 23). The Android type is now[SupportedOSPlatform("android23.0")], likeStatusBaralready was.if (OperatingSystem.IsIOS() || OperatingSystem.IsAndroidVersionAtLeast(23)). NotIsAndroidVersionAtLeast(23)alone: it is false on iOS.StatusBarBehaviordeclared in XAML gets no warning.Setup
UIViewControllerBasedStatusBarAppearancefrom Info.plist if you set it to false forStatusBar.SetStyle(see Behavior below).StatusBarBehaviorneeds a minimum Android version of 23 or a guard (see API above). CommunityToolkit.Maui's minimum Android version stays 5.0 (API 21), the same as .NET MAUI 10's.Behavior
StatusBar.SetStyleandStatusBarBehavior.SetStyleno longer needsUIViewControllerBasedStatusBarAppearanceset to false in Info.plist.LightContentorDarkContentreplacespreferredStatusBarStyleuntil the app ends, for every view controller that does not choose its own style (the system pickers included).Defaultgives each view controller its own style back.SetStylecalled off the main thread throwsUIKitThreadAccessException, in Release builds too, so call it on the main thread.Smaller changes
StringToListConverter
Smaller changes
Separatorslist, as documented; before, it split atSeparator.TouchBehavior
Smaller changes
ShouldMakeChildrenInputTransparenttakes effect when it changes.DefaultOpacity,HoveredOpacityorPressedOpacitythrowsArgumentOutOfRangeException, where 15.0.1 accepted it.CurrentHoverStatussays, a release over the element completes the tap (Command,TouchGestureCompleted), and a release outside it cancels.Entered. So a release outside the element could complete, and one over it could leave the press running.UserStoppedTypingBehavior
API
async voidoverrides ofUserStoppedTypingBehaviorthrow to the caller instead of posting the exception to theSynchronizationContext.Behavior
UserStoppedTypingBehaviorhides the keyboard only while its own InputView has the focus, and through the view's own window. So Android no longer hides the keyboard of the view the user moved to, and the keyboard goes away in a modal page or a Popup too. No action is needed.UserStoppedTypingBehavior.StoppedTypingTimeThresholdrejects a negative value. A negative value is ignored and the previous value is kept (MAUI logs a warning). Before, it failed insideTask.Delay. Use 0 or more.Validation behaviors
API
async voidoverrides ofValidationBehaviorthrow to the caller instead of posting the exception to theSynchronizationContext.Smaller changes
Errorsis set on the UI thread, andIsRunningis reset after a cancel.What's Changed
[housekeeping] Automated PR to fix formatting errors #3330
Popupsupport forAppTheme, IntroduceDefaultPopupSettings.OnInitializingby @stephenquan in AddPopupsupport forAppTheme, IntroduceDefaultPopupSettings.OnInitializing#3345Housekeeping
New Contributors
Full Changelog: 15.0.1...16.0.0
This discussion was created from the release v16.0.0 Bug Fix-A-Palooza.
All reactions