diff --git a/.github/workflows/build_avalonia.yml b/.github/workflows/build_avalonia.yml
new file mode 100644
index 0000000000..a12254c8f6
--- /dev/null
+++ b/.github/workflows/build_avalonia.yml
@@ -0,0 +1,25 @@
+name: build_avalonia
+
+on:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+ paths:
+ - .github/workflows/build_forms.yml
+ - Directory.Build.props
+ - Directory.Build.targets
+ - Directory.Packages.props
+ - xunit.runner.json
+ - 'src/Prism.Core/**'
+ - 'src/Prism.Events/**'
+ - 'tests/Prism.Core.Tests/**'
+ - 'src/Avalonia/**'
+ - 'tests/Avalonia/**'
+
+jobs:
+ build-prism-avalonia:
+ uses: avantipoint/workflow-templates/.github/workflows/dotnet-build.yml@v1
+ with:
+ name: Build Prism.Avalonia
+ solution-path: PrismLibrary_Avalonia.slnf
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3e69f3d750..da289c0a4a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -52,6 +52,20 @@ jobs:
codeSignClientSecret: ${{ secrets.CodeSignClientSecret }}
codeSignCertificate: ${{ secrets.CodeSignCertificate }}
+ build-prism-avalonia:
+ uses: avantipoint/workflow-templates/.github/workflows/dotnet-build.yml@v1
+ with:
+ name: Build Prism.Avalonia
+ solution-path: PrismLibrary_Avalonia.slnf
+ code-sign: true
+ artifact-name: Avalonia
+ secrets:
+ codeSignKeyVault: ${{ secrets.CodeSignKeyVault }}
+ codeSignClientId: ${{ secrets.CodeSignClientId }}
+ codeSignTenantId: ${{ secrets.CodeSignTenantId }}
+ codeSignClientSecret: ${{ secrets.CodeSignClientSecret }}
+ codeSignCertificate: ${{ secrets.CodeSignCertificate }}
+
build-prism-uno:
uses: avantipoint/workflow-templates/.github/workflows/msbuild-build.yml@v1
with:
@@ -89,7 +103,7 @@ jobs:
codeSignCertificate: ${{ secrets.CodeSignCertificate }}
generate-consolidated-artifacts:
- needs: [build-prism-core, build-prism-wpf, build-prism-uno, build-prism-maui]
+ needs: [build-prism-core, build-prism-wpf, build-prism-avalonia, build-prism-uno, build-prism-maui]
runs-on: windows-latest
steps:
- name: Checkout
@@ -107,6 +121,12 @@ jobs:
name: Wpf
path: artifacts\Wpf
+ - name: Download Avalonia
+ uses: actions/download-artifact@v3
+ with:
+ name: Avalonia
+ path: artifacts\Avalonia
+
- name: Download Uno
uses: actions/download-artifact@v3
with:
diff --git a/Directory.Build.props b/Directory.Build.props
index 1b57ba9bdc..551baef951 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -37,6 +37,7 @@
$(MSBuildProjectName.Contains('Uno'))
$(MSBuildProjectName.Contains('Forms'))
$(MSBuildProjectName.Contains('Maui'))
+ $(MSBuildProjectName.Contains('Avalonia'))
True
$(MSBuildThisFileDirectory)prism.snk
False
@@ -49,6 +50,7 @@
prism;mvvm;wpf;dependency injection;di
prism;mvvm;winui;uno-platform;xamarin;webassembly;android;ios;macos;dependency injection;di
prism;mvvm;uwp;android;ios;xamarin;xamarin.forms;dependency injection;di
+ prism;mvvm;axaml;xaml;desktop;navigation;prismavalonia;dialog;linux;macos;avalonia;dependency injection;di
false
false
$(IsWpfProject)
@@ -83,6 +85,22 @@
+
+
+ $(DefineConstants);AVALONIA
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
index aa251ad062..835a7f4104 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -57,7 +57,20 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PrismLibrary.sln b/PrismLibrary.sln
index 2d188338d3..5b2cc2f306 100644
--- a/PrismLibrary.sln
+++ b/PrismLibrary.sln
@@ -70,6 +70,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.Events", "src\Prism.E
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.Uno.WinUI.Markup", "src\Uno\Prism.Uno.Markup\Prism.Uno.WinUI.Markup.csproj", "{0EA416B6-0AB6-464B-9F4D-206FFCFB262D}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Avalonia", "Avalonia", "{8AE1015F-4D62-47EF-A576-2F7411EC20D5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.Avalonia", "src\Avalonia\Prism.Avalonia\Prism.Avalonia.csproj", "{C505C63F-99E6-464F-8C83-1AE4239C19B1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.DryIoc.Avalonia", "src\Avalonia\Prism.DryIoc.Avalonia\Prism.DryIoc.Avalonia.csproj", "{A6B7B19C-3288-4CD2-A737-527BEB1ED807}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.Avalonia.Tests", "tests\Avalonia\Prism.Avalonia.Tests\Prism.Avalonia.Tests.csproj", "{AB501F63-8E8C-4333-8A15-81BA02F3C703}"
+EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Prism.Container.Avalonia.Shared", "tests\Avalonia\Prism.Container.Avalonia.Shared\Prism.Container.Avalonia.Shared.shproj", "{6FDA7D49-DF44-4E8F-A182-0EB73DD3C452}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.DryIoc.Avalonia.Tests", "tests\Avalonia\Prism.DryIoc.Avalonia.Tests\Prism.DryIoc.Avalonia.Tests.csproj", "{03B9C775-9582-409F-B67F-6B8A0CE0C7AF}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.IocContainer.Avalonia.Tests.Support", "tests\Avalonia\Prism.IocContainer.Avalonia.Tests.Support\Prism.IocContainer.Avalonia.Tests.Support.csproj", "{887E0794-798D-4127-847E-6F68D5C9B334}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Avalonia", "Avalonia", "{904D5094-55F9-4581-90AC-D6C1131F8152}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -296,6 +312,66 @@ Global
{0EA416B6-0AB6-464B-9F4D-206FFCFB262D}.Release|x64.Build.0 = Release|Any CPU
{0EA416B6-0AB6-464B-9F4D-206FFCFB262D}.Release|x86.ActiveCfg = Release|Any CPU
{0EA416B6-0AB6-464B-9F4D-206FFCFB262D}.Release|x86.Build.0 = Release|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Debug|x64.Build.0 = Debug|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Debug|x86.Build.0 = Debug|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Release|x64.ActiveCfg = Release|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Release|x64.Build.0 = Release|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Release|x86.ActiveCfg = Release|Any CPU
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1}.Release|x86.Build.0 = Release|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Debug|x64.Build.0 = Debug|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Debug|x86.Build.0 = Debug|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Release|x64.ActiveCfg = Release|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Release|x64.Build.0 = Release|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Release|x86.ActiveCfg = Release|Any CPU
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807}.Release|x86.Build.0 = Release|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Debug|x64.Build.0 = Debug|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Debug|x86.Build.0 = Debug|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Release|x64.ActiveCfg = Release|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Release|x64.Build.0 = Release|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Release|x86.ActiveCfg = Release|Any CPU
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703}.Release|x86.Build.0 = Release|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Debug|x64.Build.0 = Debug|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Debug|x86.Build.0 = Debug|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Release|x64.ActiveCfg = Release|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Release|x64.Build.0 = Release|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Release|x86.ActiveCfg = Release|Any CPU
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF}.Release|x86.Build.0 = Release|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Debug|x64.Build.0 = Debug|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Debug|x86.Build.0 = Debug|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Release|Any CPU.Build.0 = Release|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Release|x64.ActiveCfg = Release|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Release|x64.Build.0 = Release|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Release|x86.ActiveCfg = Release|Any CPU
+ {887E0794-798D-4127-847E-6F68D5C9B334}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -325,12 +401,22 @@ Global
{8711D306-1118-4A11-9399-EF14AA13015E} = {540CEEC1-D541-4614-BF0B-18056A83E434}
{8610485A-BE9F-4938-86D4-E9F1FA1739A0} = {F3664D7A-6FF5-4D1F-9F5F-26EE87F032D3}
{0EA416B6-0AB6-464B-9F4D-206FFCFB262D} = {8F959801-D494-4CAF-9437-90F30472E169}
+ {8AE1015F-4D62-47EF-A576-2F7411EC20D5} = {F3664D7A-6FF5-4D1F-9F5F-26EE87F032D3}
+ {C505C63F-99E6-464F-8C83-1AE4239C19B1} = {8AE1015F-4D62-47EF-A576-2F7411EC20D5}
+ {A6B7B19C-3288-4CD2-A737-527BEB1ED807} = {8AE1015F-4D62-47EF-A576-2F7411EC20D5}
+ {AB501F63-8E8C-4333-8A15-81BA02F3C703} = {904D5094-55F9-4581-90AC-D6C1131F8152}
+ {6FDA7D49-DF44-4E8F-A182-0EB73DD3C452} = {904D5094-55F9-4581-90AC-D6C1131F8152}
+ {03B9C775-9582-409F-B67F-6B8A0CE0C7AF} = {904D5094-55F9-4581-90AC-D6C1131F8152}
+ {887E0794-798D-4127-847E-6F68D5C9B334} = {904D5094-55F9-4581-90AC-D6C1131F8152}
+ {904D5094-55F9-4581-90AC-D6C1131F8152} = {00FFDC13-7397-46F1-897E-A62A7575D28A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C7433AE2-B1A0-4C1A-887E-5CAA7AAF67A6}
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ tests\Avalonia\Prism.Container.Avalonia.Shared\Prism.Container.Avalonia.Shared.projitems*{03b9c775-9582-409f-b67f-6b8a0ce0c7af}*SharedItemsImports = 5
tests\Wpf\Prism.Container.Wpf.Shared\Prism.Container.Wpf.Shared.projitems*{367be810-5b34-4894-be47-1c8dcc67de81}*SharedItemsImports = 5
+ tests\Avalonia\Prism.Container.Avalonia.Shared\Prism.Container.Avalonia.Shared.projitems*{6fda7d49-df44-4e8f-a182-0eb73dd3c452}*SharedItemsImports = 13
tests\Wpf\Prism.Container.Wpf.Shared\Prism.Container.Wpf.Shared.projitems*{ba05687e-2317-4a65-805b-c596f52f7203}*SharedItemsImports = 5
tests\Wpf\Prism.Container.Wpf.Shared\Prism.Container.Wpf.Shared.projitems*{bd42a7d6-a84d-4d27-9c28-7f6a2ec477f1}*SharedItemsImports = 13
EndGlobalSection
diff --git a/PrismLibrary_Avalonia.slnf b/PrismLibrary_Avalonia.slnf
new file mode 100644
index 0000000000..4dc61b7f6e
--- /dev/null
+++ b/PrismLibrary_Avalonia.slnf
@@ -0,0 +1,16 @@
+{
+ "solution": {
+ "path": "PrismLibrary.sln",
+ "projects": [
+ "src\\Avalonia\\Prism.Avalonia\\Prism.Avalonia.csproj",
+ "src\\Avalonia\\Prism.DryIoc.Avalonia\\Prism.DryIoc.Avalonia.csproj",
+ "src\\Prism.Core\\Prism.Core.csproj",
+ "src\\Prism.Events\\Prism.Events.csproj",
+ "tests\\Avalonia\\Prism.Avalonia.Tests\\Prism.Avalonia.Tests.csproj",
+ "tests\\Avalonia\\Prism.Container.Avalonia.Shared\\Prism.Container.Avalonia.Shared.shproj",
+ "tests\\Avalonia\\Prism.DryIoc.Avalonia.Tests\\Prism.DryIoc.Avalonia.Tests.csproj",
+ "tests\\Avalonia\\Prism.IocContainer.Avalonia.Tests.Support\\Prism.IocContainer.Avalonia.Tests.Support.csproj",
+ "tests\\Prism.Core.Tests\\Prism.Core.Tests.csproj"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index b8edb84d63..de04d0354c 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Prism
-Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Xamarin Forms, Uno Platform and WinUI. Separate releases are available for each platform and those will be developed on independent timelines. Prism provides an implementation of a collection of design patterns that are helpful in writing well-structured and maintainable XAML applications, including MVVM, dependency injection, commands, EventAggregator, and others. Prism's core functionality is a shared code base supported in .NET Standard 2.0, .NET Framework 4.6 / 4.7, and .NET6.0/.NET8.0. Those things that need to be platform specific are implemented in the respective libraries for the target platform. Prism also provides great integration of these patterns with the target platform.
+Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Avalonia, MAUI, Uno Platform and WinUI. Separate releases are available for each platform and those will be developed on independent timelines. Prism provides an implementation of a collection of design patterns that are helpful in writing well-structured and maintainable XAML applications, including MVVM, dependency injection, commands, EventAggregator, and others. Prism's core functionality is a shared code base supported in .NET Standard 2.0, .NET Framework 4.6 / 4.7, and .NET6.0/.NET8.0. Those things that need to be platform specific are implemented in the respective libraries for the target platform. Prism also provides great integration of these patterns with the target platform.
## Licensing
@@ -13,7 +13,7 @@ The Prism Team would first and foremost like to thank all of those developers wh
| Full Build | [![Prism CI](https://github.com/PrismLibrary/Prism/actions/workflows/ci.yml/badge.svg)](https://github.com/PrismLibrary/Prism/actions/workflows/ci.yml) |
| Prism.Core | [![build_core](https://github.com/PrismLibrary/Prism/actions/workflows/build_core.yml/badge.svg)](https://github.com/PrismLibrary/Prism/actions/workflows/build_core.yml) |
| Prism.Wpf | [![build_wpf](https://github.com/PrismLibrary/Prism/actions/workflows/build_wpf.yml/badge.svg)](https://github.com/PrismLibrary/Prism/actions/workflows/build_wpf.yml) |
-| Prism.Forms | [![build_forms](https://github.com/PrismLibrary/Prism/actions/workflows/build_forms.yml/badge.svg)](https://github.com/PrismLibrary/Prism/actions/workflows/build_forms.yml) |
+| Prism.Avalonia | [![build_avalonia](https://github.com/PrismLibrary/Prism/actions/workflows/build_avalonia.yml/badge.svg)](https://github.com/PrismLibrary/Prism/actions/workflows/build_avalonia.yml) |
| Prism.Uno | [![build_uno](https://github.com/PrismLibrary/Prism/actions/workflows/build_uno.yml/badge.svg)](https://github.com/PrismLibrary/Prism/actions/workflows/build_uno.yml) |
| Prism.Maui | [![build_maui](https://github.com/PrismLibrary/Prism/actions/workflows/build_maui.yml/badge.svg)](https://github.com/PrismLibrary/Prism/actions/workflows/build_maui.yml) |
diff --git a/e2e/Avalonia/PrismAvaloniaDemo.sln b/e2e/Avalonia/PrismAvaloniaDemo.sln
new file mode 100644
index 0000000000..903b5aa92c
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo.sln
@@ -0,0 +1,57 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.10.35122.118
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrismAvaloniaDemo", "PrismAvaloniaDemo\PrismAvaloniaDemo.csproj", "{89EF9B20-B80E-4B4C-A202-170CC0C0B62A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Prism Library", "Prism Library", "{7EEF2BF7-98F5-432B-B36D-DB4915CF4A3D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.Avalonia", "..\..\src\Avalonia\Prism.Avalonia\Prism.Avalonia.csproj", "{CD50461C-B394-433A-86CA-3DBCD13E8B22}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.DryIoc.Avalonia", "..\..\src\Avalonia\Prism.DryIoc.Avalonia\Prism.DryIoc.Avalonia.csproj", "{FF15E73E-5D71-48DD-80D5-DDCA6A59A6B2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.Core", "..\..\src\Prism.Core\Prism.Core.csproj", "{62ADE0CF-751D-4AC6-9C2C-21DC12CBD338}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.Events", "..\..\src\Prism.Events\Prism.Events.csproj", "{FE27B57B-F986-44B7-90CE-421034A891B9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {89EF9B20-B80E-4B4C-A202-170CC0C0B62A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {89EF9B20-B80E-4B4C-A202-170CC0C0B62A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {89EF9B20-B80E-4B4C-A202-170CC0C0B62A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {89EF9B20-B80E-4B4C-A202-170CC0C0B62A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CD50461C-B394-433A-86CA-3DBCD13E8B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CD50461C-B394-433A-86CA-3DBCD13E8B22}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CD50461C-B394-433A-86CA-3DBCD13E8B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CD50461C-B394-433A-86CA-3DBCD13E8B22}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FF15E73E-5D71-48DD-80D5-DDCA6A59A6B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FF15E73E-5D71-48DD-80D5-DDCA6A59A6B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FF15E73E-5D71-48DD-80D5-DDCA6A59A6B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FF15E73E-5D71-48DD-80D5-DDCA6A59A6B2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {62ADE0CF-751D-4AC6-9C2C-21DC12CBD338}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {62ADE0CF-751D-4AC6-9C2C-21DC12CBD338}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {62ADE0CF-751D-4AC6-9C2C-21DC12CBD338}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {62ADE0CF-751D-4AC6-9C2C-21DC12CBD338}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FE27B57B-F986-44B7-90CE-421034A891B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FE27B57B-F986-44B7-90CE-421034A891B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FE27B57B-F986-44B7-90CE-421034A891B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FE27B57B-F986-44B7-90CE-421034A891B9}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {CD50461C-B394-433A-86CA-3DBCD13E8B22} = {7EEF2BF7-98F5-432B-B36D-DB4915CF4A3D}
+ {FF15E73E-5D71-48DD-80D5-DDCA6A59A6B2} = {7EEF2BF7-98F5-432B-B36D-DB4915CF4A3D}
+ {62ADE0CF-751D-4AC6-9C2C-21DC12CBD338} = {7EEF2BF7-98F5-432B-B36D-DB4915CF4A3D}
+ {FE27B57B-F986-44B7-90CE-421034A891B9} = {7EEF2BF7-98F5-432B-B36D-DB4915CF4A3D}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {2E70249F-C31B-4FB3-A901-E3767E3A9FF5}
+ EndGlobalSection
+EndGlobal
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/.editorconfig b/e2e/Avalonia/PrismAvaloniaDemo/.editorconfig
new file mode 100644
index 0000000000..845700edbd
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/.editorconfig
@@ -0,0 +1,378 @@
+# Copyright 2024 Xeno Innovations, Inc.
+#
+# This EditorConfig file provides consistent coding styles and formatting structure based on
+# C# standards by Xeno Innovations for your team's projects while preserving your personal defaults.
+#
+# For more info:
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options
+#
+
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+[*]
+# All generic files should use MSDOS style endings, not Unix (lf)
+# end_of_line = crlf
+indent_style = space
+
+[*.{cs,csx}]
+indent_style = space
+indent_size = 4
+tab_width = 4
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{xml,xaml,axml,axaml}]
+indent_style = space
+indent_size = 2
+charset = utf-8-bom
+trim_trailing_whitespace = true
+
+[*.json]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+
+[*.sln]
+indent_size = 2
+
+# Xml project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+
+# Xml config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+
+[*.svg]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+
+# PList Files
+[*.plist]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+
+# YAML files
+[*.{yaml,yml}]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+
+# Shell script files
+[*.sh]
+end_of_line = lf
+indent_style = space
+indent_size = 2
+
+# Powershell
+[*.{ps1,psd1,psm1}]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+
+[*.md]
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+# C# Ruleset
+[*.{cs,csx}]
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = false
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+## Formatting - new line options
+### Require braces to be on a new line for (also known as "Allman" style)
+### accessors, methods, object_collection, control_blocks, types, properties, lambdas
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
+
+## Spaces
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+
+# Modifier preferences
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
+
+# Organize Usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = true
+file_header_template = unset
+# file_header_template = Copyright Xeno Innovations, Inc. 2022\nSee the LICENSE file in the project root for more information.
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_predefined_type_for_member_access = true
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# Modifier preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+dotnet_style_readonly_field = true
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# New line preferences
+#dotnet_diagnostic.IDE2000.severity = warning
+dotnet_style_allow_multiple_blank_lines_experimental = false:error
+
+# dotnet_diagnostic.IDE2001.severity = none
+csharp_style_allow_embedded_statements_on_same_line_experimental = false
+
+# dotnet_diagnostic.IDE2002.severity = warning
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
+
+# dotnet_diagnostic.IDE2003.severity = error
+dotnet_style_allow_statement_immediately_after_block_experimental = false:error
+
+# Naming Conventions
+## Async methods must use "Async" suffix
+dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods
+dotnet_naming_rule.async_methods_end_in_async.style = end_in_async
+dotnet_naming_rule.async_methods_end_in_async.severity = error
+dotnet_naming_symbols.any_async_methods.applicable_kinds = method
+dotnet_naming_symbols.any_async_methods.applicable_accessibilities = *
+dotnet_naming_symbols.any_async_methods.required_modifiers = async
+dotnet_naming_style.end_in_async.capitalization = pascal_case
+dotnet_naming_style.end_in_async.required_prefix =
+dotnet_naming_style.end_in_async.required_suffix = Async
+dotnet_naming_style.end_in_async.word_separator =
+
+## private fields must prefix with an underscore
+dotnet_naming_rule.private_members_with_underscore.symbols = private_fields
+dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore
+dotnet_naming_rule.private_members_with_underscore.severity = error
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private
+dotnet_naming_style.prefix_underscore.capitalization = camel_case
+dotnet_naming_style.prefix_underscore.required_prefix = _
+
+## private static fields must use PascalCase (overrides '_' based on SA1311)
+dotnet_naming_rule.private_static_field_naming.symbols = private_static_field_naming
+dotnet_naming_rule.private_static_field_naming.style = pascal_case_style
+dotnet_naming_rule.private_static_field_naming.severity = error
+dotnet_naming_symbols.private_static_field_naming.applicable_kinds = field
+dotnet_naming_symbols.private_static_field_naming.applicable_accessibilities = private
+dotnet_naming_symbols.private_static_field_naming.required_modifiers = static, readonly
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+
+## Constant fields must use PascalCase
+dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = error
+dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
+dotnet_naming_symbols.constant_fields.applicable_kinds = field
+dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
+dotnet_naming_symbols.constant_fields.required_modifiers = const
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+
+## Interfaces must have an I suffix
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+
+## Types and Non-Field Members must be PascalCase
+dotnet_naming_rule.types_should_be_pascal_case.severity = error
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = error
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+## Code Style Rules
+# IDE1005: Use conditional delegate call
+csharp_style_conditional_delegate_call = true
+# IDE1005: Delegate invocation can be simplified.
+dotnet_diagnostic.IDE1005.severity = warning
+# IDE1006: Naming Styles
+dotnet_diagnostic.IDE1006.severity = error
+# IDEOOO8: Use of var
+dotnet_diagnostic.IDE0008.severity = none
+# IDE0010: Add missing cases
+dotnet_diagnostic.IDE0010.severity = none
+# IDE0011: Add braces
+csharp_prefer_braces = when_multiline
+# IDE0025: Use expression body for properties
+csharp_style_expression_bodied_properties = true
+# IDE0026: Use expression body for indexers
+csharp_style_expression_bodied_indexers = true
+# IDE0027: Use expression body for accessors
+csharp_style_expression_bodied_accessors = true
+# IDE0046: Convert to conditional expression
+dotnet_diagnostic.IDE0046.severity = none
+# IDE0058: Expression value is never used
+# csharp_style_unused_value_expression_statement_preference = discard_variable
+dotnet_diagnostic.IDE0058.severity = none
+
+## Code Quality Rules
+# CA1031: Do not catch general exception types
+dotnet_diagnostic.CA1031.severity = none
+# CA1822: Mark members as static
+##dotnet_diagnostic.CA1822.severity = none
+# CA1507: Use nameof to express symbol names
+dotnet_diagnostic.CA1507.severity = error
+
+## Compiler
+# CS0618: Type or member is obsolete
+##dotnet_diagnostic.CS0618.severity = error
+dotnet_diagnostic.CS0618.severity = warning
+# CS1591: Missing XML comment for publicly visible type or member
+dotnet_diagnostic.CS1591.severity = none
+
+## StyleCop.Analyzers
+# SA1000: Keywords should be spaced correctly
+dotnet_diagnostic.SA1000.severity = error
+# SA1005: Single line comments should begin with single space
+dotnet_diagnostic.SA1005.severity = error
+# SA1008
+# SA1025: Spacing??
+# SA1101: PrefixLocalCallsWithThis
+dotnet_diagnostic.SA1101.severity = none
+# SA1116: Split parameters should start on line after declaration
+dotnet_diagnostic.SA1116.severity = none
+# SA1118: Parameter should not span multiple lines
+dotnet_diagnostic.SA1118.severity = warning
+# SA1137: Elements should have the same indentation
+dotnet_diagnostic.SA1137.severity = error
+# SA1124: Do not use regions
+dotnet_diagnostic.SA1124.severity = error
+# SA1200: UsingDirectivesMustBePlacedWithinNamespace
+dotnet_diagnostic.SA1200.severity = none
+# SA1201: Elements should appear in the correct order
+dotnet_diagnostic.SA1201.severity = error
+# SA1202: Elements should be ordered by access
+dotnet_diagnostic.SA1202.severity = error
+# SA1203: Constants should appear before fields
+dotnet_diagnostic.SA1203.severity = error
+# SA1204: Static elements should appear before instance elements
+dotnet_diagnostic.SA1204.severity = error
+# SA1214: Readonly fields should appear before non-readonly fields
+dotnet_diagnostic.SA1214.severity = error
+# SA1306: Field names should begin with lower-case letter
+dotnet_diagnostic.SA1306.severity = error
+# SA1309: FieldNamesMustNotBeginWithUnderscore
+dotnet_diagnostic.SA1309.severity = none
+# SA1313: Parameter names should begin with lower-case letter
+dotnet_diagnostic.SA1313.severity = error
+# SA1414: Tuple types in signatures should have element names
+dotnet_diagnostic.SA1414.severity = silent
+# SA1503: Braces should not be omitted
+dotnet_diagnostic.SA1503.severity = none
+# SA1505: Opening braces should not be floowed by a blank line
+dotnet_diagnostic.SA1505.severity= error
+# SA1507: Code should not contain multiple blank lines in a row
+dotnet_diagnostic.SA1507.severity = error
+# SA1508: Closing brac should not be preceded by a blank line
+dotnet_diagnostic.SA1508.severity= error
+# SA1513: Closing brace should be followed by blank line
+dotnet_diagnostic.SA1513.severity = error
+# SA1514: Element documentation header should be preceded by blank line
+dotnet_diagnostic.SA1514.severity = error
+# SA1515: Single-line comment should be preceded by blank line
+dotnet_diagnostic.SA1515.severity = warning
+# SA1516: Elements should be separated by blank line
+dotnet_diagnostic.SA1516.severity = error
+# SA1600: Elements should be documented
+dotnet_diagnostic.SA1600.severity = none
+# SA1602: Enumeration items should not be documented
+dotnet_diagnostic.SA1602.severity = none
+# SA1623: Property summary documentation should match accessors
+dotnet_diagnostic.SA1623.severity = warning
+# SA1633: FileMustHaveHeader
+dotnet_diagnostic.SA1633.severity = silent
+# SA1636: File header copyright text should match
+dotnet_diagnostic.SA1636.severity = none
+
+# Default severity for analyzer diagnostics with category 'StyleCop.CSharp.SpacingRules' SA1028: Code should not contain trailing whitespace
+dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpacingRules.severity = error
+
+dotnet_diagnostic.VSSpell001.severity = suggestion
+dotnet_diagnostic.VSSpell002.severity = none
+
+# SA0001: XML comment analysis is disabled due to project configuration
+dotnet_diagnostic.SA0001.severity = none
+
+# Ignore Generated XAML
+[*.sg.cs]
+# CS1591: Missing XML comment for publicly visible type or member
+dotnet_diagnostic.CS1591.severity = none
+dotnet_analyzer_diagnostic.CS1591.severity = none
+# generated_code = true
+
+[WindowsAppSDK-VersionInfo.cs]
+dotnet_diagnostic.CS1591.severity = none
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/.gitignore b/e2e/Avalonia/PrismAvaloniaDemo/.gitignore
new file mode 100644
index 0000000000..2ebe4cf80d
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/.gitignore
@@ -0,0 +1,86 @@
+# Copyright CURRENT_YEAR COMPANY_NAME
+# Template for C#
+
+# Generic Visual Studio files
+*.bak
+*.csproj.user
+*.suo
+*.vshost.exe
+*.vshost.exe.manifest
+*.exe.config
+*.exp
+*.lib
+*.pdb
+*.pfx
+*.user
+*.vbw
+*.scc
+*.oca
+*.userprefs
+*.userosscache
+*.sln.docstates
+*.autorecover
+*.coverage
+
+# VS Code
+.vscode/*
+!.vscode/extensions.json
+!.vscode/launch.json
+!.vscode/settings.json
+
+# SQLite datbases
+*.db3
+
+# Xamarin.Android Resource.Designer.cs files
+**/*.Android/**/[Rr]esource.[Dd]esigner.cs
+**/*.Droid/**/[Rr]esource.[Dd]esigner.cs
+**/Android/**/[Rr]esource.[Dd]esigner.cs
+**/Droid/**/[Rr]esource.[Dd]esigner.cs
+
+# Build results
+[Oo]utput/
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+.vs/
+
+# NuGet Packages
+*.nupkg
+## The packages folder can be ignored because of Package Restore
+**/packages/*
+## Except build/, which is used as an MSBuild target.
+!**/packages/build/
+## Uncomment if necessary however generally it will be regenerated when needed
+!**/packages/repositories.config
+## NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+
+# Installer Folder
+/installer/*.exe
+!/installer/autorun.exe
+
+# Lib folder
+/lib/*
+!/lib/readme.txt
+!/lib/readme.md
+
+# Output
+!/[Oo]utput/readme.txt
+!/[Oo]utput/readme.md
+
+## USER DEFINED
+/[Dd]ocs/*.csv
+/[Dd]ocs/backup
+/[Tt]ools
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/App.axaml b/e2e/Avalonia/PrismAvaloniaDemo/App.axaml
new file mode 100644
index 0000000000..e662fa7e63
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/App.axaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/App.axaml.cs b/e2e/Avalonia/PrismAvaloniaDemo/App.axaml.cs
new file mode 100644
index 0000000000..19fec496f4
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/App.axaml.cs
@@ -0,0 +1,54 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Data.Core;
+using Avalonia.Data.Core.Plugins;
+using Avalonia.Markup.Xaml;
+using Prism.DryIoc;
+using Prism.Ioc;
+using Prism.Navigation.Regions;
+using SampleApp.Services;
+using SampleApp.ViewModels;
+using SampleApp.Views;
+
+namespace SampleApp;
+
+public partial class App : PrismApplication
+{
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+
+ // Required when overriding Initialize
+ base.Initialize();
+ }
+
+ protected override AvaloniaObject CreateShell()
+ {
+ return Container.Resolve();
+ }
+
+ protected override void RegisterTypes(IContainerRegistry containerRegistry)
+ {
+ // Register your Services, Views, Dialogs, etc. here
+
+ // Services
+ containerRegistry.RegisterSingleton();
+
+ // Views - Region Navigation
+ containerRegistry.RegisterForNavigation();
+ containerRegistry.RegisterForNavigation();
+ containerRegistry.RegisterForNavigation();
+ }
+
+ /// Called after Initialize.
+ protected override void OnInitialized()
+ {
+ // Register Views to the Region it will appear in. Don't register them in the ViewModel.
+ var regionManager = Container.Resolve();
+
+ // WARNING: Prism v11.0.0
+ // - DataTemplates MUST define a DataType or else an XAML error will be thrown
+ // - Error: DataTemplate inside of DataTemplates must have a DataType set
+ regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(DashboardView));
+ }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Assets/logo.ico b/e2e/Avalonia/PrismAvaloniaDemo/Assets/logo.ico
new file mode 100644
index 0000000000..e16d84d5d0
Binary files /dev/null and b/e2e/Avalonia/PrismAvaloniaDemo/Assets/logo.ico differ
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/PrismAvaloniaDemo.csproj b/e2e/Avalonia/PrismAvaloniaDemo/PrismAvaloniaDemo.csproj
new file mode 100644
index 0000000000..b90410dc08
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/PrismAvaloniaDemo.csproj
@@ -0,0 +1,34 @@
+
+
+ WinExe
+ net8.0
+ latest
+ enable
+ true
+ app.manifest
+ true
+ false
+ Sample Prism.Avalonia App
+ Damian Suess
+ Prism.Avalonia end-to-end demonstration application.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Program.cs b/e2e/Avalonia/PrismAvaloniaDemo/Program.cs
new file mode 100644
index 0000000000..5e783c8451
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Program.cs
@@ -0,0 +1,21 @@
+using System;
+using Avalonia;
+
+namespace SampleApp;
+
+internal sealed class Program
+{
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp() => AppBuilder
+ .Configure()
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace();
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/RegionNames.cs b/e2e/Avalonia/PrismAvaloniaDemo/RegionNames.cs
new file mode 100644
index 0000000000..7ebaa5ddde
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/RegionNames.cs
@@ -0,0 +1,8 @@
+namespace SampleApp;
+
+public static class RegionNames
+{
+ public const string ContentRegion = "ContentRegion";
+ public const string FooterRegion = "FooterRegion";
+ public const string SidebarRegion = "SidebarRegion";
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Services/INotificationService.cs b/e2e/Avalonia/PrismAvaloniaDemo/Services/INotificationService.cs
new file mode 100644
index 0000000000..5c69f78cd9
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Services/INotificationService.cs
@@ -0,0 +1,31 @@
+using System;
+using Avalonia.Controls;
+using Avalonia.Controls.Notifications;
+
+namespace SampleApp.Services;
+
+/// In-application Notification Service Interface.
+public interface INotificationService
+{
+ /// Defines the maximum number of notifications visible at once.
+ int MaxItems { get; set; }
+
+ /// The expiry time in seconds at which the notification will close (default 5 seconds).
+ int NotificationTimeout { get; set; }
+
+ // Set the host window.
+ // Parent window.
+ void SetHostWindow(TopLevel window);
+
+ /// Display the notification.
+ /// Title.
+ /// Message.
+ /// The of the notification.
+ /// An optional action to call when the notification is clicked.
+ /// An optional action to call when the notification is closed.
+ void Show(string title,
+ string message,
+ NotificationType notificationType = NotificationType.Information,
+ Action? onClick = null,
+ Action? onClose = null);
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Services/NotificationService.cs b/e2e/Avalonia/PrismAvaloniaDemo/Services/NotificationService.cs
new file mode 100644
index 0000000000..7f6a009ea5
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Services/NotificationService.cs
@@ -0,0 +1,49 @@
+using System;
+using Avalonia.Controls.Notifications;
+
+namespace SampleApp.Services;
+
+public class NotificationService : INotificationService
+{
+ private int _notificationTimeout = 5;
+ private WindowNotificationManager? _notificationManager;
+
+ ///
+ public int MaxItems { get; set; } = 4;
+
+ ///
+ public int NotificationTimeout { get => _notificationTimeout; set => _notificationTimeout = (value < 0) ? 0 : value; }
+
+ ///
+ public void SetHostWindow(TopLevel hostWindow)
+ {
+ var notificationManager = new WindowNotificationManager(hostWindow)
+ {
+ Position = NotificationPosition.BottomRight,
+ MaxItems = MaxItems,
+ Margin = new Thickness(0, 0, 15, 40)
+ };
+
+ _notificationManager = notificationManager;
+ }
+
+ ///
+ public void Show(string title,
+ string message,
+ NotificationType notificationType = NotificationType.Information,
+ Action? onClick = null,
+ Action? onClose = null)
+ {
+ if (_notificationManager is { } nm)
+ {
+ nm.Show(
+ new Notification(
+ title,
+ message,
+ notificationType,
+ TimeSpan.FromSeconds(_notificationTimeout),
+ onClick,
+ onClose));
+ }
+ }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Styles/Icons.axaml b/e2e/Avalonia/PrismAvaloniaDemo/Styles/Icons.axaml
new file mode 100644
index 0000000000..36a5d3b893
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Styles/Icons.axaml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/DashboardViewModel.cs b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/DashboardViewModel.cs
new file mode 100644
index 0000000000..b9ae3ad215
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/DashboardViewModel.cs
@@ -0,0 +1,85 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Avalonia;
+using Avalonia.Styling;
+using Prism.Commands;
+using SampleApp.Services;
+
+namespace SampleApp.ViewModels;
+
+public class DashboardViewModel : ViewModelBase
+{
+ private readonly INotificationService _notification;
+ private int _counter = 0;
+ private ObservableCollection _listItems = new();
+ private int _listItemSelected = -1;
+ private string _listItemText = string.Empty;
+ private ThemeVariant? _themeSelected;
+
+ public DashboardViewModel(INotificationService notifyService)
+ {
+ _notification = notifyService;
+
+ ThemeSelected = Application.Current!.RequestedThemeVariant;
+ }
+
+ public DelegateCommand CmdAddItem => new(() =>
+ {
+ _counter++;
+ ListItems.Add($"Item Number: {_counter}");
+
+ // Optionally use, `Insert(0, ..)` to insert items at the top
+ //ListItems.Insert(0, entry);
+ });
+
+ public DelegateCommand CmdClearItems => new(() =>
+ {
+ ListItems.Clear();
+ });
+
+ public DelegateCommand CmdNotification => new(() =>
+ {
+ _notification.Show("Hello Prism!", "Notification Pop-up Message.");
+
+ // Alternate OnClick action
+ ////_notification.Show("Hello Prism!", "Notification Pop-up Message.", () =>
+ ////{
+ //// // Action to perform
+ ////});
+ });
+
+ public ObservableCollection ListItems { get => _listItems; set => SetProperty(ref _listItems, value); }
+
+ public int ListItemSelected
+ {
+ get => _listItemSelected;
+ set
+ {
+ SetProperty(ref _listItemSelected, value);
+
+ if (value == -1)
+ return;
+
+ ListItemText = ListItems[ListItemSelected];
+ }
+ }
+
+ public string ListItemText { get => _listItemText; set => SetProperty(ref _listItemText, value); }
+
+ public ThemeVariant? ThemeSelected
+ {
+ get => _themeSelected;
+ set
+ {
+ SetProperty(ref _themeSelected, value);
+ Application.Current!.RequestedThemeVariant = _themeSelected;
+ }
+ }
+
+ public List ThemeStyles => new()
+ {
+ ThemeVariant.Default,
+ ThemeVariant.Dark,
+ ThemeVariant.Light,
+ };
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/MainWindowViewModel.cs b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000000..92a7af5aaf
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,41 @@
+using Prism.Commands;
+using Prism.Navigation.Regions;
+
+using SampleApp.Views;
+
+namespace SampleApp.ViewModels;
+
+public class MainWindowViewModel : ViewModelBase
+{
+ private readonly IRegionManager _regionManager;
+ private bool _isPaneOpened;
+
+ public MainWindowViewModel(IRegionManager regionManager)
+ {
+ // Since this is a basic ShellWindow, there's not much to do here.
+ // For enterprise apps, you could register up subscriptions
+ // or other startup background tasks so that they get triggered
+ // on startup, rather than putting them in the DashboardViewModel.
+ //
+ // For example, initiate the pulling of News Feeds, etc.
+
+ _regionManager = regionManager;
+ Title = "Sample Prism.Avalonia";
+ IsPaneOpened = true;
+ }
+
+ public DelegateCommand CmdDashboard => new(() =>
+ {
+ // _journal.Clear();
+ _regionManager.RequestNavigate(RegionNames.ContentRegion, nameof(DashboardView));
+ });
+
+ public DelegateCommand CmdFlyoutMenu => new(() =>
+ {
+ IsPaneOpened = !IsPaneOpened;
+ });
+
+ public DelegateCommand CmdSettings => new(() => _regionManager.RequestNavigate(RegionNames.ContentRegion, nameof(SettingsView)));
+
+ public bool IsPaneOpened { get => _isPaneOpened; set => SetProperty(ref _isPaneOpened, value); }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/SettingsViewModel.cs b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/SettingsViewModel.cs
new file mode 100644
index 0000000000..9bbb327779
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/SettingsViewModel.cs
@@ -0,0 +1,36 @@
+using SampleApp.Views;
+using Prism.Commands;
+using Prism.Navigation;
+using Prism.Navigation.Regions;
+
+namespace SampleApp.ViewModels;
+
+public class SettingsViewModel : ViewModelBase
+{
+ private readonly IRegionManager _regionManager;
+
+ public SettingsViewModel(IRegionManager regionManager)
+ {
+ _regionManager = regionManager;
+ Title = "Settings";
+ }
+
+ public DelegateCommand CmdNavigateToChild => new(() =>
+ {
+ var navParams = new NavigationParameters
+ {
+ { "key1", "Some text" },
+ { "key2", 999 }
+ };
+
+ _regionManager.RequestNavigate(
+ RegionNames.ContentRegion,
+ nameof(SubSettingsView),
+ navParams);
+ });
+
+ public override void OnNavigatedFrom(NavigationContext navigationContext)
+ {
+ base.OnNavigatedFrom(navigationContext);
+ }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/SubSettingsViewModel.cs b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/SubSettingsViewModel.cs
new file mode 100644
index 0000000000..939bbe6cf2
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/SubSettingsViewModel.cs
@@ -0,0 +1,53 @@
+using Prism.Commands;
+using Prism.Navigation.Regions;
+using SampleApp.Views;
+
+namespace SampleApp.ViewModels;
+
+public class SubSettingsViewModel : ViewModelBase
+{
+ private readonly IRegionManager _regionManager;
+ private IRegionNavigationJournal? _journal;
+ private string _messageNumber = string.Empty;
+ private string _messageText = string.Empty;
+
+ public SubSettingsViewModel(IRegionManager regionManager)
+ {
+ _regionManager = regionManager;
+
+ Title = "Settings - SubView";
+ }
+
+ public DelegateCommand CmdNavigateBack => new DelegateCommand(() =>
+ {
+ // Go back to the previous calling page, otherwise, Dashboard.
+ if (_journal != null && _journal.CanGoBack)
+ _journal.GoBack();
+ else
+ _regionManager.RequestNavigate(RegionNames.ContentRegion, nameof(DashboardView));
+ });
+
+ public string MessageNumber { get => _messageNumber; set => SetProperty(ref _messageNumber, value); }
+
+ public string MessageText { get => _messageText; set => SetProperty(ref _messageText, value); }
+
+ /// Navigation completed successfully.
+ /// Navigation context.
+ public override void OnNavigatedTo(NavigationContext navigationContext)
+ {
+ // Used to "Go Back" to parent
+ _journal = navigationContext.NavigationService.Journal;
+
+ // Display our parameters
+ MessageText = navigationContext.Parameters["key1"].ToString() ?? "";
+ MessageNumber = navigationContext.Parameters["key2"].ToString() ?? "";
+ }
+
+ public override bool OnNavigatingTo(NavigationContext navigationContext)
+ {
+ // Navigation permission sample:
+ // Don't allow navigation if our keys are missing
+ return navigationContext.Parameters.ContainsKey("key1") &&
+ navigationContext.Parameters.ContainsKey("key2");
+ }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/ViewModelBase.cs b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/ViewModelBase.cs
new file mode 100644
index 0000000000..048a65f401
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/ViewModels/ViewModelBase.cs
@@ -0,0 +1,45 @@
+using Prism.Mvvm;
+using Prism.Navigation.Regions;
+
+namespace SampleApp.ViewModels;
+
+public class ViewModelBase : BindableBase, INavigationAware
+{
+ private string _title = string.Empty;
+
+ /// Gets or sets the title of the View.
+ public string Title { get => _title; set => SetProperty(ref _title, value); }
+
+ ///
+ /// Called to determine if this instance can handle the navigation request.
+ /// Don't call this directly, use .
+ ///
+ /// The navigation context.
+ /// if this instance accepts the navigation request; otherwise, .
+ public virtual bool IsNavigationTarget(NavigationContext navigationContext)
+ {
+ // Auto-allow navigation
+ return OnNavigatingTo(navigationContext);
+ }
+
+ /// Called when the implementer is being navigated away from.
+ /// The navigation context.
+ public virtual void OnNavigatedFrom(NavigationContext navigationContext)
+ {
+ }
+
+ /// Called when the implementer has been navigated to.
+ /// The navigation context.
+ public virtual void OnNavigatedTo(NavigationContext navigationContext)
+ {
+ }
+
+ /// Navigation validation checker.
+ /// Override for Prism 7.2's IsNavigationTarget.
+ /// The navigation context.
+ /// if this instance accepts the navigation request; otherwise, .
+ public virtual bool OnNavigatingTo(NavigationContext navigationContext)
+ {
+ return true;
+ }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Views/DashboardView.axaml b/e2e/Avalonia/PrismAvaloniaDemo/Views/DashboardView.axaml
new file mode 100644
index 0000000000..d9b8271eec
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Views/DashboardView.axaml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Views/DashboardView.axaml.cs b/e2e/Avalonia/PrismAvaloniaDemo/Views/DashboardView.axaml.cs
new file mode 100644
index 0000000000..a7fa3dd6af
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Views/DashboardView.axaml.cs
@@ -0,0 +1,24 @@
+using Avalonia;
+using Avalonia.Controls;
+using Prism.Ioc;
+using SampleApp.Services;
+
+namespace SampleApp.Views;
+
+/// DashboardView.
+public partial class DashboardView : UserControl
+{
+ public DashboardView()
+ {
+ InitializeComponent();
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ // Initialize the WindowNotificationManager with the "TopLevel". Previously (v0.10), MainWindow
+ var notifyService = ContainerLocator.Current.Resolve();
+ notifyService.SetHostWindow(TopLevel.GetTopLevel(this));
+ }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Views/MainWindow.axaml b/e2e/Avalonia/PrismAvaloniaDemo/Views/MainWindow.axaml
new file mode 100644
index 0000000000..7d61f2c6b2
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Views/MainWindow.axaml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Views/MainWindow.axaml.cs b/e2e/Avalonia/PrismAvaloniaDemo/Views/MainWindow.axaml.cs
new file mode 100644
index 0000000000..96e38830f1
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Views/MainWindow.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace SampleApp.Views;
+
+/// Main window view.
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Views/SettingsView.axaml b/e2e/Avalonia/PrismAvaloniaDemo/Views/SettingsView.axaml
new file mode 100644
index 0000000000..b699c9bbf0
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Views/SettingsView.axaml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Views/SettingsView.axaml.cs b/e2e/Avalonia/PrismAvaloniaDemo/Views/SettingsView.axaml.cs
new file mode 100644
index 0000000000..1e09aeaf23
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Views/SettingsView.axaml.cs
@@ -0,0 +1,12 @@
+using Avalonia.Controls;
+
+namespace SampleApp.Views;
+
+/// Sample Settings View.
+public partial class SettingsView : UserControl
+{
+ public SettingsView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Views/SubSettingsView.axaml b/e2e/Avalonia/PrismAvaloniaDemo/Views/SubSettingsView.axaml
new file mode 100644
index 0000000000..8956b11d65
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Views/SubSettingsView.axaml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/Views/SubSettingsView.axaml.cs b/e2e/Avalonia/PrismAvaloniaDemo/Views/SubSettingsView.axaml.cs
new file mode 100644
index 0000000000..aa5c7558db
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/Views/SubSettingsView.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace SampleApp.Views;
+
+public partial class SubSettingsView : UserControl
+{
+ public SubSettingsView()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/app.manifest b/e2e/Avalonia/PrismAvaloniaDemo/app.manifest
new file mode 100644
index 0000000000..dd6799f249
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e2e/Avalonia/PrismAvaloniaDemo/settings.XamlStyler b/e2e/Avalonia/PrismAvaloniaDemo/settings.XamlStyler
new file mode 100644
index 0000000000..170db90968
--- /dev/null
+++ b/e2e/Avalonia/PrismAvaloniaDemo/settings.XamlStyler
@@ -0,0 +1,44 @@
+{
+ "AttributesTolerance": 2,
+ "KeepFirstAttributeOnSameLine": true,
+ "MaxAttributeCharactersPerLine": 0,
+ "MaxAttributesPerLine": 1,
+ "NewlineExemptionElements": "RadialGradientBrush, GradientStop, LinearGradientBrush, ScaleTransfom, SkewTransform, RotateTransform, TranslateTransform, Trigger, Condition, Setter",
+ "SeparateByGroups": false,
+ "AttributeIndentation": 0,
+ "AttributeIndentationStyle": 1,
+ "RemoveDesignTimeReferences": false,
+ "EnableAttributeReordering": true,
+ "AttributeOrderingRuleGroups": [
+ "xmlns, xmlns:x",
+ "xmlns:*",
+ "x:Class",
+ "x:*",
+ "*:*",
+ "Key, Name, Uid, Title, Text",
+ "Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom",
+ "Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight",
+ "Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex",
+ "Source",
+ "*",
+ "PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint",
+ "mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText",
+ "Storyboard.*, From, To, Duration"
+ ],
+ "FirstLineAttributes": "Text",
+ "OrderAttributesByName": true,
+ "PutEndingBracketOnNewLine": false,
+ "RemoveEndingTagOfEmptyElement": true,
+ "SpaceBeforeClosingSlash": true,
+ "RootElementLineBreakRule": 0,
+ "ReorderVSM": 2,
+ "ReorderGridChildren": false,
+ "ReorderCanvasChildren": false,
+ "ReorderSetters": 0,
+ "FormatMarkupExtension": true,
+ "NoNewLineMarkupExtensions": "x:Bind, Binding",
+ "ThicknessSeparator": 2,
+ "ThicknessAttributes": "Margin, Padding, BorderThickness, ThumbnailClipMargin",
+ "FormatOnSave": true,
+ "CommentPadding": 1
+}
\ No newline at end of file
diff --git a/images/Prism.Avalonia.png b/images/Prism.Avalonia.png
new file mode 100644
index 0000000000..1d1c114966
Binary files /dev/null and b/images/Prism.Avalonia.png differ
diff --git a/src/Avalonia/Prism.Avalonia/Common/ObservableObject.cs b/src/Avalonia/Prism.Avalonia/Common/ObservableObject.cs
new file mode 100644
index 0000000000..721b92f907
--- /dev/null
+++ b/src/Avalonia/Prism.Avalonia/Common/ObservableObject.cs
@@ -0,0 +1,51 @@
+using System.ComponentModel;
+using Prism.Extensions;
+using Avalonia;
+using Avalonia.Controls;
+
+namespace Prism.Common
+{
+ ///
+ /// Class that wraps an object, so that other classes can notify for Change events. Typically, this class is set as
+ /// a Dependency Property on AvaloniaObjects, and allows other classes to observe any changes in the Value.
+ ///
+ ///
+ /// This class is required, because in Silverlight, it's not possible to receive Change notifications for Dependency properties that you do not own.
+ ///
+ /// The type of the property that's wrapped in the Observable object
+ public class ObservableObject : Control, INotifyPropertyChanged
+ {
+ ///
+ /// Identifies the Value property of the ObservableObject
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "This is the pattern for dependency properties")]
+ public static readonly StyledProperty ValueProperty =
+ AvaloniaProperty.Register(name: nameof(Value));
+
+ ///
+ /// Event that gets invoked when the Value property changes.
+ ///
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ ///
+ /// The value that's wrapped inside the ObservableObject.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
+ public T Value
+ {
+ get => (T)GetValue(ValueProperty);
+ set => SetValue(ValueProperty, value);
+ }
+
+ private static void ValueChangedCallback(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
+ {
+ ObservableObject thisInstance = ((ObservableObject)d);
+ thisInstance.PropertyChanged?.Invoke(thisInstance, new PropertyChangedEventArgs(nameof(Value)));
+ }
+
+ static ObservableObject()
+ {
+ ValueProperty.Changed.Subscribe(args => ValueChangedCallback(args?.Sender, args));
+ }
+ }
+}
diff --git a/src/Avalonia/Prism.Avalonia/Common/Stubs.cs b/src/Avalonia/Prism.Avalonia/Common/Stubs.cs
new file mode 100644
index 0000000000..bbc331eeda
--- /dev/null
+++ b/src/Avalonia/Prism.Avalonia/Common/Stubs.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Prism.Common
+{
+ internal static class Stubs
+ {
+ public static readonly Action Nop = () => { };
+ public static readonly Action Throw = ex => { throw ex; };
+ }
+}
diff --git a/src/Avalonia/Prism.Avalonia/Dialogs/Dialog.cs b/src/Avalonia/Prism.Avalonia/Dialogs/Dialog.cs
new file mode 100644
index 0000000000..84642492a6
--- /dev/null
+++ b/src/Avalonia/Prism.Avalonia/Dialogs/Dialog.cs
@@ -0,0 +1,75 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Styling;
+using Prism.Extensions;
+
+namespace Prism.Dialogs
+{
+ /// This class contains attached properties.
+ public class Dialog
+ {
+ /// Identifies the WindowStyle attached property.
+ /// This attached property is used to specify the style of a .
+ public static readonly AvaloniaProperty WindowStyleProperty =
+ AvaloniaProperty.RegisterAttached("WindowStyle", typeof(Dialog));
+
+ /// Identifies the WindowStartupLocation attached property.
+ /// This attached property is used to specify the startup location of a .
+ public static readonly AvaloniaProperty WindowStartupLocationProperty =
+ AvaloniaProperty.RegisterAttached(
+ name: "WindowStartupLocation",
+ ownerType: typeof(Dialog));
+
+ /// Creates an instance of the Dialog class.
+ public Dialog()
+ {
+ WindowStartupLocationProperty.Changed.Subscribe(args => OnWindowStartupLocationChanged(args?.Sender, args));
+ }
+
+ ///
+ /// Gets the value for the attached property.
+ ///
+ /// The target element.
+ /// The attached to the element.
+ public static Style GetWindowStyle(AvaloniaObject obj)
+ {
+ return (Style)obj.GetValue(WindowStyleProperty);
+ }
+
+ ///
+ /// Sets the attached property.
+ ///
+ /// The target element.
+ /// The Style to attach.
+ public static void SetWindowStyle(AvaloniaObject obj, Style value)
+ {
+ obj.SetValue(WindowStyleProperty, value);
+ }
+
+ ///
+ /// Gets the value for the attached property.
+ ///
+ /// The target element.
+ /// The attached to the element.
+ public static WindowStartupLocation GetWindowStartupLocation(AvaloniaObject obj)
+ {
+ return (WindowStartupLocation)obj.GetValue(WindowStartupLocationProperty);
+ }
+
+ ///
+ /// Sets the attached property.
+ ///
+ /// The target element.
+ /// The WindowStartupLocation to attach.
+ public static void SetWindowStartupLocation(AvaloniaObject obj, WindowStartupLocation value)
+ {
+ obj.SetValue(WindowStartupLocationProperty, value);
+ }
+
+ private static void OnWindowStartupLocationChanged(AvaloniaObject sender, AvaloniaPropertyChangedEventArgs e)
+ {
+ if (sender is Window window)
+ window.WindowStartupLocation = (WindowStartupLocation)e.NewValue;
+ }
+ }
+}
diff --git a/src/Avalonia/Prism.Avalonia/Dialogs/DialogService.cs b/src/Avalonia/Prism.Avalonia/Dialogs/DialogService.cs
new file mode 100644
index 0000000000..f091944c5e
--- /dev/null
+++ b/src/Avalonia/Prism.Avalonia/Dialogs/DialogService.cs
@@ -0,0 +1,180 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Prism.Common;
+using Prism.Ioc;
+
+namespace Prism.Dialogs
+{
+ /// Implements to show modal and non-modal dialogs.
+ /// The dialog's ViewModel must implement IDialogAware.
+ public class DialogService : IDialogService
+ {
+ private readonly IContainerExtension _containerExtension;
+
+ /// Initializes a new instance of the class.
+ /// The
+ public DialogService(IContainerExtension containerExtension)
+ {
+ _containerExtension = containerExtension;
+ }
+
+ /// Show dialog.
+ /// Name of the dialog window to show.
+ /// .
+ /// The action to perform when the dialog is closed.
+ public void ShowDialog(string name, IDialogParameters parameters, DialogCallback callback)
+ {
+ parameters ??= new DialogParameters();
+ var isModal = parameters.TryGetValue(KnownDialogParameters.ShowNonModal, out var show) ? !show : true;
+ var windowName = parameters.TryGetValue(KnownDialogParameters.WindowName, out var wName) ? wName : null;
+ var owner = parameters.TryGetValue(KnownDialogParameters.ParentWindow, out var hWnd) ? hWnd : null;
+
+ IDialogWindow dialogWindow = CreateDialogWindow(windowName);
+ ConfigureDialogWindowEvents(dialogWindow, callback);
+ ConfigureDialogWindowContent(name, dialogWindow, parameters);
+
+ ShowDialogWindow(dialogWindow, isModal, owner);
+ }
+
+ /// Shows the dialog window.
+ /// The dialog window to show.
+ /// If true; dialog is shown as a modal
+ /// Optional host window of the dialog. Use-case, Dialog calling a dialog.
+ protected virtual void ShowDialogWindow(IDialogWindow dialogWindow, bool isModal, Window owner = null)
+ {
+ if (isModal &&
+ Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime deskLifetime)
+ {
+ // Ref:
+ // - https://docs.avaloniaui.net/docs/reference/controls/window#show-a-window-as-a-dialog
+ // - https://github.com/AvaloniaUI/Avalonia/discussions/7924
+ // (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
+
+ if (owner != null)
+ dialogWindow.ShowDialog(owner);
+ else
+ dialogWindow.ShowDialog(deskLifetime.MainWindow);
+ }
+ else
+ {
+ dialogWindow.Show();
+ }
+ }
+
+ ///
+ /// Create a new .
+ ///
+ /// The name of the hosting window registered with the IContainerRegistry.
+ /// The created .
+ protected virtual IDialogWindow CreateDialogWindow(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ return _containerExtension.Resolve();
+ else
+ return _containerExtension.Resolve(name);
+ }
+
+ ///
+ /// Configure content.
+ ///
+ /// The name of the dialog to show.
+ /// The hosting window.
+ /// The parameters to pass to the dialog.
+ protected virtual void ConfigureDialogWindowContent(string dialogName, IDialogWindow window, IDialogParameters parameters)
+ {
+ var content = _containerExtension.Resolve