diff --git a/samples/MvvmSample.Core/Helpers/EmbeddedResources.cs b/samples/MvvmSample.Core/Helpers/EmbeddedResources.cs
new file mode 100644
index 0000000..535044e
--- /dev/null
+++ b/samples/MvvmSample.Core/Helpers/EmbeddedResources.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics.Contracts;
+using System.IO;
+using System.Reflection;
+using System.Threading.Tasks;
+
+namespace MvvmSample.Core.Helpers
+{
+ ///
+ /// A helper class to retrieve embedded resources from the executing assembly.
+ ///
+ public static class EmbeddedResources
+ {
+ ///
+ /// The sequence of available directory separators for all platforms.
+ ///
+ private static readonly char[] PathSeparators = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
+
+ ///
+ /// Returns a instance for a specified manifest file.
+ ///
+ /// The target instance.
+ /// The relative path of the file to open, with respect of the root of the assembly.
+ /// A for the requested file.
+ /// Thrown when does not represent a valid file.
+ ///
+ /// The relative path should not include the name of the given assembly. For instance, consider an embedded
+ /// resource file that is located at the path /Assets/myfile.txt into the assembly MyAssembly.
+ /// You can then invoke with equal to either
+ /// /Assets/myfile.txt, \Assets\myfile.txt, Assets/myfile.txt or similar combinations.
+ /// This method will reconstruct the full path of the target file (which is MyAssembly.Assets.myfile.txt
+ /// in this case, retrieve the file and return a to use to read from it.
+ ///
+ [Pure]
+ public static Stream GetStream(Assembly assembly, string path)
+ {
+ string[] parts = path.Split(PathSeparators, StringSplitOptions.RemoveEmptyEntries);
+ string filename = $"{assembly.GetName().Name}.{string.Join(".", parts)}";
+
+ Stream? stream = assembly.GetManifestResourceStream(filename);
+
+ if (stream is null)
+ {
+ static void Throw() => throw new ArgumentException("The input path was not valid or the item didn't exist", nameof(path));
+
+ Throw();
+ }
+
+ return stream!;
+ }
+
+ ///
+ /// Returns the contents of a specified manifest file, as a .
+ ///
+ /// The target instance.
+ /// The relative path of the file to read, with respect of the root of the assembly.
+ /// The text contents of the specified manifest file.
+ /// Thrown when does not represent a valid file.
+ ///
+ /// This method will automatically get the assembly for the caller to retrieve the target file.
+ /// See remarks for for more info.
+ ///
+ [Pure]
+ public static async Task GetStringAsync(Assembly assembly, string path)
+ {
+ using Stream stream = GetStream(assembly, path);
+ using StreamReader reader = new(stream);
+
+ return await reader.ReadToEndAsync();
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/MvvmSample.Core/MvvmSample.Core.csproj b/samples/MvvmSample.Core/MvvmSample.Core.csproj
index 9be1ede..65065bd 100644
--- a/samples/MvvmSample.Core/MvvmSample.Core.csproj
+++ b/samples/MvvmSample.Core/MvvmSample.Core.csproj
@@ -2,50 +2,40 @@
netstandard2.0
- 8.0
+ 9.0
+ enable
4e66f7b4-01a8-4f00-8733-4ae6a08c741f
-
-
-
-
-
-
-
-
-
-
-
-
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
-
+
+
PreserveNewest
-
+
+
+
+
+
+
-
-
-
-
-
diff --git a/samples/MvvmSample.Core/ViewModels/SamplePageViewModel.cs b/samples/MvvmSample.Core/ViewModels/SamplePageViewModel.cs
index 3bd766b..476b24b 100644
--- a/samples/MvvmSample.Core/ViewModels/SamplePageViewModel.cs
+++ b/samples/MvvmSample.Core/ViewModels/SamplePageViewModel.cs
@@ -4,12 +4,11 @@
using System.Collections.Generic;
using System.IO;
+using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Toolkit.Mvvm.ComponentModel;
-using Microsoft.Toolkit.Mvvm.DependencyInjection;
using Microsoft.Toolkit.Mvvm.Input;
using MvvmSample.Core.Helpers;
-using MvvmSample.Core.Services;
namespace MvvmSample.Core.ViewModels
{
@@ -18,11 +17,7 @@ namespace MvvmSample.Core.ViewModels
///
public class SamplePageViewModel : ObservableObject
{
- private IReadOnlyDictionary texts;
- ///
- /// The instance currently in use.
- ///
- private readonly IFilesService FilesServices = Ioc.Default.GetRequiredService();
+ private IReadOnlyDictionary? texts;
public SamplePageViewModel()
{
@@ -34,7 +29,10 @@ public SamplePageViewModel()
///
public IAsyncRelayCommand LoadDocsCommand { get; }
- public IReadOnlyDictionary Texts
+ ///
+ /// Gets or sets the collection of loaded paragraphs.
+ ///
+ public IReadOnlyDictionary? Texts
{
get => texts;
set => SetProperty(ref texts, value);
@@ -54,15 +52,14 @@ public string GetParagraph(string key)
/// Implements the logic for .
///
/// The name of the docs file to load.
- private async Task LoadDocsAsync(string name)
+ private async Task LoadDocsAsync(string? name)
{
// Skip if the loading has already started
if (!(LoadDocsCommand.ExecutionTask is null)) return;
- var path = Path.Combine("Assets", "docs", $"{name}.md");
- using var stream = await FilesServices.OpenForReadAsync(path);
- using var reader = new StreamReader(stream);
- var text = await reader.ReadToEndAsync();
+ string
+ path = Path.Combine("Docs", $"{name!}.md"),
+ text = await EmbeddedResources.GetStringAsync(Assembly.GetExecutingAssembly(), path);
Texts = MarkdownHelper.GetParagraphs(text);
diff --git a/samples/MvvmSampleUwp/MvvmSampleUwp.csproj b/samples/MvvmSampleUwp/MvvmSampleUwp.csproj
index 935e8b1..2d5354c 100644
--- a/samples/MvvmSampleUwp/MvvmSampleUwp.csproj
+++ b/samples/MvvmSampleUwp/MvvmSampleUwp.csproj
@@ -339,40 +339,6 @@
MSBuild:Compile
-
-
- Assets\docs\AsyncRelayCommand.md
- PreserveNewest
-
-
- Assets\docs\Introduction.md
- PreserveNewest
-
-
- Assets\docs\Ioc.md
- PreserveNewest
-
-
- Assets\docs\Messenger.md
- PreserveNewest
-
-
- Assets\docs\ObservableObject.md
- PreserveNewest
-
-
- Assets\docs\ObservableRecipient.md
- PreserveNewest
-
-
- Assets\docs\PuttingThingsTogether.md
- PreserveNewest
-
-
- Assets\docs\RelayCommand.md
- PreserveNewest
-
-
{28e524f7-61b3-455d-8cbd-615bc4fde814}
diff --git a/samples/MvvmSampleWinUI3Desktop.sln b/samples/MvvmSampleWinUI3Desktop.sln
new file mode 100644
index 0000000..e821f57
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop.sln
@@ -0,0 +1,119 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31004.235
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "MvvmSampleWinUI3Desktop (Package)", "MvvmSampleWinUI3Desktop\MvvmSampleWinUI3Desktop (Package)\MvvmSampleWinUI3Desktop (Package).wapproj", "{8E012FC5-B17A-4E98-A8CA-ED7571F9274E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvvmSampleWinUI3Desktop", "MvvmSampleWinUI3Desktop\MvvmSampleWinUI3Desktop\MvvmSampleWinUI3Desktop.csproj", "{CB559D9F-B686-49EC-814C-FA7DC1912497}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvvmSample.Core", "MvvmSample.Core\MvvmSample.Core.csproj", "{4437A937-EB2A-4A46-9588-E2DF5E52D647}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvvmSampleUwp", "MvvmSampleUwp\MvvmSampleUwp.csproj", "{E667FBF4-30F2-49AC-8576-7A2B867F8F1A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|ARM = Debug|ARM
+ Debug|arm64 = Debug|arm64
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|ARM = Release|ARM
+ Release|arm64 = Release|arm64
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|ARM.ActiveCfg = Debug|x86
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|arm64.ActiveCfg = Debug|arm64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|arm64.Build.0 = Debug|arm64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|arm64.Deploy.0 = Debug|arm64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|x64.ActiveCfg = Debug|x64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|x64.Build.0 = Debug|x64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|x64.Deploy.0 = Debug|x64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|x86.ActiveCfg = Debug|x86
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|x86.Build.0 = Debug|x86
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Debug|x86.Deploy.0 = Debug|x86
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|Any CPU.ActiveCfg = Release|x86
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|ARM.ActiveCfg = Release|x86
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|arm64.ActiveCfg = Release|arm64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|arm64.Build.0 = Release|arm64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|arm64.Deploy.0 = Release|arm64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|x64.ActiveCfg = Release|x64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|x64.Build.0 = Release|x64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|x64.Deploy.0 = Release|x64
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|x86.ActiveCfg = Release|x86
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|x86.Build.0 = Release|x86
+ {8E012FC5-B17A-4E98-A8CA-ED7571F9274E}.Release|x86.Deploy.0 = Release|x86
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Debug|ARM.ActiveCfg = Debug|x86
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Debug|arm64.ActiveCfg = Debug|arm64
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Debug|arm64.Build.0 = Debug|arm64
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Debug|x64.ActiveCfg = Debug|x64
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Debug|x64.Build.0 = Debug|x64
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Debug|x86.ActiveCfg = Debug|x86
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Debug|x86.Build.0 = Debug|x86
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Release|Any CPU.ActiveCfg = Release|x86
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Release|ARM.ActiveCfg = Release|x86
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Release|arm64.ActiveCfg = Release|arm64
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Release|arm64.Build.0 = Release|arm64
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Release|x64.ActiveCfg = Release|x64
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Release|x64.Build.0 = Release|x64
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Release|x86.ActiveCfg = Release|x86
+ {CB559D9F-B686-49EC-814C-FA7DC1912497}.Release|x86.Build.0 = Release|x86
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|ARM.Build.0 = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|arm64.Build.0 = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|x64.Build.0 = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Debug|x86.Build.0 = Debug|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|ARM.ActiveCfg = Release|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|ARM.Build.0 = Release|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|arm64.ActiveCfg = Release|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|arm64.Build.0 = Release|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|x64.ActiveCfg = Release|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|x64.Build.0 = Release|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|x86.ActiveCfg = Release|Any CPU
+ {4437A937-EB2A-4A46-9588-E2DF5E52D647}.Release|x86.Build.0 = Release|Any CPU
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|ARM.ActiveCfg = Debug|ARM
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|ARM.Build.0 = Debug|ARM
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|ARM.Deploy.0 = Debug|ARM
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|arm64.ActiveCfg = Debug|ARM64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|arm64.Build.0 = Debug|ARM64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|arm64.Deploy.0 = Debug|ARM64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|x64.ActiveCfg = Debug|x64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|x64.Build.0 = Debug|x64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|x64.Deploy.0 = Debug|x64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|x86.ActiveCfg = Debug|x86
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|x86.Build.0 = Debug|x86
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Debug|x86.Deploy.0 = Debug|x86
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|Any CPU.ActiveCfg = Release|x86
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|ARM.ActiveCfg = Release|ARM
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|ARM.Build.0 = Release|ARM
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|ARM.Deploy.0 = Release|ARM
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|arm64.ActiveCfg = Release|ARM64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|arm64.Build.0 = Release|ARM64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|arm64.Deploy.0 = Release|ARM64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|x64.ActiveCfg = Release|x64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|x64.Build.0 = Release|x64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|x64.Deploy.0 = Release|x64
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|x86.ActiveCfg = Release|x86
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|x86.Build.0 = Release|x86
+ {E667FBF4-30F2-49AC-8576-7A2B867F8F1A}.Release|x86.Deploy.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C9D13338-F430-461C-B906-F31D95512E65}
+ EndGlobalSection
+EndGlobal
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/LockScreenLogo.scale-200.png b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/LockScreenLogo.scale-200.png
new file mode 100644
index 0000000..735f57a
Binary files /dev/null and b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/LockScreenLogo.scale-200.png differ
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/SplashScreen.scale-200.png b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/SplashScreen.scale-200.png
new file mode 100644
index 0000000..023e7f1
Binary files /dev/null and b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/SplashScreen.scale-200.png differ
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Square150x150Logo.scale-200.png b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000..af49fec
Binary files /dev/null and b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Square150x150Logo.scale-200.png differ
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Square44x44Logo.scale-200.png b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000..ce342a2
Binary files /dev/null and b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Square44x44Logo.scale-200.png differ
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Square44x44Logo.targetsize-24_altform-unplated.png b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 0000000..f6c02ce
Binary files /dev/null and b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/StoreLogo.png b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/StoreLogo.png
new file mode 100644
index 0000000..7385b56
Binary files /dev/null and b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/StoreLogo.png differ
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Wide310x150Logo.scale-200.png b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000..288995b
Binary files /dev/null and b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Images/Wide310x150Logo.scale-200.png differ
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/MvvmSampleWinUI3Desktop (Package).wapproj b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/MvvmSampleWinUI3Desktop (Package).wapproj
new file mode 100644
index 0000000..bd22ac2
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/MvvmSampleWinUI3Desktop (Package).wapproj
@@ -0,0 +1,88 @@
+
+
+
+ 15.0
+
+
+
+ Debug
+ x86
+
+
+ Release
+ x86
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+ Debug
+ arm64
+
+
+ Release
+ arm64
+
+
+
+ $(MSBuildExtensionsPath)\Microsoft\DesktopBridge\
+ MvvmSampleWinUI3Desktop\
+
+
+
+ 8e012fc5-b17a-4e98-a8ca-ed7571f9274e
+ 10.0.19041.0
+ 10.0.17763.0
+ en-US
+ false
+ $(MSBuildThisFileDirectory)build\
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ Properties\PublishProfiles\win10-$(Platform).pubxml
+
+
+
+
+ $(NUGET_PACKAGES)
+ $(UserProfile)\.nuget\packages
+ $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'Microsoft.ProjectReunion', '0.5.0-prerelease'))
+ $(SolutionDir)packages\Microsoft.ProjectReunion.0.5.0-prerelease\
+ $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'Microsoft.ProjectReunion.WinUI', '0.5.0-prerelease'))
+ $(SolutionDir)packages\Microsoft.ProjectReunion.WinUI.0.5.0-prerelease\
+ $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_ProjectReunion)', 'build'))Microsoft.ProjectReunion.AppXReference.props
+ $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_ProjectReunion_WinUI)', 'build'))Microsoft.WinUI.AppX.targets
+ ..\MvvmSampleWinUI3Desktop\MvvmSampleWinUI3Desktop.csproj
+
+
+
+ all
+
+
+ all
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Package.appxmanifest b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Package.appxmanifest
new file mode 100644
index 0000000..1be0c8a
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop (Package)/Package.appxmanifest
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+ MvvmSampleWinUI3Desktop (Package)
+ Sergio
+ Images\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/App.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/App.xaml
new file mode 100644
index 0000000..d7bca4d
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/App.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/App.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/App.xaml.cs
new file mode 100644
index 0000000..0323ad5
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/App.xaml.cs
@@ -0,0 +1,59 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Toolkit.Mvvm.DependencyInjection;
+using Microsoft.UI.Xaml;
+using MvvmSample.Core.Services;
+using MvvmSampleUwp.Services;
+using Refit;
+using Windows.ApplicationModel;
+
+namespace MvvmSampleWinUI3Desktop
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ public partial class App : Application
+ {
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ this.Suspending += OnSuspending;
+ }
+
+ ///
+ /// Invoked when the application is launched normally by the end user. Other entry points
+ /// will be used such as when the application is launched to open a specific file.
+ ///
+ /// Details about the launch request and process.
+ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
+ {
+ m_window = new MainWindow();
+ m_window.Activate();
+
+ // Register services
+ Ioc.Default.ConfigureServices(
+ new ServiceCollection()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton(RestService.For("https://www.reddit.com/"))
+ .BuildServiceProvider());
+ }
+
+ ///
+ /// Invoked when application execution is being suspended. Application state is saved
+ /// without knowing whether the application will be terminated or resumed with the contents
+ /// of memory still intact.
+ ///
+ /// The source of the suspend request.
+ /// Details about the suspend request.
+ private void OnSuspending(object sender, SuspendingEventArgs e)
+ {
+ // Save application state and stop any background activity
+ }
+
+ private Window m_window;
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Controls/InteractiveSample.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Controls/InteractiveSample.cs
new file mode 100644
index 0000000..58635ff
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Controls/InteractiveSample.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Controls
+{
+ ///
+ /// A simple control that acts as a frame for an interactive sample.
+ ///
+ public sealed class InteractiveSample : ContentControl
+ {
+ ///
+ /// Gets or sets the representing the C# code to display.
+ ///
+ public string CSharpCode
+ {
+ get => (string)GetValue(CSharpCodeProperty);
+ set => SetValue(CSharpCodeProperty, $"```csharp\n{value.Trim()}\n```");
+ }
+
+ ///
+ /// The backing .
+ ///
+ public static readonly DependencyProperty CSharpCodeProperty = DependencyProperty.Register(
+ nameof(CSharpCode),
+ typeof(string),
+ typeof(InteractiveSample),
+ new PropertyMetadata(default(string)));
+
+ ///
+ /// Gets or sets the representing the XAML code to display.
+ ///
+ public string XamlCode
+ {
+ get => (string)GetValue(XamlCodeProperty);
+ set => SetValue(XamlCodeProperty, $"```xml\n{value.Trim()}\n```");
+ }
+
+ ///
+ /// The backing .
+ ///
+ public static readonly DependencyProperty XamlCodeProperty = DependencyProperty.Register(
+ nameof(XamlCode),
+ typeof(string),
+ typeof(InteractiveSample),
+ new PropertyMetadata(default(string)));
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Controls/InteractiveSample.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Controls/InteractiveSample.xaml
new file mode 100644
index 0000000..618ee86
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Controls/InteractiveSample.xaml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/MainWindow.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/MainWindow.xaml
new file mode 100644
index 0000000..ad503c7
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/MainWindow.xaml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/MainWindow.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/MainWindow.xaml.cs
new file mode 100644
index 0000000..0ff8fa3
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/MainWindow.xaml.cs
@@ -0,0 +1,19 @@
+using Microsoft.UI.Xaml;
+
+namespace MvvmSampleWinUI3Desktop
+{
+ ///
+ /// An empty window that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ this.InitializeComponent();
+ this.Title = "MVVM Toolkit sample app";
+ this.ExtendsContentIntoTitleBar = true;
+
+ Shell.SetTitleBar(this);
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop.csproj b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop.csproj
new file mode 100644
index 0000000..95e1f25
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop.csproj
@@ -0,0 +1,100 @@
+
+
+ WinExe
+ net5.0-windows10.0.19041.0
+ 10.0.17763.0
+ MvvmSampleWinUI3Desktop
+ app.manifest
+ x86;x64;arm64
+ win10-x86;win10-x64;win10-arm64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Services/FileService.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Services/FileService.cs
new file mode 100644
index 0000000..60411ae
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Services/FileService.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MvvmSample.Core.Services;
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using Windows.ApplicationModel;
+using Windows.Storage;
+
+#nullable enable
+
+namespace MvvmSampleUwp.Services
+{
+ ///
+ /// A that implements the using UWP APIs.
+ ///
+ public sealed class FilesService : IFilesService
+ {
+ ///
+ public string InstallationPath => Package.Current.InstalledLocation.Path;
+
+ ///
+ public async Task OpenForReadAsync(string path)
+ {
+ StorageFile file = await StorageFile.GetFileFromPathAsync(Path.Combine(InstallationPath, path));
+
+ return await file.OpenStreamForReadAsync();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Services/SettingsService.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Services/SettingsService.cs
new file mode 100644
index 0000000..3e8b196
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Services/SettingsService.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using MvvmSample.Core.Services;
+using Windows.Foundation.Collections;
+using Windows.Storage;
+
+namespace MvvmSampleUwp.Services
+{
+ ///
+ /// A simple that handles the local app settings.
+ ///
+ public sealed class SettingsService : ISettingsService
+ {
+ ///
+ /// The with the settings targeted by the current instance.
+ ///
+ private readonly IPropertySet SettingsStorage = ApplicationData.Current.LocalSettings.Values;
+
+ ///
+ public void SetValue(string key, T value)
+ {
+ if (!SettingsStorage.ContainsKey(key)) SettingsStorage.Add(key, value);
+ else SettingsStorage[key] = value;
+ }
+
+ ///
+ public T GetValue(string key)
+ {
+ if (SettingsStorage.TryGetValue(key, out object value))
+ {
+ return (T)value;
+ }
+
+ return default;
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Shell.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Shell.xaml
new file mode 100644
index 0000000..4010005
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Shell.xaml
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Shell.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Shell.xaml.cs
new file mode 100644
index 0000000..5996422
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Shell.xaml.cs
@@ -0,0 +1,141 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Navigation;
+using MvvmSampleUwp.Views;
+
+#nullable enable
+
+namespace MvvmSampleUwp
+{
+ public sealed partial class Shell : UserControl
+ {
+ private readonly IReadOnlyCollection NavigationItems;
+
+ public Shell()
+ {
+ this.InitializeComponent();
+
+ NavigationItems = new[]
+ {
+ new SampleEntry(IntroductionItem, typeof(IntroductionPage)),
+ new SampleEntry(ObservableObjectItem, typeof(ObservableObjectPage), "ObservableObject", "observable inotify property changed propertychanging changing"),
+ new SampleEntry(CommandsItem, typeof(RelayCommandPage), "RelayCommand and RelayCommand", "commands icommand relaycommand binding"),
+ new SampleEntry(AsyncCommandsItem, typeof(AsyncRelayCommandPage), "AsyncRelayCommand and AsyncRelayCommand", "asynccommands icommand relaycommand binding asynchronous"),
+ new SampleEntry(MessengerItem, typeof(MessengerPage), "Messenger and IMessenger", "messenger messaging message receiver recipient"),
+ new SampleEntry(SendMessagesItem, typeof(MessengerSendPage), "[IMessenger] Send messages", "messenger messaging message receiver recipient send"),
+ new SampleEntry(RequestMessagesItem, typeof(MessengerRequestPage), "[IMessenger] Request messages", "messenger messaging message receiver recipient request reply"),
+ new SampleEntry(InversionOfControlItem, typeof(IocPage), "Ioc (Inversion of control)", "ioc inversion control dependency injection service locator"),
+ new SampleEntry(RedditBrowserOverviewItem, typeof(PuttingThingsTogetherPage), "Putting things together"),
+ new SampleEntry(ViewModelsSetupItem, typeof(SettingUpTheViewModelsPage), "Setting up the ViewModels"),
+ new SampleEntry(SettingsServiceItem, typeof(SettingsServicePage), "Settings service"),
+ new SampleEntry(RedditServiceItem, typeof(RedditServicePage), "Reddit service"),
+ new SampleEntry(BuildingTheUIItem, typeof(BuildingTheUIPage), "Building the UI"),
+ new SampleEntry(FinalResultItem, typeof(RedditBrowserPage), "Reddit browser")
+ };
+ }
+
+ public void SetTitleBar(Window window)
+ {
+ window.SetTitleBar(TitleBarBorder);
+ }
+
+ // Navigates to a sample page when a button is clicked
+ private void NavigationView_OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
+ {
+ if (NavigationItems.FirstOrDefault(item => item.Item == args.InvokedItemContainer)?.PageType is Type pageType)
+ {
+ NavigationFrame.Navigate(pageType);
+ }
+ }
+
+ // Sets whether or not the back button is enabled
+ private void NavigationFrame_OnNavigated(object sender, NavigationEventArgs e)
+ {
+ NavigationView.IsBackEnabled = ((Frame)sender).BackStackDepth > 0;
+ }
+
+ // Navigates back
+ private void NavigationView_OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
+ {
+ if (NavigationFrame.BackStack.LastOrDefault() is PageStackEntry entry)
+ {
+ NavigationView.SelectedItem = NavigationItems.First(item => item.PageType == entry.SourcePageType).Item;
+
+ NavigationFrame.GoBack();
+ }
+ }
+
+ // Select the introduction item when the shell is loaded
+ private void Shell_OnLoaded(object sender, RoutedEventArgs e)
+ {
+ NavigationView.SelectedItem = IntroductionItem;
+
+ NavigationFrame.Navigate(typeof(IntroductionPage));
+ }
+
+ // Updates the search results
+ private void SearchBox_OnTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
+ {
+ if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
+ {
+ // Not a simple tokenized search, but good enough for now
+ string query = sender.Text.ToLowerInvariant();
+
+ sender.ItemsSource = NavigationItems.Where(item => item.Tags?.Contains(query) == true);
+ }
+ }
+
+ // Navigates to a selected item
+ private void AutoSuggestBox_OnSuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
+ {
+ SampleEntry entry = (SampleEntry)args.SelectedItem;
+
+ NavigationFrame.Navigate(entry.PageType);
+
+ NavigationView.SelectedItem = entry.Item;
+
+ sender.Text = string.Empty;
+ }
+ }
+
+ ///
+ /// A simple model for tracking sample pages associated with buttons.
+ ///
+ public sealed class SampleEntry
+ {
+ public SampleEntry(NavigationViewItem viewItem, Type pageType, string? name = null, string? tags = null)
+ {
+ Item = viewItem;
+ PageType = pageType;
+ Name = name;
+ Tags = tags;
+ }
+
+ ///
+ /// The navigation item for the current entry.
+ ///
+ public NavigationViewItem Item { get; }
+
+ ///
+ /// The associated page type for the current entry.
+ ///
+ public Type PageType { get; }
+
+ ///
+ /// Gets the name of the current entry.
+ ///
+ public string? Name { get; }
+
+ ///
+ /// Gets the tag for the current entry, if any.
+ ///
+ public string? Tags { get; }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/AsyncRelayCommandPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/AsyncRelayCommandPage.xaml
new file mode 100644
index 0000000..16d4211
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/AsyncRelayCommandPage.xaml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<Page.Resources>
+ <converters:TaskResultConverter x:Key="TaskResultConverter"/>
+</Page.Resources>
+<StackPanel Spacing="8">
+ <TextBlock>
+ <Run Text="Task status:"/>
+ <Run Text="{x:Bind ViewModel.DownloadTextCommand.ExecutionTask.Status, Mode=OneWay}"/>
+ <LineBreak/>
+ <Run Text="Result:"/>
+ <Run Text="{x:Bind ViewModel.DownloadTextCommand.ExecutionTask, Converter={StaticResource TaskResultConverter}, Mode=OneWay}"/>
+ </TextBlock>
+ <Button
+ Content="Click me!"
+ Command="{x:Bind ViewModel.DownloadTextCommand}"/>
+ <muxc:ProgressRing
+ HorizontalAlignment="Left"
+ IsActive="{x:Bind ViewModel.DownloadTextCommand.IsRunning, Mode=OneWay}"/>
+</StackPanel>
+
+
+public MyViewModel()
+{
+ DownloadTextCommand = new AsyncRelayCommand(DownloadTextAsync);
+}
+
+public IAsyncRelayCommand DownloadTextCommand { get; }
+
+private async Task<string> DownloadTextAsync()
+{
+ await Task.Delay(3000); // Simulate a web request
+
+ return "Hello world!";
+}
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/AsyncRelayCommandPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/AsyncRelayCommandPage.xaml.cs
new file mode 100644
index 0000000..eb85db1
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/AsyncRelayCommandPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class AsyncRelayCommandPage : Page
+ {
+ public AsyncRelayCommandPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/BuildingTheUIPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/BuildingTheUIPage.xaml
new file mode 100644
index 0000000..d68bed6
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/BuildingTheUIPage.xaml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/BuildingTheUIPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/BuildingTheUIPage.xaml.cs
new file mode 100644
index 0000000..6486e2e
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/BuildingTheUIPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class BuildingTheUIPage : Page
+ {
+ public BuildingTheUIPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IntroductionPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IntroductionPage.xaml
new file mode 100644
index 0000000..cc24804
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IntroductionPage.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IntroductionPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IntroductionPage.xaml.cs
new file mode 100644
index 0000000..4529b28
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IntroductionPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class IntroductionPage : Page
+ {
+ public IntroductionPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IocPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IocPage.xaml
new file mode 100644
index 0000000..a6f5fdc
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IocPage.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IocPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IocPage.xaml.cs
new file mode 100644
index 0000000..e53c30e
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/IocPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class IocPage : Page
+ {
+ public IocPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerPage.xaml
new file mode 100644
index 0000000..a5a04b8
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerPage.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerPage.xaml.cs
new file mode 100644
index 0000000..433774b
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class MessengerPage : Page
+ {
+ public MessengerPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerRequestPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerRequestPage.xaml
new file mode 100644
index 0000000..7f2daa6
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerRequestPage.xaml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<StackPanel Spacing="8">
+ <TextBlock Text="{x:Bind ViewModel.Username, Mode=OneWay}"/>
+ <Button
+ Content="Click to request the username!"
+ Click="{x:Bind ViewModel.RequestCurrentUsername}"/>
+ <Button
+ Content="Click to reset the local username!"
+ Click="{x:Bind ViewModel.ResetCurrentUsername}"/>
+</StackPanel>
+
+
+// Simple viewmodel for a module responding to a request username message.
+// Don't forget to set the IsActive property to true when this viewmodel is in use!
+public class UserSenderViewModel : ObservableRecipient
+{
+ public string Username { get; private set; } = "Bob";
+
+ protected override void OnActivated()
+ {
+ Messenger.Register<UserSenderViewModel, CurrentUsernameRequestMessage>(this, (r, m) => m.Reply(r.Username));
+ }
+}
+
+private string username;
+
+public string Username
+{
+ get => username;
+ private set => SetProperty(ref username, value);
+}
+
+// Sends a message to request the current username, and updates the property
+public void RequestCurrentUsername()
+{
+ Username = WeakReferenceMessenger.Default.Send<CurrentUsernameRequestMessage>();
+}
+
+// Resets the current username
+public void ResetCurrentUsername()
+{
+ Username = null;
+}
+
+// A sample request message to get the current username
+public sealed class CurrentUsernameRequestMessage : RequestMessage<string>
+{
+}
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerRequestPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerRequestPage.xaml.cs
new file mode 100644
index 0000000..547df68
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerRequestPage.xaml.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Navigation;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class MessengerRequestPage : Page
+ {
+ public MessengerRequestPage()
+ {
+ this.InitializeComponent();
+ }
+
+ ///
+ protected override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ ViewModel.SenderViewModel.IsActive = true;
+ ViewModel.ReceiverViewModel.IsActive = true;
+ }
+
+ ///
+ protected override void OnNavigatedFrom(NavigationEventArgs e)
+ {
+ ViewModel.SenderViewModel.IsActive = false;
+ ViewModel.ReceiverViewModel.IsActive = false;
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerSendPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerSendPage.xaml
new file mode 100644
index 0000000..140d93a
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerSendPage.xaml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<StackPanel Spacing="8">
+
+ <!--Sender module-->
+ <Border BorderBrush="#40FFFFFF" BorderThickness="2" CornerRadius="4" Padding="8">
+ <StackPanel Spacing="8">
+ <TextBlock Text="{x:Bind ViewModel.SenderViewModel.Username, Mode=OneWay}"/>
+ <Button
+ Content="Click to send a message!"
+ Click="{x:Bind ViewModel.SenderViewModel.SendUserMessage}"/>
+ </StackPanel>
+ </Border>
+
+ <!--Receiver module-->
+ <Border BorderBrush="#40FFFFFF" BorderThickness="2" CornerRadius="4" Padding="8">
+ <StackPanel Spacing="8">
+ <TextBlock Text="{x:Bind ViewModel.ReceiverViewModel.Username, Mode=OneWay}"/>
+ </StackPanel>
+ </Border>
+</StackPanel>
+
+
+public UserSenderViewModel SenderViewModel { get; } = new UserSenderViewModel();
+
+public UserReceiverViewModel ReceiverViewModel { get; } = new UserReceiverViewModel();
+
+// Simple viewmodel for a module sending a username message
+public class UserSenderViewModel : ObservableRecipient
+{
+ private string username = "Bob";
+
+ public string Username
+ {
+ get => username;
+ private set => SetProperty(ref username, value);
+ }
+
+ public void SendUserMessage()
+ {
+ Username = Username == "Bob" ? "Alice" : "Bob";
+
+ Messenger.Send(new UsernameChangedMessage(Username));
+ }
+}
+
+// Simple viewmodel for a module receiving a username message
+public class UserReceiverViewModel : ObservableRecipient
+{
+ private string username = "";
+
+ public string Username
+ {
+ get => username;
+ private set => SetProperty(ref username, value);
+ }
+
+ protected override void OnActivated()
+ {
+ Messenger.Register<UserReceiverViewModel, UsernameChangedMessage>(this, (r, m) => r.Username = m.Value);
+ }
+}
+
+// A sample message with a username value
+public sealed class UsernameChangedMessage : ValueChangedMessage<string>
+{
+ public UsernameChangedMessage(string value) : base(value)
+ {
+ }
+}
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerSendPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerSendPage.xaml.cs
new file mode 100644
index 0000000..484bd35
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/MessengerSendPage.xaml.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Navigation;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class MessengerSendPage : Page
+ {
+ public MessengerSendPage()
+ {
+ this.InitializeComponent();
+ }
+
+ ///
+ protected override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ ViewModel.SenderViewModel.IsActive = true;
+ ViewModel.ReceiverViewModel.IsActive = true;
+ }
+
+ ///
+ protected override void OnNavigatedFrom(NavigationEventArgs e)
+ {
+ ViewModel.SenderViewModel.IsActive = false;
+ ViewModel.ReceiverViewModel.IsActive = false;
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/ObservableObjectPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/ObservableObjectPage.xaml
new file mode 100644
index 0000000..9a46e96
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/ObservableObjectPage.xaml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<StackPanel Spacing="8">
+ <TextBox
+ PlaceholderText="Type here to update the text below"
+ Text="{x:Bind ViewModel.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
+ <TextBlock Text="{x:Bind ViewModel.Name, Mode=OneWay}"/>
+</StackPanel>
+
+
+private string name;
+
+/// <summary>
+/// Gets or sets the name to display.
+/// </summary>
+public string Name
+{
+ get => name;
+ set => SetProperty(ref name, value);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<StackPanel Spacing="8">
+ <Button
+ Content="Click me to load a Task to await"
+ Click="{x:Bind ViewModel.ReloadTask}"/>
+ <TextBlock Text="{x:Bind ViewModel.MyTask.Status, Mode=OneWay}"/>
+</StackPanel>
+
+
+private TaskNotifier myTask;
+
+/// <summary>
+/// Gets or sets the name to display.
+/// </summary>
+public Task MyTask
+{
+ get => myTask;
+ private set => SetPropertyAndNotifyOnCompletion(ref myTask, value);
+}
+
+/// <summary>
+/// Simulates an asynchronous method.
+/// </summary>
+public void ReloadTask()
+{
+ MyTask = Task.Delay(3000);
+}
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/ObservableObjectPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/ObservableObjectPage.xaml.cs
new file mode 100644
index 0000000..8d8aac9
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/ObservableObjectPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class ObservableObjectPage : Page
+ {
+ public ObservableObjectPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/PuttingThingsTogetherPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/PuttingThingsTogetherPage.xaml
new file mode 100644
index 0000000..e737785
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/PuttingThingsTogetherPage.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/PuttingThingsTogetherPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/PuttingThingsTogetherPage.xaml.cs
new file mode 100644
index 0000000..efa4b3c
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/PuttingThingsTogetherPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class PuttingThingsTogetherPage : Page
+ {
+ public PuttingThingsTogetherPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditBrowserPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditBrowserPage.xaml
new file mode 100644
index 0000000..3155098
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditBrowserPage.xaml
@@ -0,0 +1,315 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<!--Feed widget-->
+<Grid
+ BorderThickness="1"
+ BorderBrush="{ThemeResource SystemControlBackgroundBaseLowBrush}">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="60"/>
+ <RowDefinition Height="8"/>
+ <RowDefinition Height="*"/>
+ </Grid.RowDefinitions>
+
+ <!--Header with topic selector and refresh button-->
+ <Border
+ Padding="12"
+ Background="{ThemeResource SystemControlAltMediumLowAcrylicElementMediumBrush}">
+ <StackPanel Orientation="Horizontal" Spacing="16">
+ <ComboBox
+ ItemsSource="{x:Bind ViewModel.Subreddits, Mode=OneWay}"
+ SelectedItem="{x:Bind ViewModel.SelectedSubreddit, Mode=TwoWay}"
+ SelectionChangedTrigger="Committed"
+ VerticalAlignment="Center"
+ MinWidth="240">
+ <interactivity:Interaction.Behaviors>
+ <core:EventTriggerBehavior EventName="SelectionChanged">
+ <core:InvokeCommandAction Command="{x:Bind ViewModel.LoadPostsCommand}"/>
+ </core:EventTriggerBehavior>
+ </interactivity:Interaction.Behaviors>
+ </ComboBox>
+ <Button
+ Content="{extensions:SymbolIcon Symbol=Refresh}"
+ Command="{x:Bind ViewModel.LoadPostsCommand}"/>
+ </StackPanel>
+ </Border>
+
+ <!--Items list-->
+ <ListView
+ Grid.Row="1"
+ Grid.RowSpan="2"
+ ItemsSource="{x:Bind ViewModel.Posts}"
+ SelectedItem="{x:Bind ViewModel.SelectedPost, Mode=TwoWay}"
+ ScrollViewer.CanContentRenderOutsideBounds="True">
+ <ListView.ItemContainerStyle>
+ <Style TargetType="ListViewItem">
+ <Setter Property="Background" Value="#10FFFFFF"/>
+ <Setter Property="Margin" Value="0,2,0,2"/>
+ <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
+ </Style>
+ </ListView.ItemContainerStyle>
+ <ListView.ItemTemplate>
+ <DataTemplate x:DataType="models:Post">
+ <Grid
+ ColumnSpacing="8"
+ Padding="16">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+ <TextBlock
+ Text="{x:Bind Title}"
+ FontSize="15"
+ FontWeight="Normal"
+ TextWrapping="WrapWholeWords"
+ VerticalAlignment="Center"/>
+ <controls:ImageEx
+ Grid.Column="1"
+ Source="{x:Bind Thumbnail}"
+ Stretch="Uniform"
+ HorizontalAlignment="Right"
+ Width="120"/>
+ </Grid>
+ </DataTemplate>
+ </ListView.ItemTemplate>
+ </ListView>
+
+ <!--Loading bar-->
+ <muxc:ProgressBar
+ Grid.Row="1"
+ Grid.RowSpan="2"
+ IsHitTestVisible="False"
+ VerticalAlignment="Top"
+ IsIndeterminate="True"
+ Visibility="{x:Bind ViewModel.LoadPostsCommand.IsRunning, Mode=OneWay}"/>
+
+ <!--Header drop shadow-->
+ <Rectangle Grid.Row="1" Height="8" IsHitTestVisible="False">
+ <Rectangle.Fill>
+ <LinearGradientBrush EndPoint="0,0" StartPoint="0,1">
+ <GradientStop Color="#60000000" Offset="1"/>
+ <GradientStop Offset="0"/>
+ </LinearGradientBrush>
+ </Rectangle.Fill>
+ </Rectangle>
+</Grid>
+
+<!--Post widget-->
+<Grid
+ BorderThickness="1"
+ BorderBrush="{ThemeResource SystemControlBackgroundBaseLowBrush}">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*"/>
+ </Grid.RowDefinitions>
+
+ <!--Self text-->
+ <Border
+ Grid.Row="1"
+ extensions:UIElementExtensions.ClipToBounds="True">
+ <ScrollViewer CanContentRenderOutsideBounds="True">
+ <TextBlock
+ Text="{x:Bind ViewModel.Post.SelfText, Mode=OneWay}"
+ TextWrapping="WrapWholeWords"
+ Margin="16"/>
+ </ScrollViewer>
+ </Border>
+
+ <!--Header-->
+ <Grid
+ Grid.Row="0"
+ ColumnSpacing="8"
+ Padding="16"
+ Background="{ThemeResource SystemControlAltMediumLowAcrylicElementMediumBrush}">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+ <TextBlock
+ Text="{x:Bind ViewModel.Post.Title, Mode=OneWay}"
+ FontSize="16"
+ FontWeight="SemiBold"
+ TextWrapping="WrapWholeWords"
+ VerticalAlignment="Center"/>
+ <controls:ImageEx
+ Grid.Column="1"
+ Source="{x:Bind ViewModel.Post.Thumbnail, Mode=OneWay}"
+ Stretch="Uniform"
+ HorizontalAlignment="Right"
+ Width="160"/>
+ </Grid>
+
+ <!--Header drop shadow-->
+ <Rectangle
+ Grid.Row="1"
+ Height="8"
+ VerticalAlignment="Top"
+ IsHitTestVisible="False">
+ <Rectangle.Fill>
+ <LinearGradientBrush EndPoint="0,0" StartPoint="0,1">
+ <GradientStop Color="#60000000" Offset="1"/>
+ <GradientStop Offset="0"/>
+ </LinearGradientBrush>
+ </Rectangle.Fill>
+ </Rectangle>
+</Grid>
+
+
+/// <summary>
+/// A viewmodel for a subreddit widget.
+/// </summary>
+public sealed class SubredditWidgetViewModel : ObservableRecipient
+{
+ /// <summary>
+ /// Gets the <see cref="IRedditService"/> instance to use.
+ /// </summary>
+ private readonly IRedditService RedditService = Ioc.Default.GetRequiredService<IRedditService>();
+
+ /// <summary>
+ /// Gets the <see cref="ISettingsService"/> instance to use.
+ /// </summary>
+ private readonly ISettingsService SettingsService = Ioc.Default.GetRequiredService<ISettingsService>();
+
+ /// <summary>
+ /// An <see cref="AsyncLock"/> instance to avoid concurrent requests.
+ /// </summary>
+ private readonly AsyncLock LoadingLock = new AsyncLock();
+
+ /// <summary>
+ /// Creates a new <see cref="SubredditWidgetViewModel"/> instance.
+ /// </summary>
+ public SubredditWidgetViewModel()
+ {
+ LoadPostsCommand = new AsyncRelayCommand(LoadPostsAsync);
+
+ selectedSubreddit = SettingsService.GetValue<string>(nameof(SelectedSubreddit)) ?? Subreddits[0];
+ }
+
+ /// <summary>
+ /// Gets the <see cref="IAsyncRelayCommand"/> instance responsible for loading posts.
+ /// </summary>
+ public IAsyncRelayCommand LoadPostsCommand { get; }
+
+ /// <summary>
+ /// Gets the collection of loaded posts.
+ /// </summary>
+ public ObservableCollection<Post> Posts { get; } = new ObservableCollection<Post>();
+
+ /// <summary>
+ /// Gets the collection of available subreddits to pick from.
+ /// </summary>
+ public IReadOnlyList<string> Subreddits { get; } = new[]
+ {
+ "microsoft",
+ "windows",
+ "surface",
+ "windowsphone",
+ "dotnet",
+ "csharp"
+ };
+
+ private string selectedSubreddit;
+
+ /// <summary>
+ /// Gets or sets the currently selected subreddit.
+ /// </summary>
+ public string SelectedSubreddit
+ {
+ get => selectedSubreddit;
+ set
+ {
+ SetProperty(ref selectedSubreddit, value);
+
+ SettingsService.SetValue(nameof(SelectedSubreddit), value);
+ }
+ }
+
+ private Post selectedPost;
+
+ /// <summary>
+ /// Gets or sets the currently selected subreddit.
+ /// </summary>
+ public Post SelectedPost
+ {
+ get => selectedPost;
+ set => SetProperty(ref selectedPost, value, true);
+ }
+
+ /// <summary>
+ /// Loads the posts from a specified subreddit.
+ /// </summary>
+ private async Task LoadPostsAsync()
+ {
+ using (await LoadingLock.LockAsync())
+ {
+ try
+ {
+ var response = await RedditService.GetSubredditPostsAsync(SelectedSubreddit);
+
+ Posts.Clear();
+
+ foreach (var item in response.Data.Items)
+ {
+ Posts.Add(item.Data);
+ }
+ }
+ catch
+ {
+ // Whoops!
+ }
+ }
+ }
+}
+
+/// <summary>
+/// A viewmodel for a post widget.
+/// </summary>
+public sealed class PostWidgetViewModel : ObservableRecipient, IRecipient<PropertyChangedMessage<Post>>
+{
+ private Post post;
+
+ /// <summary>
+ /// Gets the currently selected post, if any.
+ /// </summary>
+ public Post Post
+ {
+ get => post;
+ private set => SetProperty(ref post, value);
+ }
+
+ /// <inheritdoc/>
+ public void Receive(PropertyChangedMessage<Post> message)
+ {
+ if (message.Sender.GetType() == typeof(SubredditWidgetViewModel) &&
+ message.PropertyName == nameof(SubredditWidgetViewModel.SelectedPost))
+ {
+ Post = message.NewValue;
+ }
+ }
+}
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditBrowserPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditBrowserPage.xaml.cs
new file mode 100644
index 0000000..4f207f8
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditBrowserPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class RedditBrowserPage : Page
+ {
+ public RedditBrowserPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditServicePage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditServicePage.xaml
new file mode 100644
index 0000000..14e8931
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditServicePage.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditServicePage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditServicePage.xaml.cs
new file mode 100644
index 0000000..ba248f6
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RedditServicePage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class RedditServicePage : Page
+ {
+ public RedditServicePage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RelayCommandPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RelayCommandPage.xaml
new file mode 100644
index 0000000..74a93cc
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RelayCommandPage.xaml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<Page
+ x:Class="MyApp.Views.MyPage"
+ xmlns:viewModels="using:MyApp.ViewModels">
+ <Page.DataContext>
+ <viewModels:MyViewModel x:Name="ViewModel"/>
+ </Page.DataContext>
+
+ <StackPanel Spacing="8">
+ <TextBlock Text="{x:Bind ViewModel.Counter, Mode=OneWay}"/>
+ <Button
+ Content="Click me!"
+ Command="{x:Bind ViewModel.IncrementCounterCommand}"/>
+ </StackPanel>
+</Page>
+
+
+public class MyViewModel : ObservableObject
+{
+ public MyViewModel()
+ {
+ IncrementCounterCommand = new RelayCommand(IncrementCounter);
+ }
+
+ /// <summary>
+ /// Gets the <see cref="ICommand"/> responsible for incrementing <see cref="Counter"/>.
+ /// </summary>
+ public ICommand IncrementCounterCommand { get; }
+
+ private int counter;
+
+ /// <summary>
+ /// Gets the current value of the counter.
+ /// </summary>
+ public int Counter
+ {
+ get => counter;
+ private set => SetProperty(ref counter, value);
+ }
+
+ /// <summary>
+ /// Increments <see cref="Counter"/>.
+ /// </summary>
+ private void IncrementCounter() => Counter++;
+}
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RelayCommandPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RelayCommandPage.xaml.cs
new file mode 100644
index 0000000..0b06ee0
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/RelayCommandPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class RelayCommandPage : Page
+ {
+ public RelayCommandPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingUpTheViewModelsPage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingUpTheViewModelsPage.xaml
new file mode 100644
index 0000000..c0788e8
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingUpTheViewModelsPage.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingUpTheViewModelsPage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingUpTheViewModelsPage.xaml.cs
new file mode 100644
index 0000000..dd6f011
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingUpTheViewModelsPage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class SettingUpTheViewModelsPage : Page
+ {
+ public SettingUpTheViewModelsPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingsServicePage.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingsServicePage.xaml
new file mode 100644
index 0000000..62072e9
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingsServicePage.xaml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingsServicePage.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingsServicePage.xaml.cs
new file mode 100644
index 0000000..e19717b
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/SettingsServicePage.xaml.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class SettingsServicePage : Page
+ {
+ public SettingsServicePage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/PostWidget.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/PostWidget.xaml
new file mode 100644
index 0000000..fdf2fc9
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/PostWidget.xaml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/PostWidget.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/PostWidget.xaml.cs
new file mode 100644
index 0000000..e909345
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/PostWidget.xaml.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views.Widgets
+{
+ public sealed partial class PostWidget : UserControl
+ {
+ public PostWidget()
+ {
+ this.InitializeComponent();
+ this.Loaded += (s, e) => ViewModel.IsActive = true;
+ this.Unloaded += (s, e) => ViewModel.IsActive = false;
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/SubredditWidget.xaml b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/SubredditWidget.xaml
new file mode 100644
index 0000000..981f200
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/SubredditWidget.xaml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/SubredditWidget.xaml.cs b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/SubredditWidget.xaml.cs
new file mode 100644
index 0000000..53363be
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/Views/Widgets/SubredditWidget.xaml.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.UI.Xaml.Controls;
+
+namespace MvvmSampleUwp.Views.Widgets
+{
+ public sealed partial class SubredditWidget : UserControl
+ {
+ public SubredditWidget()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/app.manifest b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/app.manifest
new file mode 100644
index 0000000..547717f
--- /dev/null
+++ b/samples/MvvmSampleWinUI3Desktop/MvvmSampleWinUI3Desktop/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/samples/NuGet.config b/samples/NuGet.config
index 3463255..2f5ef0d 100644
--- a/samples/NuGet.config
+++ b/samples/NuGet.config
@@ -3,5 +3,6 @@
+
\ No newline at end of file