Migration Guide to v3.0
Version 3.0 is the migration from UraniumUI v2.16 to the .NET 10 generation. v3.0.0 has not been published yet; the develop branch represents the current v3 code.
You can see the related v3 breaking-change PRs from here and all v3 milestone PRs from here.
Before you update
- Upgrade your application to .NET 10 and .NET MAUI 10 first.
- Upgrade all UraniumUI packages together. Do not mix v2.16 and v3 packages in the same app.
- If your app must stay on .NET 9, stay on UraniumUI v2.16.0.
- Rebuild after each section below if you have custom controls, custom dialog services, or custom TreeView integrations.
.NET 10 is required
UraniumUI v3 targets .NET 10 only. All .NET 9 target frameworks were removed from the core and add-on packages.
Update your application target frameworks from net9.0 to net10.0.
Before:
<TargetFrameworks>net9.0;net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.19041.0</TargetFrameworks>
After:
<TargetFrameworks>net10.0;net10.0-android;net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>
If your app pins transitive packages directly, review these package versions after the upgrade:
Microsoft.Maui.Controlsis based on MAUI10.0.71in the v3 branch.InputKit.Mauiis updated to4.6.0.Plainer.Mauiis updated to1.8.0.CommunityToolkit.Mauiis updated to13.0.0inUraniumUI.Dialogs.CommunityToolkit.
DatePickerField now uses nullable dates
material:DatePickerField now supports empty values. The date-related bindable properties changed from DateTime to DateTime?:
DatePickerField.DateDatePickerField.MinimumDateDatePickerField.MaximumDate
The default Date value is now null, and clear/reset operations also set Date to null.
Before:
public DateTime BirthDate { get; set; } = DateTime.Today;
After:
public DateTime? BirthDate { get; set; }
If the field is required, make the bound property nullable and apply validation instead of relying on a default date value.
[Required]
public DateTime? BirthDate { get; set; }
If existing code requires a non-null value, coalesce where you use it.
var selectedDate = BirthDate ?? DateTime.Today;
DatePickerField opens a date prompt dialog
DatePickerField no longer exposes a native picker as its main content. It renders the selected value through a label and opens IDialogService.DisplayDatePromptAsync(...) when tapped.
No change is required if your app uses .UseUraniumUI() and the built-in dialog service.
Check your code if you customized the old native picker behavior:
- If you replaced or inspected
DatePickerField.Contentas aDatePicker, update that code.Contentis now aLabelby default. DatePickerField.DatePickerViewstill exists for compatibility with date picker properties, but it is not the visible interactive control.- Apps with a custom
IDialogServicemust implement the new date prompt method described below.
Custom IDialogService implementations must add the date prompt API
IDialogService has a new required method:
Task<DateTime?> DisplayDatePromptAsync(
string title,
DateTime? selectedDate = null,
DateTime? minimumDate = null,
DateTime? maximumDate = null,
string accept = "OK",
string cancel = "Cancel",
string clear = "Clear",
string today = "Today");
If your app implements IDialogService, add this method or the app will not compile against v3.
The built-in behavior is:
acceptreturns the selected date.cancelreturns the original selected date.clearreturnsnull.todayselectsDateTime.Todaywhen it is inside the allowed date range.
IDialogService also has a cancellable custom view dialog overload:
Task<bool> DisplayViewAsync(
string title,
View content,
string okText,
string cancelText);
This overload has a default interface implementation that throws NotSupportedException. The built-in dialog services implement it. Override it in custom services if your app uses cancellable custom view dialogs or controls that depend on this overload.
Custom IDropdown implementations must add Close
IDropdown now includes a Close() method.
public interface IDropdown : IView
{
// Existing members...
void Close();
}
If you implemented IDropdown directly, add Close() and close the open dropdown/popup from that method.
No change is required for the built-in Dropdown and DropdownField controls.
TreeView was reworked for virtualization
material:TreeView now uses a flat, virtualized CollectionView internally instead of recursively creating node holder views.
This improves large tree performance, but it affects code that depended on TreeView internals.
Removed API:
TreeViewNodeHolderView
TreeView.AllNodeViews
Use the public selection APIs instead of walking generated node views:
treeView.SelectedItem
treeView.SelectedItems
treeView.SelectedItemChanged
treeView.SelectedItemsChanged
treeView.SelectedItemChangedCommand
treeView.SelectedItemsChangedCommand
Do not wrap large TreeView instances in a ScrollView. Let TreeView own scrolling so virtualization can work.
<!-- Before -->
<ScrollView>
<material:TreeView ItemsSource="{Binding Nodes}" />
</ScrollView>
<!-- After -->
<material:TreeView ItemsSource="{Binding Nodes}" />
ItemTemplate still receives your original item as its binding context.
<material:TreeView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}" />
</DataTemplate>
</material:TreeView.ItemTemplate>
ExpanderTemplate now receives a TreeViewNode. Existing bindings such as IsExpanded and IsLeaf still work. If the expander template needs the original item, bind through Item.
<material:TreeView.ExpanderTemplate>
<DataTemplate>
<Label Text="{Binding Item.Name}" />
</DataTemplate>
</material:TreeView.ExpanderTemplate>
If you subclassed TreeViewHierarchicalSelectBehavior, update overrides that used TreeViewNodeHolderView.
Before:
protected override void CheckStateItself(TreeViewNodeHolderView holder, bool forcedSemiSelected = false)
After:
protected override void CheckStateItself(TreeViewNodeView row, bool forcedSemiSelected = false)
FormView has async validation support
v3 adds UraniumUI.Controls.FormView, which extends the InputKit FormView with async validation APIs:
SubmitAsync(...)ValidateFormAsync(...)IFormValidatorFormValidationHandlerValidationModelShowValidationSummaryIsBusyandIsValidating- attached
FormView.ValidationPath - attached
FormView.IsBusyIndicator
AutoFormView and built-in form dialogs now use this validation flow. Dialog form submissions no longer close when validation fails.
If you have custom form dialog code, follow the v3 pattern:
if (await formView.SubmitAsync())
{
// Close the dialog and return the submitted model.
}
If C# code imports both InputKit.Shared.Controls and UraniumUI.Controls, unqualified FormView references may become ambiguous. Fully qualify the type or remove the unused namespace.
var formView = new UraniumUI.Controls.FormView();
Manual handler registration
No startup method was renamed. UseUraniumUI(), UseUraniumUIMaterial(), UseUraniumUIBlurs(), and UseUraniumUIWebComponents() are unchanged.
If you use .UseUraniumUI(), no action is required for handlers.
If your app copied UraniumUI handler registrations manually, add the Select handler:
handlers.AddHandler(typeof(UraniumUI.Controls.Select), typeof(StatefulContentViewHandler));
XAML namespaces are unchanged
The existing XAML namespace URIs are unchanged:
xmlns:uranium="http://schemas.enisn-projects.io/dotnet/maui/uraniumui"
xmlns:material="http://schemas.enisn-projects.io/dotnet/maui/uraniumui/material"
v3 adds new XAML-visible controls under these existing namespaces, including uranium:CalendarView, uranium:Select, uranium:FormView, and material:SelectField.
Migration checklist
- Update the app to .NET 10 and .NET MAUI 10.
- Update all UraniumUI packages to v3 together.
- Change
DatePickerFieldview model properties fromDateTimetoDateTime?where they bind toDate,MinimumDate, orMaximumDate. - Implement
IDialogService.DisplayDatePromptAsync(...)in custom dialog services. - Implement
IDropdown.Close()in custom dropdown controls. - Remove usages of
TreeViewNodeHolderViewandTreeView.AllNodeViews. - Remove external
ScrollViewwrappers around largeTreeViewcontrols. - Update custom form dialog code to await
SubmitAsync()before closing. - Add the
Selecthandler if your app registers UraniumUI handlers manually.