Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
438cdf0
Add Aspire.Hosting.Maui and Tests projects
jfversluis Oct 22, 2025
46a5ea4
Add AspireWithMaui playground
jfversluis Oct 22, 2025
ed758be
Add Windows app support
jfversluis Oct 22, 2025
bf89ebb
Detect unsupported platform
jfversluis Oct 22, 2025
2ab330d
Detect missing TFM + tests
jfversluis Oct 22, 2025
a008c1d
Throw on missing TFM
jfversluis Oct 22, 2025
919ee0b
Refactoring
jfversluis Oct 22, 2025
962095d
Update CODEOWNERS
jfversluis Oct 22, 2025
8558d87
Update src/Aspire.Hosting.Maui/MauiWindowsExtensions.cs
jfversluis Oct 22, 2025
8f973ca
Better UX for missing TFM
jfversluis Oct 22, 2025
7d3cfcd
Set Windows TFM args earlier in AddWindowsDevice
jfversluis Oct 22, 2025
4bcb3e4
Reshuffle workload restore scripts
jfversluis Oct 23, 2025
9e21c55
Use dotnet msbuild for GetPlatformTargetFramework
jfversluis Oct 23, 2025
a017a0c
Iterate over resource model for Windows resources
jfversluis Oct 23, 2025
1c8c669
Add AddWindowsDevice overload
jfversluis Oct 23, 2025
604e7df
MauiResource inherits from ProjectResource
jfversluis Oct 23, 2025
d4d0d9b
Add public API definition
jfversluis Oct 23, 2025
3d41998
Add Maui projects to the solution (for now).
mitchdenny Oct 23, 2025
23d11bd
Minor improvement
jfversluis Oct 23, 2025
4566eef
Merge branch 'jfversluis/maui-windows' of https://github.com/dotnet/a…
jfversluis Oct 23, 2025
29443e2
Fix test when running on non-Windows
jfversluis Oct 23, 2025
d2f0607
Fix test build error
jfversluis Oct 23, 2025
5fc6849
Scripts separate flag for installing MAUI workload
jfversluis Oct 23, 2025
cc5f433
Merge branch 'main' into jfversluis/maui-windows
jfversluis Oct 23, 2025
bf8d872
Add a second solution file for Maui Stuff.
mitchdenny Oct 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
/src/Aspire.Hosting.Azure.AppContainers @captainsafia @eerhardt
/src/Aspire.Hosting.Azure.AppService @captainsafia @eerhardt
/src/Aspire.Hosting.Docker @captainsafia
/src/Aspire.Hosting.Maui @jfversluis

# tests

/tests/Aspire.EndToEnd.Tests @radical @eerhardt
/tests/Aspire.Hosting.Maui.Tests @jfversluis
/tests/Aspire.Hosting.Testing.Tests @reubenbond
/tests/Aspire.Hosting.Tests @mitchdenny
/tests/Aspire.Templates.Tests @radical @eerhardt
Expand All @@ -20,3 +22,4 @@
# playground apps
/playground/deployers @captainsafia
/playground/publishers @captainsafia
/playground/AspireWithMaui @jfversluis
1 change: 1 addition & 0 deletions Aspire.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<Project Path="src/Aspire.Hosting.Kafka/Aspire.Hosting.Kafka.csproj" />
<Project Path="src/Aspire.Hosting.Keycloak/Aspire.Hosting.Keycloak.csproj" />
<Project Path="src/Aspire.Hosting.Kubernetes/Aspire.Hosting.Kubernetes.csproj" />
<Project Path="src/Aspire.Hosting.Maui/Aspire.Hosting.Maui.csproj" />
<Project Path="src/Aspire.Hosting.Milvus/Aspire.Hosting.Milvus.csproj" />
<Project Path="src/Aspire.Hosting.MongoDB/Aspire.Hosting.MongoDB.csproj" />
<Project Path="src/Aspire.Hosting.MySql/Aspire.Hosting.MySql.csproj" />
Expand Down
479 changes: 479 additions & 0 deletions AspireWithMaui.slnx

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
<add key="dotnet9" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json" />
<add key="dotnet10" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10/nuget/v3/index.json" />
<add key="dotnet10-workloads" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet10-workloads/nuget/v3/index.json" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not so sure about this change. With SDK workloads, you want to be careful when you add dependencies to feeds that have unreleased versions of workloads or workload sets, as that can potentially put your machine in a torn state if a version is picked up and it depends on something else that is not part of the feed.

IMO we should probably consider reverting this particular feed, and instead see if there is a way to have the SDK team push only released versions into dotnet-public to prevent the above.

cc: @marcpopMSFT in case anything I said above is wrong.

<add key="dotnet-libraries" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-libraries/nuget/v3/index.json" />
<add key="dotnet9-transport" value="https://dnceng.pkgs.visualstudio.com/public/_packaging/dotnet9-transport/nuget/v3/index.json" />
</packageSources>
Expand All @@ -35,6 +36,9 @@
<packageSource key="dotnet10">
<package pattern="*" />
</packageSource>
<packageSource key="dotnet10-workloads">
<package pattern="*" />
</packageSource>
<packageSource key="dotnet-libraries">
<package pattern="Microsoft.DeveloperControlPlane*" />
<package pattern="System.CommandLine" />
Expand Down
4 changes: 3 additions & 1 deletion eng/Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
<ProjectToBuild Include="$(RepoRoot)eng\dcppack\**\*.csproj" />
<ProjectToBuild Include="$(RepoRoot)eng\dashboardpack\**\*.csproj" />

<ProjectToBuild Include="$(RepoRoot)playground\**\*.csproj" />
<!-- Exclude only the MAUI client project as it requires the .NET MAUI workload.
Other projects in the AspireWithMaui playground can still be built. -->
<ProjectToBuild Include="$(RepoRoot)playground\**\*.csproj" Exclude="$(RepoRoot)playground\AspireWithMaui\AspireWithMaui.MauiClient\*.csproj" />
</ItemGroup>

<!-- `$(SkipTestProjects)` allows skipping test projects from being
Expand Down
34 changes: 34 additions & 0 deletions eng/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ function Get-Help() {
Write-Host "Libraries settings:"
Write-Host " -testnobuild Skip building tests when invoking -test."
Write-Host " -buildExtension Build the VS Code extension."
Write-Host " -restore-maui Restore the MAUI workload after restore (only on Windows/macOS)."
Write-Host ""

Write-Host "Command-line arguments not listed above are passed through to MSBuild."
Expand Down Expand Up @@ -109,3 +110,36 @@ if ($env:TreatWarningsAsErrors -eq 'false') {

Write-Host "& `"$PSScriptRoot/common/build.ps1`" $arguments"
Invoke-Expression "& `"$PSScriptRoot/common/build.ps1`" $arguments"
$buildExitCode = $LASTEXITCODE

# Install MAUI workload after restore if -restore-maui was passed
# Only on Windows and macOS (MAUI doesn't support Linux)
$restoreMauiPassed = $properties -contains "-restore-maui"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfversluis is the plan to also add a github workflow or something that is protecting this part of the script and ensuring it doesn't get broken? We should have some sort of protection by having a job that restores the workload and tries to build the maui playground project.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely! Opened this to track it for now: #12372

$isWindowsOrMac = ($IsWindows -or $IsMacOS -or (-not (Get-Variable -Name IsWindows -ErrorAction SilentlyContinue)))
if ($restoreMauiPassed -and $buildExitCode -eq 0 -and $isWindowsOrMac) {
Write-Host ""
Write-Host "Installing MAUI workload into local .dotnet..."

$repoRoot = Split-Path $PSScriptRoot -Parent
$dotnetRoot = Join-Path $repoRoot ".dotnet"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not always be true, as there are cases where the machine-wide version matches the version in global.json then the build won't create a local .dotnet folder. Instead, this script should be calling the dotnet.cmd or dotnet.sh scripts in the root of the repo which will resolve to the right place always.

cc: @jfversluis

$dotnetExe = Join-Path $dotnetRoot "dotnet.exe"

if (Test-Path $dotnetExe) {
$env:DOTNET_ROOT = $dotnetRoot
$env:PATH = "$dotnetRoot;$env:PATH"

& $dotnetExe workload install maui 2>&1 | Out-Host
if ($LASTEXITCODE -ne 0) {
Write-Host ""
Write-Warning "Failed to install MAUI workload. You may need to run this command manually:"
Write-Warning " $dotnetExe workload install maui"
Write-Host ""
Write-Host "The MAUI playground may not work without the MAUI workload installed."
}
else {
Write-Host "MAUI workload installed successfully."
}
}
}

exit $buildExitCode
39 changes: 39 additions & 0 deletions eng/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ usage()
echo "Libraries settings:"
echo " --testnobuild Skip building tests when invoking -test."
echo " --build-extension Build the VS Code extension."
echo " --restore-maui Restore the MAUI workload after restore (only on macOS)."
echo ""

echo "Command line arguments starting with '/p:' are passed through to MSBuild."
Expand All @@ -55,6 +56,7 @@ usage()

arguments=''
extraargs=''
restore_maui=false

# Check if an action is passed in
declare -a actions=("b" "build" "r" "restore" "rebuild" "testnobuild" "sign" "publish" "clean" "t" "test" "build-extension")
Expand Down Expand Up @@ -142,6 +144,11 @@ while [[ $# > 0 ]]; do
shift 1
;;

-restore-maui)
restore_maui=true
shift 1
;;

*)
extraargs="$extraargs $1"
shift 1
Expand All @@ -159,3 +166,35 @@ fi

arguments="$arguments $extraargs"
"$scriptroot/common/build.sh" $arguments
buildExitCode=$?

# Install MAUI workload after restore if --restore-maui was passed
# Only on macOS (MAUI doesn't support Linux, Windows uses .cmd)
if [ "$restore_maui" = true ] && [ $buildExitCode -eq 0 ]; then
# Check if we're on macOS
if [[ "$(uname -s)" == "Darwin" ]]; then
echo ""
echo "Installing MAUI workload into local .dotnet..."

repo_root="$(cd "$scriptroot/.." && pwd)"
dotnet_root="$repo_root/.dotnet"
dotnet_exe="$dotnet_root/dotnet"

if [ -f "$dotnet_exe" ]; then
export DOTNET_ROOT="$dotnet_root"
export PATH="$dotnet_root:$PATH"

if "$dotnet_exe" workload install maui; then
echo "MAUI workload installed successfully."
else
echo ""
echo "WARNING: Failed to install MAUI workload. You may need to run this command manually:"
echo " $dotnet_exe workload install maui"
echo ""
echo "The MAUI playground may not work without the MAUI workload installed."
fi
fi
fi
fi

exit $buildExitCode
10 changes: 10 additions & 0 deletions playground/AspireWithMaui/AspireWithMaui.AppHost/AppHost.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var builder = DistributedApplication.CreateBuilder(args);

var weatherApi = builder.AddProject("webapi", @"../AspireWithMaui.WeatherApi/AspireWithMaui.WeatherApi.csproj");

var mauiapp = builder.AddMauiProject("mauiapp", @"../AspireWithMaui.MauiClient/AspireWithMaui.MauiClient.csproj");

mauiapp.AddWindowsDevice()
.WithReference(weatherApi);

builder.Build().Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>c086a0d6-b7a6-1337-a6a3-b62aa4616d88</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<AspireProjectOrPackageReference Include="Aspire.Hosting" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.AppHost" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.Maui" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.DevTunnels" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:17279;http://localhost:15173",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21046",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22093"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15173",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19172",
"ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20091"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
14 changes: 14 additions & 0 deletions playground/AspireWithMaui/AspireWithMaui.MauiClient/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AspireWithMaui.MauiClient"
x:Class="AspireWithMaui.MauiClient.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
14 changes: 14 additions & 0 deletions playground/AspireWithMaui/AspireWithMaui.MauiClient/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace AspireWithMaui.MauiClient;

public partial class App : Application
{
public App()
{
InitializeComponent();
}

protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(new AppShell());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="AspireWithMaui.MauiClient.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AspireWithMaui.MauiClient"
Title="AspireWithMaui.MauiClient">

<TabBar>
<Tab Title="Weather" Icon="weather.png">
<ShellContent
Title="Weather"
ContentTemplate="{DataTemplate local:MainPage}"
Route="weather" />
</Tab>
<Tab Title="Environment" Icon="aspire_outline.png">
<ShellContent
Title="Environment"
ContentTemplate="{DataTemplate local:EnvironmentPage}"
Route="environment" />
</Tab>
</TabBar>

</Shell>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace AspireWithMaui.MauiClient;

public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>

<!-- Note for MacCatalyst:
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifier>.
The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
<!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->

<OutputType>Exe</OutputType>
<RootNamespace>AspireWithMaui.MauiClient</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<!-- Disable CPM for this MAUI project -->
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>

<!-- Disable strong name checking for playground sample -->
<NoWarn>$(NoWarn);CS8002</NoWarn>

<!-- Ignore unnecessary using directive warnings -->
<NoWarn>$(NoWarn);IDE0005</NoWarn>

<!-- Display name -->
<ApplicationTitle>AspireWithMaui.MauiClient</ApplicationTitle>

<!-- App Identifier -->
<ApplicationId>com.companyname.aspirewithmaui.mauiclient</ApplicationId>

<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>

<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
<WindowsPackageType>None</WindowsPackageType>

<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
</PropertyGroup>

<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />

<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />

<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="680,628" />
<MauiImage Update="Resources\Images\aspire_outline.svg" Resize="True" BaseSize="256,256" />
<MauiImage Update="Resources\Images\weather.png" Resize="True" BaseSize="512,512" />

<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />

<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.0-rc.1.25452.6" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.0-rc.2.25465.106" />
<PackageReference Include="System.Net.Http.Json" Version="10.0.0-rc.2.25465.106" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.0-rc.2.25465.106" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AspireWithMaui.MauiServiceDefaults\AspireWithMaui.MauiServiceDefaults.csproj" />
</ItemGroup>

</Project>
Loading