From a3b1d80c4679ac8d0036d6070c9beb1cee23177e Mon Sep 17 00:00:00 2001 From: "Muyuan Li (from Dev Box)" Date: Wed, 11 Feb 2026 16:40:53 +0800 Subject: [PATCH 01/51] Create Dotnet New Template Branch --- dev/VSIX/DotnetNewTemplates/README.md | 34 ++++ ...WinAppSdk.CSharp.DotnetNewTemplates.csproj | 107 ++++++++++++ .../.template.config/dotnetcli.host.json | 9 ++ .../.template.config/template.json | 44 +++++ .../.template.config/dotnetcli.host.json | 9 ++ .../.template.config/template.json | 52 ++++++ .../.template.config/dotnetcli.host.json | 35 ++++ .../packaged/.template.config/template.json | 153 ++++++++++++++++++ .../templates/packaged/ProjectTemplate.sln | 30 ++++ .../.template.config/dotnetcli.host.json | 21 +++ .../.template.config/template.json | 88 ++++++++++ .../.template.config/dotnetcli.host.json | 21 +++ .../unit-test/.template.config/template.json | 88 ++++++++++ dev/VSIX/WindowsAppSDK.Extension.sln | 10 ++ 14 files changed, 701 insertions(+) create mode 100644 dev/VSIX/DotnetNewTemplates/README.md create mode 100644 dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj create mode 100644 dev/VSIX/DotnetNewTemplates/templates/class-library/.template.config/dotnetcli.host.json create mode 100644 dev/VSIX/DotnetNewTemplates/templates/class-library/.template.config/template.json create mode 100644 dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/dotnetcli.host.json create mode 100644 dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/template.json create mode 100644 dev/VSIX/DotnetNewTemplates/templates/packaged/.template.config/dotnetcli.host.json create mode 100644 dev/VSIX/DotnetNewTemplates/templates/packaged/.template.config/template.json create mode 100644 dev/VSIX/DotnetNewTemplates/templates/packaged/ProjectTemplate.sln create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/.template.config/dotnetcli.host.json create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/.template.config/template.json create mode 100644 dev/VSIX/DotnetNewTemplates/templates/unit-test/.template.config/dotnetcli.host.json create mode 100644 dev/VSIX/DotnetNewTemplates/templates/unit-test/.template.config/template.json diff --git a/dev/VSIX/DotnetNewTemplates/README.md b/dev/VSIX/DotnetNewTemplates/README.md new file mode 100644 index 0000000000..7b250be997 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/README.md @@ -0,0 +1,34 @@ +# Windows App SDK .NET CLI Templates + +This directory contains the assets that produce the `dotnet new` template pack for the +Windows App SDK C# Desktop templates. The templates are built directly from the +existing Visual Studio template sources under `dev/VSIX/ProjectTemplates/Desktop/CSharp` +and `dev/VSIX/ItemTemplates/Desktop/CSharp`, so we only maintain one copy of the XAML, +code-behind, and project files. + +## Templates + +| Short name | Identity | Description | +|-----------------------|-------------------------------------------------|--------------------------------------------| +| `wasdk-packaged` | `Microsoft.WindowsAppSDK.CSharp.Packaged` | WinUI 3 packaged app + MSIX packaging proj | +| `wasdk-single` | `Microsoft.WindowsAppSDK.CSharp.SingleProject` | Single-project MSIX WinUI 3 app | +| `wasdk-classlib` | `Microsoft.WindowsAppSDK.CSharp.ClassLibrary` | WinUI 3 class library | +| `wasdk-unittest` | `Microsoft.WindowsAppSDK.CSharp.UnitTest` | WinUI 3 packaged test app | +| `wasdk-item-blankwin` | `Microsoft.WindowsAppSDK.CSharp.Item.BlankWindow` | WinUI 3 Blank Window item template | + +Run `dotnet new -n MyProject` to create a project from the pack once it +is installed. + +## Building the NuGet package + +The pack project lives at `dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj`. +It links the Visual Studio template files into the template pack so there is a single +source of truth. The build produces a template NuGet package in `localpackages/`: + +```powershell +cd +dotnet pack dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj -c Release -o localpackages +``` + +`BuildAll.ps1` also runs the same packing step so official builds automatically emit +the template pack alongside the other Windows App SDK artifacts. diff --git a/dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj b/dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj new file mode 100644 index 0000000000..6321e5e7aa --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj @@ -0,0 +1,107 @@ + + + net8.0 + false + disable + false + false + true + Template + Microsoft.WindowsAppSDK.CSharp.Templates + $(MicrosoftWindowsAppSDKVersion) + Microsoft + Microsoft + dotnet new templates that mirror the Windows App SDK WinUI 3 Visual Studio templates. + windows;winappsdk;winui;desktop;csharp + https://github.com/microsoft/WindowsAppSDK + https://github.com/microsoft/WindowsAppSDK + git + MIT + README.md + true + $(NoWarn);NU5128 + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\localpackages')) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/VSIX/DotnetNewTemplates/templates/class-library/.template.config/dotnetcli.host.json b/dev/VSIX/DotnetNewTemplates/templates/class-library/.template.config/dotnetcli.host.json new file mode 100644 index 0000000000..9a9720c73d --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/class-library/.template.config/dotnetcli.host.json @@ -0,0 +1,9 @@ +{ + "$schema": "http://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "dotnetVersion": { + "longName": "dotnet-version", + "shortName": "tfm" + } + } +} diff --git a/dev/VSIX/DotnetNewTemplates/templates/class-library/.template.config/template.json b/dev/VSIX/DotnetNewTemplates/templates/class-library/.template.config/template.json new file mode 100644 index 0000000000..4da0d88e35 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/class-library/.template.config/template.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Microsoft", + "classifications": ["Windows", "WinUI", "Library"], + "identity": "Microsoft.WindowsAppSDK.CSharp.ClassLibrary", + "groupIdentity": "Microsoft.WindowsAppSDK.CSharp.ClassLibrary", + "name": "Windows App SDK Class Library", + "shortName": "wasdk-classlib", + "sourceName": "ProjectTemplate", + "defaultName": "WinAppSdkLibrary", + "description": "Creates a WinUI 3 class library that targets the Windows App SDK.", + "preferNameDirectory": true, + "symbols": { + "safeProjectName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "safe_name", + "replaces": "$safeprojectname$" + }, + "projectName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "identity", + "replaces": "$projectname$" + }, + "dotnetVersion": { + "type": "parameter", + "datatype": "text", + "replaces": "$DotNetVersion$", + "defaultValue": "net8.0" + } + }, + "primaryOutputs": [ + { "path": "ProjectTemplate/ProjectTemplate.csproj" } + ], + "forms": { + "safe_name": { + "identifier": "safe_name" + }, + "identity": { + "identifier": "identity" + } + } +} diff --git a/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/dotnetcli.host.json b/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/dotnetcli.host.json new file mode 100644 index 0000000000..07c0730f73 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/dotnetcli.host.json @@ -0,0 +1,9 @@ +{ + "$schema": "http://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "rootNamespace": { + "longName": "root-namespace", + "shortName": "ns" + } + } +} diff --git a/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/template.json b/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/template.json new file mode 100644 index 0000000000..006726f093 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/template.json @@ -0,0 +1,52 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Microsoft", + "classifications": ["Windows", "WinUI", "Item"], + "identity": "Microsoft.WindowsAppSDK.CSharp.Item.BlankWindow", + "groupIdentity": "Microsoft.WindowsAppSDK.CSharp.Item.BlankWindow", + "name": "Windows App SDK Blank Window (Item)", + "shortName": "wasdk-item-blankwin", + "sourceName": "BlankWindow", + "defaultName": "BlankWindow", + "description": "Adds a WinUI 3 Window with XAML markup and code-behind.", + "preferNameDirectory": false, + "symbols": { + "rootNamespace": { + "type": "parameter", + "datatype": "text", + "replaces": "$rootnamespace$", + "defaultValue": "AppNamespace", + "description": "Namespace for the generated item." + }, + "safeItemName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "safe_name", + "replaces": "$safeitemname$" + }, + "itemName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "identity", + "replaces": "$itemname$" + }, + "fileInputName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "identity", + "replaces": "$fileinputname$" + } + }, + "primaryOutputs": [ + { "path": "BlankWindow.xaml" }, + { "path": "BlankWindow.xaml.cs" } + ], + "forms": { + "safe_name": { + "identifier": "safe_name" + }, + "identity": { + "identifier": "identity" + } + } +} diff --git a/dev/VSIX/DotnetNewTemplates/templates/packaged/.template.config/dotnetcli.host.json b/dev/VSIX/DotnetNewTemplates/templates/packaged/.template.config/dotnetcli.host.json new file mode 100644 index 0000000000..76f1a23ce5 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/packaged/.template.config/dotnetcli.host.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "dotnetVersion": { + "longName": "dotnet-version", + "shortName": "tfm", + "description": "Base TFM for the WinUI project (for example net8.0)." + }, + "targetPlatformVersion": { + "longName": "target-platform-version", + "shortName": "tpv", + "description": "Windows SDK version used by the packaging project." + }, + "targetPlatformMinVersion": { + "longName": "target-platform-min-version", + "shortName": "tpmv", + "description": "Minimum supported Windows version." + }, + "publisherDn": { + "longName": "publisher-dn", + "shortName": "publisher", + "description": "Publisher distinguished name stored in Package.appxmanifest." + }, + "publisherDisplay": { + "longName": "publisher-display", + "shortName": "publisherName", + "description": "Publisher display name stored in Package.appxmanifest." + }, + "culture": { + "longName": "culture", + "shortName": "lcid", + "description": "Default display language for the packaging project." + } + } +} diff --git a/dev/VSIX/DotnetNewTemplates/templates/packaged/.template.config/template.json b/dev/VSIX/DotnetNewTemplates/templates/packaged/.template.config/template.json new file mode 100644 index 0000000000..19bbe00e62 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/packaged/.template.config/template.json @@ -0,0 +1,153 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Microsoft", + "classifications": ["Windows", "WinUI", "Desktop", "Packaged"], + "identity": "Microsoft.WindowsAppSDK.CSharp.Packaged", + "groupIdentity": "Microsoft.WindowsAppSDK.CSharp.Packaged", + "name": "Windows App SDK Packaged App", + "shortName": "wasdk-packaged", + "sourceName": "ProjectTemplate", + "defaultName": "WinAppSdkApp", + "description": "Creates a WinUI 3 desktop app plus the packaging project needed for MSIX deployment.", + "preferNameDirectory": true, + "precedence": 100, + "symbols": { + "safeProjectName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "safe_name", + "replaces": "$safeprojectname$" + }, + "projectName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "identity", + "replaces": "$projectname$" + }, + "dotnetVersion": { + "type": "parameter", + "datatype": "text", + "replaces": "$DotNetVersion$", + "defaultValue": "net8.0", + "description": "Base target framework moniker (TFM) for the WinUI project." + }, + "extDotnetVersion": { + "type": "derived", + "valueSource": "dotnetVersion", + "valueTransform": "identity", + "replaces": "$ext_DotNetVersion$" + }, + "targetPlatformVersion": { + "type": "parameter", + "datatype": "text", + "replaces": "$targetplatformversion$", + "defaultValue": "10.0.22621.0", + "description": "Windows SDK version used by the packaging project." + }, + "targetPlatformMinVersion": { + "type": "parameter", + "datatype": "text", + "replaces": "$targetplatformminversion$", + "defaultValue": "10.0.17763.0", + "description": "Minimum supported Windows version." + }, + "publisherDn": { + "type": "parameter", + "datatype": "text", + "replaces": "$XmlEscapedPublisherDistinguishedName$", + "defaultValue": "CN=AppPublisher", + "description": "Publisher distinguished name used inside Package.appxmanifest." + }, + "publisherDisplay": { + "type": "parameter", + "datatype": "text", + "replaces": "$XmlEscapedPublisher$", + "defaultValue": "AppPublisher", + "description": "Publisher display name shown in the manifest and Settings page." + }, + "culture": { + "type": "parameter", + "datatype": "text", + "replaces": "$currentuiculturename$", + "defaultValue": "en-US", + "description": "Default display language for the packaging project." + }, + "guid1": { + "type": "generated", + "generator": "guid", + "parameters": { + "format": "D" + }, + "replaces": "$guid1$" + }, + "guid2": { + "type": "generated", + "generator": "guid", + "parameters": { + "format": "D" + }, + "replaces": "$guid2$" + }, + "guid4": { + "type": "generated", + "generator": "guid", + "parameters": { + "format": "D" + }, + "replaces": "$guid4$" + }, + "guid9": { + "type": "generated", + "generator": "guid", + "parameters": { + "format": "D" + }, + "replaces": "$guid9$" + }, + "extProjectName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "safe_name", + "replaces": "$ext_projectname$" + } + }, + "sources": [ + { + "modifiers": [ + { + "replacements": [ + { + "before": "$if$($includeKeyFile$==true)", + "after": "" + }, + { + "before": "$endif$", + "after": "" + }, + { + "before": "$targetnametoken$", + "after": "{{safeProjectName}}" + }, + { + "before": "$targetentrypoint$", + "after": "{{safeProjectName}}.App" + } + ] + } + ] + } + ], + "primaryOutputs": [ + { "path": "ProjectTemplate.sln" }, + { "path": "ProjectTemplate/ProjectTemplate.csproj" }, + { "path": "ProjectTemplate.Package/ProjectTemplate.Package.wapproj" } + ], + "forms": { + "safe_name": { + "identifier": "safe_name" + }, + "identity": { + "identifier": "identity" + } + } +} diff --git a/dev/VSIX/DotnetNewTemplates/templates/packaged/ProjectTemplate.sln b/dev/VSIX/DotnetNewTemplates/templates/packaged/ProjectTemplate.sln new file mode 100644 index 0000000000..564f9b4492 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/packaged/ProjectTemplate.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectTemplate", "ProjectTemplate\ProjectTemplate.csproj", "{$guid2$}" +EndProject +Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "ProjectTemplate.Package", "ProjectTemplate.Package\ProjectTemplate.Package.wapproj", "{$guid1$}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {$guid2$}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {$guid2$}.Debug|Any CPU.Build.0 = Debug|Any CPU + {$guid2$}.Release|Any CPU.ActiveCfg = Release|Any CPU + {$guid2$}.Release|Any CPU.Build.0 = Release|Any CPU + {$guid1$}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {$guid1$}.Debug|Any CPU.Build.0 = Debug|Any CPU + {$guid1$}.Release|Any CPU.ActiveCfg = Release|Any CPU + {$guid1$}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {$guid4$} + EndGlobalSection +EndGlobal diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/.template.config/dotnetcli.host.json b/dev/VSIX/DotnetNewTemplates/templates/single-project/.template.config/dotnetcli.host.json new file mode 100644 index 0000000000..a23dfacae6 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/.template.config/dotnetcli.host.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "dotnetVersion": { + "longName": "dotnet-version", + "shortName": "tfm" + }, + "targetPlatformMinVersion": { + "longName": "target-platform-min-version", + "shortName": "tpmv" + }, + "publisherDn": { + "longName": "publisher-dn", + "shortName": "publisher" + }, + "publisherDisplay": { + "longName": "publisher-display", + "shortName": "publisherName" + } + } +} diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/.template.config/template.json b/dev/VSIX/DotnetNewTemplates/templates/single-project/.template.config/template.json new file mode 100644 index 0000000000..6d095affac --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/.template.config/template.json @@ -0,0 +1,88 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Microsoft", + "classifications": ["Windows", "WinUI", "Desktop", "Packaged"], + "identity": "Microsoft.WindowsAppSDK.CSharp.SingleProject", + "groupIdentity": "Microsoft.WindowsAppSDK.CSharp.SingleProject", + "name": "Windows App SDK Single-project App", + "shortName": "wasdk-single", + "sourceName": "ProjectTemplate", + "defaultName": "WinAppSdkSingle", + "description": "Creates a WinUI 3 desktop app that uses single-project MSIX tooling.", + "preferNameDirectory": true, + "symbols": { + "safeProjectName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "safe_name", + "replaces": "$safeprojectname$" + }, + "projectName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "identity", + "replaces": "$projectname$" + }, + "dotnetVersion": { + "type": "parameter", + "datatype": "text", + "replaces": "$DotNetVersion$", + "defaultValue": "net8.0" + }, + "targetPlatformMinVersion": { + "type": "parameter", + "datatype": "text", + "replaces": "$targetplatformminversion$", + "defaultValue": "10.0.17763.0" + }, + "publisherDn": { + "type": "parameter", + "datatype": "text", + "replaces": "$XmlEscapedPublisherDistinguishedName$", + "defaultValue": "CN=AppPublisher" + }, + "publisherDisplay": { + "type": "parameter", + "datatype": "text", + "replaces": "$XmlEscapedPublisher$", + "defaultValue": "AppPublisher" + }, + "guid9": { + "type": "generated", + "generator": "guid", + "parameters": { + "format": "D" + }, + "replaces": "$guid9$" + } + }, + "sources": [ + { + "modifiers": [ + { + "replacements": [ + { + "before": "$targetnametoken$", + "after": "{{safeProjectName}}" + }, + { + "before": "$targetentrypoint$", + "after": "{{safeProjectName}}.App" + } + ] + } + ] + } + ], + "primaryOutputs": [ + { "path": "ProjectTemplate/ProjectTemplate.csproj" } + ], + "forms": { + "safe_name": { + "identifier": "safe_name" + }, + "identity": { + "identifier": "identity" + } + } +} diff --git a/dev/VSIX/DotnetNewTemplates/templates/unit-test/.template.config/dotnetcli.host.json b/dev/VSIX/DotnetNewTemplates/templates/unit-test/.template.config/dotnetcli.host.json new file mode 100644 index 0000000000..a23dfacae6 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/unit-test/.template.config/dotnetcli.host.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "dotnetVersion": { + "longName": "dotnet-version", + "shortName": "tfm" + }, + "targetPlatformMinVersion": { + "longName": "target-platform-min-version", + "shortName": "tpmv" + }, + "publisherDn": { + "longName": "publisher-dn", + "shortName": "publisher" + }, + "publisherDisplay": { + "longName": "publisher-display", + "shortName": "publisherName" + } + } +} diff --git a/dev/VSIX/DotnetNewTemplates/templates/unit-test/.template.config/template.json b/dev/VSIX/DotnetNewTemplates/templates/unit-test/.template.config/template.json new file mode 100644 index 0000000000..7274bbbfe4 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/unit-test/.template.config/template.json @@ -0,0 +1,88 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Microsoft", + "classifications": ["Windows", "WinUI", "Test"], + "identity": "Microsoft.WindowsAppSDK.CSharp.UnitTest", + "groupIdentity": "Microsoft.WindowsAppSDK.CSharp.UnitTest", + "name": "Windows App SDK Unit Test App", + "shortName": "wasdk-unittest", + "sourceName": "ProjectTemplate", + "defaultName": "WinAppSdkTests", + "description": "Creates a WinUI 3 packaged test app that is pre-wired for MSTest.", + "preferNameDirectory": true, + "symbols": { + "safeProjectName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "safe_name", + "replaces": "$safeprojectname$" + }, + "projectName": { + "type": "derived", + "valueSource": "name", + "valueTransform": "identity", + "replaces": "$projectname$" + }, + "dotnetVersion": { + "type": "parameter", + "datatype": "text", + "replaces": "$DotNetVersion$", + "defaultValue": "net8.0" + }, + "targetPlatformMinVersion": { + "type": "parameter", + "datatype": "text", + "replaces": "$targetplatformminversion$", + "defaultValue": "10.0.17763.0" + }, + "publisherDn": { + "type": "parameter", + "datatype": "text", + "replaces": "$XmlEscapedPublisherDistinguishedName$", + "defaultValue": "CN=AppPublisher" + }, + "publisherDisplay": { + "type": "parameter", + "datatype": "text", + "replaces": "$XmlEscapedPublisher$", + "defaultValue": "AppPublisher" + }, + "guid9": { + "type": "generated", + "generator": "guid", + "parameters": { + "format": "D" + }, + "replaces": "$guid9$" + } + }, + "sources": [ + { + "modifiers": [ + { + "replacements": [ + { + "before": "$targetnametoken$", + "after": "{{safeProjectName}}" + }, + { + "before": "$targetentrypoint$", + "after": "{{safeProjectName}}.App" + } + ] + } + ] + } + ], + "primaryOutputs": [ + { "path": "ProjectTemplate/ProjectTemplate.csproj" } + ], + "forms": { + "safe_name": { + "identifier": "safe_name" + }, + "identity": { + "identifier": "identity" + } + } +} diff --git a/dev/VSIX/WindowsAppSDK.Extension.sln b/dev/VSIX/WindowsAppSDK.Extension.sln index d74763eb35..72b8bbcad1 100644 --- a/dev/VSIX/WindowsAppSDK.Extension.sln +++ b/dev/VSIX/WindowsAppSDK.Extension.sln @@ -84,6 +84,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinUI.Desktop.Cs.UnitTestAp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinUI.Desktop.CppWinRT.UnitTestApp", "ProjectTemplates\Desktop\CppWinRT\UnitTestApp\WinUI.Desktop.CppWinRT.UnitTestApp.csproj", "{DFA6B905-57A3-4B31-A74B-25BA2243389F}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DotnetNewTemplates", "DotnetNewTemplates", "{8EAF0E4D-B0E3-4A21-B0F6-EA69F0215F5A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinAppSdk.CSharp.DotnetNewTemplates", "DotnetNewTemplates\WinAppSdk.CSharp.DotnetNewTemplates.csproj", "{F9C3BC5E-2E45-4272-A4F1-0F63CFE9A2DE}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{96699B60-67B5-4186-9FEC-3963ED811FF0}" ProjectSection(SolutionItems) = preProject Shared\WizardImplementation.cs = Shared\WizardImplementation.cs @@ -183,6 +187,10 @@ Global {DFA6B905-57A3-4B31-A74B-25BA2243389F}.Debug|Any CPU.Build.0 = Debug|Any CPU {DFA6B905-57A3-4B31-A74B-25BA2243389F}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFA6B905-57A3-4B31-A74B-25BA2243389F}.Release|Any CPU.Build.0 = Release|Any CPU + {F9C3BC5E-2E45-4272-A4F1-0F63CFE9A2DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9C3BC5E-2E45-4272-A4F1-0F63CFE9A2DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9C3BC5E-2E45-4272-A4F1-0F63CFE9A2DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9C3BC5E-2E45-4272-A4F1-0F63CFE9A2DE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -220,6 +228,8 @@ Global {45777B9F-9CC1-47D7-BD66-C3C194277902} = {EC44EE95-8EC9-4EE5-A8A6-E6BE2F32C843} {DFA6B905-57A3-4B31-A74B-25BA2243389F} = {1A8A7481-2108-496B-802D-39F9C08D86F3} {96699B60-67B5-4186-9FEC-3963ED811FF0} = {50DEEF87-BC2F-4B45-B97D-B83135F12786} + {8EAF0E4D-B0E3-4A21-B0F6-EA69F0215F5A} = {34272930-CFC7-4E33-9DB9-88E82AE2C046} + {F9C3BC5E-2E45-4272-A4F1-0F63CFE9A2DE} = {8EAF0E4D-B0E3-4A21-B0F6-EA69F0215F5A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A417E325-71BD-4C29-94F8-F21FB3F5C392} From f0659d9b03362e55ff4b2ea0e6e2acd765d75daf Mon Sep 17 00:00:00 2001 From: "Muyuan Li (from Dev Box)" Date: Wed, 11 Feb 2026 18:18:16 +0800 Subject: [PATCH 02/51] Unblocking single proj and item template from building --- .../item-blank-window/.template.config/template.json | 4 ++-- .../CSharp/SingleProjectPackagedApp/ProjectTemplate.csproj | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/template.json b/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/template.json index 006726f093..16e5395b46 100644 --- a/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/template.json +++ b/dev/VSIX/DotnetNewTemplates/templates/item-blank-window/.template.config/template.json @@ -12,8 +12,8 @@ "preferNameDirectory": false, "symbols": { "rootNamespace": { - "type": "parameter", - "datatype": "text", + "type": "bind", + "binding": "msbuild:RootNamespace", "replaces": "$rootnamespace$", "defaultValue": "AppNamespace", "description": "Namespace for the generated item." diff --git a/dev/VSIX/ProjectTemplates/Desktop/CSharp/SingleProjectPackagedApp/ProjectTemplate.csproj b/dev/VSIX/ProjectTemplates/Desktop/CSharp/SingleProjectPackagedApp/ProjectTemplate.csproj index 4c6b8df175..332d862e28 100644 --- a/dev/VSIX/ProjectTemplates/Desktop/CSharp/SingleProjectPackagedApp/ProjectTemplate.csproj +++ b/dev/VSIX/ProjectTemplates/Desktop/CSharp/SingleProjectPackagedApp/ProjectTemplate.csproj @@ -37,6 +37,12 @@ + + + + + + + - - - - - - - - - - - - Date: Sat, 14 Feb 2026 18:30:17 +0800 Subject: [PATCH 05/51] feat(docs): add design principles, globalization, performance, security, and testing guidelines (#6217) --- ...WinAppSdk.CSharp.DotnetNewTemplates.csproj | 6 + .../templates/single-project/Agents.md | 116 +++++ .../accessibility.instructions.md | 60 +++ .../instructions/code-quality.instructions.md | 168 ++++++++ .../design-principles.instructions.md | 132 ++++++ .../globalization.instructions.md | 53 +++ .../instructions/performance.instructions.md | 51 +++ .../instructions/security.instructions.md | 48 +++ .../instructions/testing.instructions.md | 276 ++++++++++++ .../winui-best-practices.instructions.md | 404 ++++++++++++++++++ 10 files changed, 1314 insertions(+) create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/Agents.md create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/accessibility.instructions.md create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/code-quality.instructions.md create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/design-principles.instructions.md create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/globalization.instructions.md create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/performance.instructions.md create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/security.instructions.md create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/testing.instructions.md create mode 100644 dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/winui-best-practices.instructions.md diff --git a/dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj b/dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj index 1fec1f4401..4b1fb2cfd3 100644 --- a/dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj +++ b/dev/VSIX/DotnetNewTemplates/WinAppSdk.CSharp.DotnetNewTemplates.csproj @@ -39,6 +39,12 @@ + + diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/Agents.md b/dev/VSIX/DotnetNewTemplates/templates/single-project/Agents.md new file mode 100644 index 0000000000..f5b64ebc22 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/Agents.md @@ -0,0 +1,116 @@ +# Copilot Agent Instructions — WinUI 3 / WinAppSDK + +## Project Overview + +This is a **WinUI 3** desktop application built on the **Windows App SDK**. It uses MSIX packaging and supports x86, x64, and ARM64 architectures. + +> **Source of truth for versions & names:** Always read the project `.csproj` to determine the current `TargetFramework`, `RuntimeIdentifiers`, `Platforms`, `RootNamespace`, and `Microsoft.WindowsAppSDK` package version. Never hard-code project names or version numbers in instruction files. +> +> Throughout this document and the instruction files, `` is a placeholder — replace it with the actual project folder/assembly name (derived from the `.csproj` filename). + +| Property | How to determine | +|---|---| +| UI Framework | WinUI 3 (`Microsoft.UI.Xaml`) — always used | +| App SDK | Read `Microsoft.WindowsAppSDK` version from `.csproj` `` | +| Runtime / TFM | Read `` from `.csproj` (e.g., `net8.0-windows10.0.19041.0`) | +| Target OS | Derived from `` and `` in `.csproj` | +| Platforms | Read `` from `.csproj` (e.g., `x86;x64;ARM64`) | +| Packaging | MSIX (`true`) | +| Namespace | Read `` from `.csproj` | +| Nullable | Read `` from `.csproj` | + +## Instruction Files Index + +All detailed agent instructions are organized under `instructions/`: + +| File | Scope | +|---|---| +| [design-principles.instructions.md](instructions/design-principles.instructions.md) | DRY, KISS, SOLID, YAGNI | +| [globalization.instructions.md](instructions/globalization.instructions.md) | Globalization & Localization | +| [accessibility.instructions.md](instructions/accessibility.instructions.md) | Accessibility | +| [security.instructions.md](instructions/security.instructions.md) | Security | +| [performance.instructions.md](instructions/performance.instructions.md) | Performance | +| [code-quality.instructions.md](instructions/code-quality.instructions.md) | Static Analysis, StyleCop, Code Cleanup | +| [winui-best-practices.instructions.md](instructions/winui-best-practices.instructions.md) | WinUI 3 / WinAppSDK patterns & references | +| [testing.instructions.md](instructions/testing.instructions.md) | Unit Testing, Build & Run | + +## Core Agent Workflow + +Every time you work on this codebase, follow this checklist: + +### Before Writing Code +1. **Review the original goal** — Re-read the user's request and confirm you understand the intent. +2. **Check existing code** — Search for related implementations to avoid duplication (DRY). +3. **Find the right API** — If the task involves a platform capability (UI controls, file access, camera, notifications, sensors, etc.), look up the correct API in the [WinUI 3 API Reference](https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/) before writing code. +4. **Plan the approach** — Consider SOLID principles and identify which classes/interfaces are involved. + +### While Writing Code +5. **Apply Design Principles** — DRY, KISS, SOLID, YAGNI (see [design-principles](instructions/design-principles.instructions.md)). +6. **Follow Fundamentals** — Globalization ([globalization](instructions/globalization.instructions.md)), Accessibility ([accessibility](instructions/accessibility.instructions.md)), Security ([security](instructions/security.instructions.md)), Performance ([performance](instructions/performance.instructions.md)). +7. **Respect Code Quality Rules** — Static analysis & StyleCop (see [code-quality](instructions/code-quality.instructions.md)). +8. **Follow WinUI Patterns** — MVVM, x:Bind, community toolkit, and API verification (see [winui-best-practices](instructions/winui-best-practices.instructions.md)). + +### After Writing Code +9. **Remove unused code** — Delete unused `using` statements, dead code, commented-out blocks. +10. **Write unit tests** — Every new public method/class needs tests (see [testing](instructions/testing.instructions.md)). +11. **Build the project** — Run `dotnet build -c Debug -p:Platform=x64` from the project folder and fix all warnings/errors. +12. **Run tests** — Run tests related to the change using `--filter` (see [testing](instructions/testing.instructions.md)). Run the full suite only when the change is cross-cutting. +13. **Register the MSIX package** — See [Build, Run & Deploy](#build-run--deploy) below. +14. **Re-review against original goal** — Confirm the implementation matches the user's request. + +## Build, Run & Deploy + +This is an MSIX-packaged WinUI 3 app. You **must** pass both `-c` (Configuration) and `-p:Platform=` to every `dotnet` command. + +### Prerequisites + +- **Developer Mode must be enabled** on Windows. Verify with: + ```powershell + # Check developer mode + Get-WindowsDeveloperLicense + # If not enabled: Settings → System → For developers → Developer Mode → On + ``` + +### Build + +```powershell +# Run from the project folder containing the .csproj +cd + +# Debug x64 (default development target) +dotnet build -c Debug -p:Platform=x64 + +# Release x64 +dotnet build -c Release -p:Platform=x64 + +# Other platforms +dotnet build -c Debug -p:Platform=x86 +dotnet build -c Debug -p:Platform=ARM64 +``` + +### Register & Run the MSIX Package (Sideload) + +After building, register the app package so Windows can launch it: + +```powershell +# Register the built MSIX package from the build output +# Path pattern: .\\bin\\\\win-\AppxManifest.xml +# Read from the project .csproj to build the correct path. +Add-AppxPackage -Register .\\bin\x64\Debug\\win-x64\AppxManifest.xml +``` + +> **Note:** Replace `$(TargetFramework)` with the actual value from `.csproj` (e.g., `net8.0-windows10.0.19041.0`). +> Adjust `` and `` to match your build target. + +### Run Tests + +```powershell +# Run from the test project folder +cd .Tests +dotnet test -c Debug -p:Platform=x64 +``` + +## Key Rules (Always Enforced) + +- **Every change must build and pass tests** — Run `dotnet build` and `dotnet test` (see [Build, Run & Deploy](#build-run--deploy)) before considering any task complete. +- **Follow all instruction files** — The detailed rules in `instructions/` are authoritative. Do not skip them. diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/accessibility.instructions.md b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/accessibility.instructions.md new file mode 100644 index 0000000000..7e93ae3325 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/accessibility.instructions.md @@ -0,0 +1,60 @@ +--- +description: 'Accessibility requirements for interactive controls, keyboard navigation, screen readers, and contrast' +applyTo: '**/*.cs, **/*.xaml' +--- + +# Accessibility + +These rules apply to **every UI change**. They are not optional add-ons. + +--- + +## Rules + +- **Every interactive control** must have an `AutomationProperties.Name` or `AutomationProperties.LabeledBy`. +- Add a stable, unique `AutomationProperties.AutomationId` for controls targeted by UI automation tests (and for key interactive elements). +- Use semantic XAML controls — prefer `Button`, `HyperlinkButton`, `ListView` over styled `Border`/`Grid` with click handlers. +- Ensure **keyboard navigation** works for all features: + - Logical tab order via `TabIndex`. + - `AccessKey` bindings for frequently used actions. + - `KeyboardAccelerator` for shortcut keys. +- Maintain **minimum contrast ratios** (4.5:1 for normal text, 3:1 for large text) — test in High Contrast mode. +- Support **screen readers** (Narrator / NVDA): test that all content is announced correctly. +- Images must have `AutomationProperties.Name` describing the image purpose (or `AutomationProperties.AccessibilityView="Raw"` for decorative images). +- Do not rely on colour alone to convey meaning — add icons, text, or patterns. + +## Anti-patterns + +- Clickable `TextBlock` or `Image` without `AutomationProperties`. +- Custom controls that are not keyboard-focusable. +- Using `Visibility.Collapsed` to "hide" content from screen readers (use `AccessibilityView` instead). + +## Validation + +- Build & register the MSIX package — see **Build, Run & Deploy** in `Agents.md`. +- Test keyboard navigation: tab through every new/changed UI area. +- Test High Contrast: switch to Windows High Contrast theme and verify readability. +- Run Accessibility Insights for Windows on the app. + +### Verification Checklist + +- [ ] All interactive controls have `AutomationProperties.Name` +- [ ] Keyboard navigation works for the changed area +- [ ] Tested with High Contrast theme enabled +- [ ] Tab through the entire UI with keyboard only. +- [ ] Verify key interactive controls have stable, unique `AutomationProperties.AutomationId` values (especially controls used by UI automation tests). +- [ ] Switch to Windows High Contrast theme and verify readability. +- [ ] Run Narrator and verify all controls are announced correctly. +- [ ] Run **Accessibility Insights for Windows** on the app. + +## Must Read & Research + +> **Agent Rule:** Before any accessibility-related change, you **must** fetch and review these references using `fetch_webpage`. Apply what you learn. + +| # | Reference | When to consult | +|---|---|---| +| 1 | [Accessibility in WinUI](https://learn.microsoft.com/en-us/windows/apps/design/accessibility/accessibility) | Any UI change — verify accessibility approach | +| 2 | [AutomationProperties](https://learn.microsoft.com/en-us/windows/apps/design/accessibility/basic-accessibility-information) | Adding or modifying interactive controls | +| 3 | [Accessibility Insights](https://accessibilityinsights.io/docs/windows/overview/) | Testing tool — run before finalizing UI changes | +| 4 | [Keyboard Accessibility](https://learn.microsoft.com/en-us/windows/apps/design/accessibility/keyboard-accessibility) | Adding navigation, focus management, or shortcut keys | +| 5 | [High Contrast Themes](https://learn.microsoft.com/en-us/windows/apps/design/accessibility/high-contrast-themes) | Adding custom styles, colours, or theme resources | diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/code-quality.instructions.md b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/code-quality.instructions.md new file mode 100644 index 0000000000..6ccfa036e8 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/code-quality.instructions.md @@ -0,0 +1,168 @@ +--- +description: 'Static analysis, StyleCop, EditorConfig, naming conventions, and code cleanup rules' +applyTo: '**/*.cs, **/*.editorconfig, **/stylecop.json' +--- + +# Code Quality — Static Analysis, StyleCop & Code Cleanup + +Maintain strict code quality through automated analysis and consistent style enforcement. + +--- + +## 1. Static Analysis (Roslyn Analyzers) + +### Required Analyzer Packages + +Add these to the `.csproj` if not already present: + +```xml + + + + + all + runtime; build; native; contentfiles; analyzers + + +``` + +### Analysis Configuration in `.csproj` + +```xml + + true + latest-recommended + true + false + enable + +``` + +### Rule Enforcement + +Follow **all** CA* (quality) and IDE* (code style) analyzer rules at their configured severity. Do not cherry-pick — obey every warning the analyzers report. When encountering a specific rule violation, fetch the corresponding documentation from the **Must Read & Research** references below to understand and apply the correct fix. + +### `.editorconfig` + +The project's `.editorconfig` in the solution root is the source of truth for code style. Obey all rules defined there. When creating or modifying `.editorconfig`, fetch the [EditorConfig Reference](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options) for the full list of available settings. + +Key project conventions enforced via `.editorconfig`: +- Private fields use `_camelCase` prefix (SA1101 suppressed, SA1309 suppressed). +- File-scoped namespaces are required. +- `this.` qualification is not used. + +--- + +## 2. StyleCop Rules + +### StyleCop Configuration (`stylecop.json`) + +Place this file in the project root alongside the `.csproj`: + +```json +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "YourProjectName", + "copyrightText": "Copyright (c) {companyName}. All rights reserved.", + "xmlHeader": false, + "documentInterfaces": true, + "documentExposedElements": true, + "documentInternalElements": false, + "documentPrivateElements": false, + "documentPrivateFields": false + }, + "orderingRules": { + "usingDirectivesPlacement": "outsideNamespace", + "systemUsingDirectivesFirst": true + }, + "layoutRules": { + "newlineAtEndOfFile": "require" + }, + "namingRules": { + "allowCommonHungarianPrefixes": false + } + } +} +``` + +### Rule Enforcement + +Follow **all** SA* (StyleCop) rules at their configured severity. When encountering a specific SA* violation, fetch the [StyleCop Rules Reference](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/DOCUMENTATION.md) to understand the rule and apply the correct fix. Do not suppress rules without justification in a code comment. + +--- + +## 3. Code Cleanup Rules (Always Enforced) + +### After Every Edit + +1. **Remove unused `using` statements** — No unused imports should remain. +2. **Remove commented-out code** — Version control tracks history; dead code is noise. +3. **Remove unused variables and fields** — If it's declared but never read, delete it. +4. **Remove empty methods** — If an event handler or override does nothing, remove it. +5. **Simplify code** — Apply IDE suggestions (IDE0001–IDE0090) for: + - Removing unnecessary casts + - Simplifying `default` expressions + - Using pattern matching + - Using null-coalescing operators + +### Naming Conventions + +| Element | Convention | Example | +|---|---|---| +| Class / Struct | PascalCase | `MainViewModel` | +| Interface | I + PascalCase | `INavigationService` | +| Public method | PascalCase | `LoadDataAsync()` | +| Private method | PascalCase | `ValidateInput()` | +| Public property | PascalCase | `CurrentPage` | +| Private field | _camelCase | `_settingsService` | +| Parameter | camelCase | `userName` | +| Local variable | camelCase | `itemCount` | +| Constant | PascalCase | `MaxRetryCount` | +| Async method | Suffix `Async` | `FetchDataAsync()` | +| Boolean | Prefix `Is/Has/Can` | `IsLoading`, `HasAccess` | + +### File Organization + +Each `.cs` file should follow this order: + +1. `using` directives (System first, then others, alphabetically) +2. Namespace declaration (file-scoped) +3. Class/struct/interface declaration +4. Inside the type: + 1. Constants + 2. Static fields + 3. Instance fields + 4. Constructors + 5. Properties + 6. Public methods + 7. Private/internal methods + 8. Event handlers + 9. Nested types + +--- + +## Validation + +- Build & register the MSIX package — see **Build, Run & Deploy** in `Agents.md`. +- Fix **all** warnings — do not suppress without justification in a code comment. +- Verify no unused `using` statements remain after every edit. +- Verify no commented-out code remains. +- Confirm naming conventions match the table above for every new symbol. + +--- + +## Must Read & Research + +> **Agent Rule:** Before configuring analyzers, fixing warnings, or adjusting code style, you **must** fetch and review the relevant references below using `fetch_webpage`. Apply what you learn — do not skip this step. + +| # | Reference | When to consult | +|---|---|---| +| 1 | [.NET Code Analysis Overview](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview) | Setting up or modifying analyzer configuration | +| 2 | [Code Style Rules (IDE0001–IDE0090)](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/) | Resolving IDE* warnings or adjusting `.editorconfig` | +| 3 | [Quality Rules (CA*)](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/) | Resolving CA* warnings or suppressing with justification | +| 4 | [StyleCop.Analyzers GitHub](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) | Adding/updating StyleCop package or configuration | +| 5 | [StyleCop Rules Reference](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/DOCUMENTATION.md) | Understanding specific SA* rule violations | +| 6 | [EditorConfig Reference](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options) | Modifying `.editorconfig` style or severity settings | +| 7 | [.NET Naming Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names) | Verifying naming patterns for types, members, parameters | diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/design-principles.instructions.md b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/design-principles.instructions.md new file mode 100644 index 0000000000..fe296070a7 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/design-principles.instructions.md @@ -0,0 +1,132 @@ +--- +description: 'Design principles (DRY, KISS, SOLID, YAGNI) enforced in every code change' +applyTo: '**/*.cs, **/*.xaml' +--- + +# Design Principles + +Apply these principles in **every change** you make to this codebase. When in doubt, favour simplicity and clarity over cleverness. + +--- + +## 1. DRY — Don't Repeat Yourself + +> *"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."* + +### Rules +- Before writing new code, **search** the codebase for existing implementations that solve the same problem. +- Extract shared logic into helper methods, base classes, or services. +- If you find duplicated code during a task, **refactor it** as part of the same change. +- Prefer generic/reusable components over copy-paste variations. + +### Anti-patterns to avoid +- Copy-pasting code between classes/files instead of extracting a shared method. +- Creating multiple converters/helpers that do the same thing. +- Duplicating validation logic across ViewModel and Model layers. + +--- + +## 2. KISS — Keep It Simple, Stupid + +> *"Simplicity is the ultimate sophistication."* + +### Rules +- Choose the **simplest approach** that meets the requirement. +- Avoid unnecessary abstractions, inheritance hierarchies, or patterns that add complexity without clear benefit. +- Write code that **reads like plain English** — favour descriptive names over comments. +- If a method is longer than ~30 lines, consider splitting it. +- If a class does more than one thing, split it (see SRP below). + +### Anti-patterns to avoid +- Over-engineering with factories/builders/strategies for simple object creation. +- Creating deep inheritance trees when composition would suffice. +- Using complex LINQ chains when a simple `foreach` is clearer. + +--- + +## 3. SOLID Principles + +### 3.1 SRP — Single Responsibility Principle +> *"A class should have only one reason to change."* + +- Each class/file should have **one clear responsibility**. +- ViewModels handle UI state & commands. Services handle business logic. Models hold data. +- If a class name contains "And" or "Manager", it likely violates SRP. + +### 3.2 OCP — Open/Closed Principle +> *"Software entities should be open for extension, but closed for modification."* + +- Use interfaces and abstract classes so behaviour can be extended without modifying existing code. +- Prefer adding new implementations over modifying existing ones. +- Use dependency injection to swap implementations. + +### 3.3 LSP — Liskov Substitution Principle +> *"Objects of a superclass should be replaceable with objects of its subclasses without altering correctness."* + +- Derived classes must honour the contracts of their base classes. +- Never throw `NotImplementedException` in overridden methods — if a subclass can't fulfil the contract, the inheritance is wrong. + +### 3.4 ISP — Interface Segregation Principle +> *"No client should be forced to depend on methods it does not use."* + +- Keep interfaces small and focused. +- Prefer multiple small interfaces over one large one. +- Example: `INavigationService`, `IDialogService`, `ISettingsService` — not `IAppService`. + +### 3.5 DIP — Dependency Inversion Principle +> *"Depend upon abstractions, not concretions."* + +- High-level modules must not depend on low-level modules. Both should depend on abstractions. +- Use constructor injection for dependencies. +- Register services in a DI container (e.g., `Microsoft.Extensions.DependencyInjection`). + +--- + +## 4. YAGNI — You Aren't Gonna Need It + +> *"Don't add functionality until it is necessary."* + +### Rules +- Only implement what is **explicitly requested** or **clearly needed right now**. +- Do not add "just in case" parameters, methods, or abstractions. +- If you're unsure whether something is needed, **leave it out** — it can always be added later. + +### Anti-patterns to avoid +- Adding unused interface methods "for future use". +- Building a generic framework when a single concrete class suffices. +- Creating configuration options nobody has asked for. + +--- + +## Quick Reference Checklist + +Before submitting any code change, verify: + +- [ ] No duplicated code exists (DRY) +- [ ] The solution is as simple as possible (KISS) +- [ ] Each class has one responsibility (SRP) +- [ ] New behaviour is added via extension, not modification (OCP) +- [ ] Derived types can substitute their base types (LSP) +- [ ] Interfaces are small and focused (ISP) +- [ ] Dependencies point toward abstractions (DIP) +- [ ] No speculative features were added (YAGNI) + +## Validation + +- Review every new/changed class for SRP violations — ask "does this class have more than one reason to change?" +- Search the codebase for duplicate logic before adding new helpers: `grep_search` for similar method names or patterns. +- Verify no speculative code was added — every line must trace back to the original request. +- Build & register the MSIX package — see **Build, Run & Deploy** in `Agents.md`. + +--- + +## Must Read & Research + +> **Agent Rule:** Before making any code change related to design principles, you **must** fetch and review the relevant references below using `fetch_webpage`. Apply what you learn — do not skip this step. + +| # | Reference | When to consult | +|---|---|---| +| 1 | [SOLID Principles in C# — Microsoft Learn](https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/may/csharp-best-practices-dangers-of-violating-solid-principles-in-csharp) | Adding/refactoring classes, interfaces, or inheritance | +| 2 | [.NET Design Guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/) | Designing public APIs, naming, type design | +| 3 | [Framework Design Guidelines (Book)](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/) | Deep-dive on member design, exception patterns, collections | +| 4 | [Clean Code Summary](https://gist.github.com/wojteklu/73c6914cc446146b8b533c0988cf8d29) | Code readability, function size, naming clarity | diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/globalization.instructions.md b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/globalization.instructions.md new file mode 100644 index 0000000000..34314afd39 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/globalization.instructions.md @@ -0,0 +1,53 @@ +--- +description: 'Globalization & Localization requirements for user-facing strings, resource files, and culture-aware formatting' +applyTo: '**/*.cs, **/*.xaml, **/*.resw' +--- + +# Globalization & Localization + +These rules apply to **every feature and change** involving user-facing text. They are not optional add-ons. + +--- + +## Rules + +- **All user-facing strings** (UI text, error messages, tooltips) must come from `.resw` resource files — never hard-code them in XAML or C#. +- Resource file location: `Strings/en-us/Resources.resw` (default locale). +- Use `x:Uid` in XAML to bind controls to resource keys: + ```xml + + ``` + With a matching `.resw` entry: `WelcomeMessage.Text` = "Welcome!" +- In code-behind / ViewModels, use the `ResourceLoader`: + ```csharp + var loader = new Microsoft.Windows.ApplicationModel.Resources.ResourceLoader(); + string message = loader.GetString("ErrorFileNotFound"); + ``` +- **Format dates, numbers, and currencies** using `CultureInfo.CurrentCulture` or `DateTimeFormatter` — never assume a specific regional format. +- Avoid concatenating translated strings — use format placeholders (`{0}`, `{1}`). +- Design UI layouts to accommodate text expansion (~30-40% longer for German vs. English). + +## Anti-patterns + +- Hard-coded strings in `.xaml` or `.cs` files (e.g., `Content="Save"`). +- Using `string.Format` with hard-coded ordinal assumptions. +- Fixed-width UI elements that clip translated text. + +## Validation + +- Build & register the MSIX package — see **Build, Run & Deploy** in `Agents.md`. +- Check for hard-coded strings: search `Content="` and `Text="` in `.xaml` files — replace with `x:Uid`. + +### Verification Checklist + +- [ ] All user-facing strings are in `.resw` resource files + +## Must Read & Research + +> **Agent Rule:** Before any localization-related change, you **must** fetch and review these references using `fetch_webpage`. Apply what you learn. + +| # | Reference | When to consult | +|---|---|---| +| 1 | [Globalize your WinUI app](https://learn.microsoft.com/en-us/windows/apps/design/globalizing/guidelines-and-checklist-for-globalizing-your-app) | Adding any new user-facing strings or culture-aware formatting | +| 2 | [Resource Management System](https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/mrtcore/localize-strings) | Setting up or modifying `.resw` files and `ResourceLoader` usage | +| 3 | [WinUI Localization with x:Uid](https://learn.microsoft.com/en-us/windows/apps/develop/ui-input/localizing-strings) | Binding XAML controls to localized resources via `x:Uid` | diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/performance.instructions.md b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/performance.instructions.md new file mode 100644 index 0000000000..6c93e2369a --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/performance.instructions.md @@ -0,0 +1,51 @@ +--- +description: 'Performance requirements for data binding, layout, threading, and collection virtualization' +applyTo: '**/*.cs, **/*.xaml' +--- + +# Performance + +These rules apply to **every feature and change**. They are not optional add-ons. + +--- + +## Rules + +- **Use `x:Bind`** (compiled bindings) instead of `{Binding}` — it's faster and type-safe. +- Use **`x:Load`** (or `x:DeferLoadStrategy`) to defer loading of UI elements not immediately visible. +- Avoid heavy work on the UI thread — use `Task.Run` for CPU-bound work and `async/await` for I/O. +- Use **virtualizing panels** (`ItemsRepeater` with `StackLayout`, or `ListView`) for long lists — never use `StackPanel` with hundreds of items. +- **Cache** expensive computations and HTTP responses when appropriate. +- Minimize XAML visual tree depth — deep nesting hurts layout performance. +- Use **incremental loading** (`ISupportIncrementalLoading`) for large data sets. +- Profile with **Visual Studio Diagnostics Tools** and **PerfView** before and after optimizations. +- Be cautious with `DispatcherQueue.TryEnqueue` — don't flood the dispatcher queue. + +## Anti-patterns + +- Blocking the UI thread with `.Result` or `.GetAwaiter().GetResult()`. +- Loading all data upfront when only a subset is needed. +- Creating new `HttpClient` instances per request (use `IHttpClientFactory`). +- Using `FindName()` or `VisualTreeHelper` in tight loops. + +## Validation + +- Build & register the MSIX package — see **Build, Run & Deploy** in `Agents.md`. + +### Verification Checklist + +- [ ] No blocking calls on the UI thread +- [ ] `x:Bind` is used instead of `{Binding}` +- [ ] Large lists use virtualization + +## Must Read & Research + +> **Agent Rule:** Before any performance-sensitive change (data binding, layout, collections, async), you **must** fetch and review these references using `fetch_webpage`. Apply what you learn. + +| # | Reference | When to consult | +|---|---|---| +| 1 | [Performance best practices for WinUI 3](https://learn.microsoft.com/en-us/windows/apps/performance/) | Any change touching UI rendering, data loading, or threading | +| 2 | [x:Bind markup extension](https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension) | Adding or modifying XAML data bindings | +| 3 | [x:Load attribute](https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-load-attribute) | Deferring UI element loading | +| 4 | [Optimize XAML layout](https://learn.microsoft.com/en-us/windows/apps/performance/optimize-xaml-layout) | Restructuring XAML panels, reducing visual tree depth | +| 5 | [ListView optimization](https://learn.microsoft.com/en-us/windows/apps/performance/optimize-listview) | Working with lists, collections, or `ItemsRepeater` | diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/security.instructions.md b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/security.instructions.md new file mode 100644 index 0000000000..cf4461a774 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/security.instructions.md @@ -0,0 +1,48 @@ +--- +description: 'Security requirements for secrets management, input validation, permissions, and secure coding' +applyTo: '**/*.cs, **/*.appxmanifest' +--- + +# Security + +These rules apply to **every feature and change**. They are not optional add-ons. + +--- + +## Rules + +- **Never hard-code secrets** (API keys, passwords, connection strings) — use environment variables, Windows Credential Manager, or Azure Key Vault. +- Validate and sanitize **all external input** (user input, file content, network responses). +- Use `SecureString` or `PasswordVault` for sensitive data in memory when practical. +- Follow the **principle of least privilege** — request only the permissions the app actually needs in `Package.appxmanifest`. +- Keep NuGet packages up to date — run `dotnet list package --outdated` regularly. +- Enable **code signing** for published MSIX packages. +- When using `HttpClient`, always validate TLS certificates and use HTTPS. +- Never log sensitive data (PII, tokens, passwords). + +## Anti-patterns + +- Storing secrets in `appsettings.json` committed to source control. +- Disabling TLS validation for debugging and forgetting to re-enable it. +- Using `Process.Start` with unsanitized user input. +- Broad `try { } catch (Exception) { }` that swallows errors silently without any logging. + +## Validation + +- Build & register the MSIX package — see **Build, Run & Deploy** in `Agents.md`. +- Check for hard-coded secrets: search for `password`, `apikey`, `secret`, `connectionstring` in `.cs` files. + +### Verification Checklist + +- [ ] No secrets are hard-coded + +## Must Read & Research + +> **Agent Rule:** Before any security-related change (auth, input handling, permissions, HTTP), you **must** fetch and review these references using `fetch_webpage`. Apply what you learn. + +| # | Reference | When to consult | +|---|---|---| +| 1 | [.NET Security Best Practices](https://learn.microsoft.com/en-us/dotnet/standard/security/) | Any code handling credentials, tokens, or sensitive data | +| 2 | [Secure coding guidelines for .NET](https://learn.microsoft.com/en-us/dotnet/standard/security/secure-coding-guidelines) | Input validation, exception handling, type safety | +| 3 | [MSIX Security](https://learn.microsoft.com/en-us/windows/msix/msix-container) | Packaging, signing, or distribution changes | +| 4 | [Package.appxmanifest capabilities](https://learn.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations) | Adding or modifying app capabilities/permissions | diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/testing.instructions.md b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/testing.instructions.md new file mode 100644 index 0000000000..1878872942 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/testing.instructions.md @@ -0,0 +1,276 @@ +--- +description: 'Unit testing standards, test project setup, naming, build & run commands' +applyTo: '**/*Tests.cs, **/*Test.cs, **/*.Tests.csproj' +--- + +# Testing — Unit Tests, Build & Run + +Every public method and class must have corresponding unit tests. Tests are not optional. + +--- + +## 1. Test Framework & Project Setup + +### Recommended Stack + +| Component | Package | Purpose | +|---|---|---| +| Test Framework | `MSTest` | Test runner & assertions | +| Mocking | `Moq` | Mock dependencies | +| UI Testing | `Microsoft.Windows.Apps.Test` | WinUI UI automation (optional) | + +### Test Project Setup + +Create a test project alongside the main project: + +``` +/ + / ← Main app project + .Tests/ ← Unit test project +``` + +Test project `.csproj` should reference the main project: + +```xml + + + + + true + enable + false + + + + + + + + + + + + + + +``` + +--- + +## 2. Test Writing Rules + +### What to Test +- **All public methods** in ViewModels, Services, Helpers, and Models. +- **Edge cases** — null inputs, empty collections, boundary values. +- **Error paths** — exception handling, invalid state transitions. +- **Business logic** — calculations, transformations, state management. + +### What NOT to Test (Directly) +- XAML layout / visual rendering (use UI tests for that). +- Framework internals (e.g., `InitializeComponent()`). +- Private methods — test them indirectly through public methods. + +### Test Naming Convention + +Use the pattern: `MethodName_Scenario_ExpectedResult` + +```csharp +[TestMethod] +public void CalculateTotal_WithEmptyCart_ReturnsZero() { } + +[TestMethod] +public void LoadDataAsync_WhenServiceThrows_SetsErrorState() { } + +[TestMethod] +public async Task SaveAsync_WithValidInput_ReturnsTrue() { } +``` + +### Test Structure (AAA Pattern) + +Every test follows **Arrange → Act → Assert**: + +```csharp +[TestMethod] +public void Add_TwoPositiveNumbers_ReturnsSum() +{ + // Arrange + var calculator = new Calculator(); + + // Act + int result = calculator.Add(2, 3); + + // Assert + Assert.AreEqual(5, result); +} +``` + +### ViewModel Testing Example + +```csharp +[TestMethod] +public async Task LoadItemsAsync_OnSuccess_PopulatesItems() +{ + // Arrange + var mockService = new Mock(); + mockService + .Setup(s => s.GetItemsAsync()) + .ReturnsAsync(new List { new("Test") }); + + var viewModel = new MainViewModel(mockService.Object); + + // Act + await viewModel.LoadItemsAsync(); + + // Assert + Assert.AreEqual(1, viewModel.Items.Count); + Assert.IsFalse(viewModel.IsLoading); +} +``` + +--- + +## 3. Test Organization + +### File Structure + +Mirror the main project's folder structure (defined in [winui-best-practices](winui-best-practices.instructions.md)) in the test project. As the main project grows with subfolders under `ViewModels/`, `Services/`, `Views/`, etc., the test project must grow organically in the same way. This alignment enables on-demand test runs scoped to the area you changed: + +``` +/ .Tests/ + Models/ Models/ + User.cs UserTests.cs + ViewModels/ ViewModels/ + MainViewModelTests.cs MainViewModelTests.cs + Settings/ Settings/ + ThemeViewModel.cs ThemeViewModelTests.cs + Services/ Services/ + DataService.cs DataServiceTests.cs + Auth/ Auth/ + AuthService.cs AuthServiceTests.cs + Helpers/ Helpers/ + StringHelper.cs StringHelperTests.cs + Converters/ Converters/ + BoolToVisibilityConverter.cs BoolToVisibilityConverterTests.cs +``` + +### One Test Class per Class Under Test + +```csharp +namespace .Tests.ViewModels; + +[TestClass] +public class MainViewModelTests +{ + // All tests for MainViewModel go here +} +``` + +### Running Tests On-Demand + +After a change, run only the tests related to the affected area instead of the full suite: + +```powershell +# Run from the test project folder +cd .Tests + +# Run tests for a specific class +dotnet test -c Debug -p:Platform=x64 --filter "FullyQualifiedName~MainViewModelTests" + +# Run a single test +dotnet test -c Debug -p:Platform=x64 --filter "FullyQualifiedName~MainViewModelTests.LoadItemsAsync_OnSuccess_PopulatesItems" + +# Run all tests in a namespace (e.g., all ViewModel tests) +dotnet test -c Debug -p:Platform=x64 --filter "FullyQualifiedName~Tests.ViewModels" + +# Run tests in a subfolder namespace (e.g., only Settings ViewModels) +dotnet test -c Debug -p:Platform=x64 --filter "FullyQualifiedName~Tests.ViewModels.Settings" + +# Run the full suite (for cross-cutting changes) +dotnet test -c Debug -p:Platform=x64 +``` +``` + +--- + +## 4. Test-Specific Commands + +For general build and register commands, see **Build, Run & Deploy** in `Agents.md`. +For on-demand test filtering, see **Running Tests On-Demand** above. + +Below are additional test commands: + +### Build Only the Test Project + +```powershell +cd .Tests +dotnet build -c Debug -p:Platform=x64 +``` + +### Run Tests with Verbose Output + +```powershell +dotnet test -c Debug -p:Platform=x64 --verbosity normal +``` + +--- + +## 5. Agent Workflow for Tests + +When you write or modify code, follow this sequence: + +1. **Implement the feature or fix** in the main project. +2. **Write unit tests** for every new/changed public method. +3. **Build** — see **Build, Run & Deploy** in `Agents.md`. Fix all errors and warnings. +4. **Run tests** — `dotnet test -c Debug -p:Platform=x64` (from the test project folder) and ensure all pass. +5. **Review** — Confirm tests cover the happy path, edge cases, and error cases. + +### When Modifying Existing Code + +1. **Run existing tests first** to establish a baseline. +2. Make the code change. +3. **Run tests again** — fix any failures. +4. **Add new tests** if the change introduces new behaviour. + +### Coverage Goals + +- Aim for **80%+ code coverage** on business logic (ViewModels, Services). +- 100% coverage of utility/helper methods. +- UI code-behind is exempt from unit test coverage (tested via integration/UI tests). + +--- + +## 6. Common Test Pitfalls + +| Pitfall | Fix | +|---|---| +| Test depends on another test's state | Each test must be fully independent | +| Testing multiple things in one test | One assertion per logical concept | +| Tests pass but don't actually verify anything | Always have meaningful assertions | +| Mocking too much | Mock only external dependencies, not the class under test | +| Testing implementation details | Test behaviour and outcomes, not internal method calls | +| Async tests without `await` | Always `await` async methods and use `async Task` return type | + +--- + +## Validation + +- Build & run tests — see **Build, Run & Deploy** in `Agents.md`. +- Verify all tests pass — zero failures, zero skipped without justification. +- Verify naming follows `MethodName_Scenario_ExpectedResult` pattern. +- Verify AAA structure (Arrange/Act/Assert) in every test method. +- Confirm coverage goals: 80%+ on ViewModels/Services, 100% on helpers. + +--- + +## Must Read & Research + +> **Agent Rule:** Before writing or modifying tests, you **must** fetch and review the relevant references below using `fetch_webpage`. Apply what you learn — do not skip this step. + +| # | Reference | When to consult | +|---|---|---| +| 1 | [Unit testing C# with MSTest](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest) | Setting up test project, writing first tests, MSTest attributes | +| 2 | [Unit testing best practices .NET](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices) | Every time you write tests — naming, structure, AAA pattern | +| 3 | [Moq Quickstart](https://github.com/devlooped/moq/wiki/Quickstart) | Mocking interfaces, setting up `Returns`/`Throws`, verifying calls | +| 4 | [FluentAssertions Documentation](https://fluentassertions.com/introduction) | Writing expressive assertions (`Should().Be()`, collections, exceptions) | +| 5 | [dotnet test CLI](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test) | Running tests from terminal, filtering, verbosity options | +| 6 | [Test Explorer in Visual Studio](https://learn.microsoft.com/en-us/visualstudio/test/run-unit-tests-with-test-explorer) | Debugging tests, viewing coverage, understanding test output | diff --git a/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/winui-best-practices.instructions.md b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/winui-best-practices.instructions.md new file mode 100644 index 0000000000..9a17a96268 --- /dev/null +++ b/dev/VSIX/DotnetNewTemplates/templates/single-project/instructions/winui-best-practices.instructions.md @@ -0,0 +1,404 @@ +--- +description: 'WinUI 3 / WinAppSDK architecture, MVVM, XAML patterns, DI, theming, and controls guidance' +applyTo: '**/*.cs, **/*.xaml, **/*.csproj' +--- + +# WinUI 3 / WinAppSDK — Best Practices & Patterns + +This file covers WinUI 3-specific patterns, conventions, and architecture guidance for this project. + +--- + +## 1. Architecture — MVVM Pattern + +### Overview + +Use **Model-View-ViewModel (MVVM)** for all UI features: + +| Layer | Responsibility | Example | +|---|---|---| +| **Model** | Data structures & business entities | `Item.cs`, `UserProfile.cs` | +| **View** | XAML UI — layout, styles, animations | `MainPage.xaml` | +| **ViewModel** | UI state, commands, data transformation | `MainViewModel.cs` | +| **Service** | Business logic, data access, navigation | `IDataService.cs`, `NavigationService.cs` | + +### Project Folder Structure + +``` +/ + Models/ ← Data classes + ViewModels/ ← ViewModels (one per page/dialog) + Views/ ← XAML pages and windows + Services/ ← Business logic & platform services + Converters/ ← IValueConverter implementations + Helpers/ ← Static utility methods + Controls/ ← Custom/reusable controls + Strings/ + en-us/ + Resources.resw + Assets/ ← Images, icons, splash screens +``` + +### ViewModel Base + +Use `CommunityToolkit.Mvvm` (recommended) for boilerplate-free ViewModels: + +```xml + + +``` + +```csharp +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +public partial class MainViewModel : ObservableObject +{ + [ObservableProperty] + private string _title = string.Empty; + + [ObservableProperty] + private bool _isLoading; + + [RelayCommand] + private async Task LoadDataAsync() + { + IsLoading = true; + try + { + // Load data + } + finally + { + IsLoading = false; + } + } +} +``` + +### View-ViewModel Binding + +```xml + + + + + + + + +