microsoft / microsoft/WindowsAppSDK

C++/WinRT WinUI item templates generate incorrect namespaces and generated-file paths for items in project subfolders

Open
#6,688 0 comments 0 reactions 0 assignees View on GitHub
area-WinAppSDK:Templates needs-triage
Dominant language
C++
Stars
4.7k
Forks
471
Avg merge
3d 13h
Merged PRs (30d)
28

Description

# C++/WinRT WinUI item templates generate incorrect namespaces and generated-file paths for items in project subfolders

### Describe the bug

The WinUI C++/WinRT item templates do not correctly account for the project subfolder in which an item is created.

This issue only becomes apparent when using **Add > New Item** on a folder inside the project.

For example:

```text
OpenNet
└─ UI
└─ Xaml
└─ Control
└─ Add > New Item > Templated Control
````

The user enters only the leaf item name:

```text
TaskStatusIndicator
```

This is expected behavior. The user should not need to encode the project folder or namespace into the item name.

The generated source files themselves should also remain leaf names:

```text
TaskStatusIndicator.idl
TaskStatusIndicator.h
TaskStatusIndicator.cpp
```

The selected project folder should instead be reflected in the generated namespace and in any C++/WinRT component-generated include paths.

### Current behavior

Assume:

```text
Project RootNamespace: OpenNet
Selected project folder: UI\Xaml\Control
Item name: TaskStatusIndicator
```

The WinRT type should be:

```text
OpenNet.UI.Xaml.Control.TaskStatusIndicator
```

However, the current C++ templates use `$rootnamespace$` directly as a C++ namespace and use only `$safeitemname$` for C++/WinRT generated component files.

For example, the current TemplatedControl template contains logic equivalent to:

```cpp
#include "$safeitemname$.h"

#if __has_include("$safeitemname$.g.cpp")
#include "$safeitemname$.g.cpp"
#endif

namespace winrt::$rootnamespace$::implementation
{
...
}
```

and its header contains:

```cpp
#include "$safeitemname$.g.h"

namespace winrt::$rootnamespace$::implementation
{
...
}
```

This makes two assumptions that are only accidentally valid when the item is created directly in the project root:

1. the Visual Studio item namespace can be inserted unchanged into a C++
namespace declaration;
2. the C++/WinRT generated component file is always named only after the leaf
type.

Neither assumption is valid for items created in project subfolders.

### Expected namespace mapping

For:

```text
Project RootNamespace = OpenNet
Selected folder = UI\Xaml\Control
Item name = TaskStatusIndicator
```

the different representations should be kept distinct.

#### WinRT / IDL / XAML namespace

```text
OpenNet.UI.Xaml.Control
```

For example:

```idl
namespace OpenNet.UI.Xaml.Control
{
runtimeclass TaskStatusIndicator : Microsoft.UI.Xaml.Controls.Control
{
TaskStatusIndicator();
}
}
```

#### C++ namespace

```cpp
namespace winrt::OpenNet::UI::Xaml::Control::implementation
{
}
```

#### C++/WinRT generated component filename

The generated filename is relative to the component root and also depends on the C++/WinRT prefix convention.

For a component root of `OpenNet`, the relative component name is:

```text
UI.Xaml.Control.TaskStatusIndicator
```

With the prefix convention this can produce:

```text
UI.Xaml.Control.TaskStatusIndicator.g.h
UI.Xaml.Control.TaskStatusIndicator.g.cpp
```

With the nested-directory convention it can produce:

```text
UI/Xaml/Control/TaskStatusIndicator.g.h
UI/Xaml/Control/TaskStatusIndicator.g.cpp
```

The item template should generate an include that matches the actual C++/WinRT component projection instead of assuming:

```cpp
#include "TaskStatusIndicator.g.h"
```

### Why this can also cause linker failures

The current `.cpp` template conditionally includes the generated `.g.cpp`:

```cpp
#if __has_include("$safeitemname$.g.cpp")
#include "$safeitemname$.g.cpp"
#endif
```

When a type lives in a nested namespace, the actual generated component file may include the namespace-relative prefix/path.

In that case `__has_include("TaskStatusIndicator.g.cpp")` is false and the generated forwarding definitions are silently excluded from the translation unit.

This can later surface as unresolved public-projection symbols referenced from generated XAML metadata code such as `XamlTypeInfo.g.cpp`.

### Expected behavior

The item wizard/template should derive namespace/path information from the selected project folder.

The user should still only enter:

```text
TaskStatusIndicator
```

and the generated physical source filenames should still be:

```text
TaskStatusIndicator.idl
TaskStatusIndicator.h
TaskStatusIndicator.cpp
```

The template system should independently derive values equivalent to:

```text
WinRT namespace:
OpenNet.UI.Xaml.Control

C++ namespace:
OpenNet::UI::Xaml::Control

component-relative namespace:
UI.Xaml.Control
```

rather than treating `$rootnamespace$` and `$safeitemname$` as sufficient for all three purposes.

### Important note about DefaultStyleKey

For a templated control, this expression is conceptually correct:

```cpp
DefaultStyleKey(box_value(L"$rootnamespace$.$safeitemname$"));
```

because the runtime type name is dotted:

```text
OpenNet.UI.Xaml.Control.TaskStatusIndicator
```

The issue is specifically using the same representation for C++ namespace syntax and generated component filenames.

### Affected templates

This pattern should be reviewed across the C++/WinRT item templates, including:

* Blank Window
* Blank Page
* User Control
* Templated Control

The issue is not specific to Templated Control.

### Suggested direction

A shared C++/WinRT item-template wizard could calculate separate replacement parameters for:

* WinRT namespace
* C++ namespace
* component-relative prefix/path

based on the project root namespace and the selected project folder.

The important part is that the user should not need to put the folder path in the Add New Item name field.

### Additional implementation context

There is also an important C++/WinRT implementation detail supporting this issue. The C++/WinRT generator itself does **not** assume that a generated component file is always simply:

```text
TypeName.g.h
TypeName.g.cpp
````

When C++/WinRT generates component implementation scaffolding, it uses its calculated component filename.

For example, the generator uses logic equivalent to:

```cpp
auto filename = get_generated_component_filename(type);

auto format = R"(#include "%.g.cpp"
)";

w.write(format, filename);
```

The same calculated component filename is also used when the generated component `.g.h` probes for the corresponding XAML-generated header:

```cpp
auto include_path = get_generated_component_filename(type);
```

which is then used for:

```cpp
#if defined(WINRT_FORCE_INCLUDE_..._XAML_G_H) || __has_include(".xaml.g.h")
#include ".xaml.g.h"
#endif
```

So the current WinUI item templates:

```cpp
#include "$safeitemname$.g.h"

#if __has_include("$safeitemname$.g.cpp")
#include "$safeitemname$.g.cpp"
#endif
```

are not only problematic for nested project folders; they are also inconsistent with the filename calculation used by C++/WinRT's own generated component skeletons.

For example, for:

```text
Project RootNamespace:
OpenNet

WinRT type:
OpenNet.UI.Xaml.Control.TaskStatusIndicator
```

the component-generated filename is relative to the component root. Depending on `CppWinRTUsePrefixes`, that can be represented as:

```text
UI.Xaml.Control.TaskStatusIndicator
```

or:

```text
UI/Xaml/Control/TaskStatusIndicator
```

rather than only:

```text
TaskStatusIndicator
```

This is another reason why the item template should not construct generated component filenames from `$safeitemname$` alone.

The current Templated Control item also runs:

```xml

Microsoft.VisualStudio.WinRT.TemplateWizards.TemplatedControl.Wizard

```

so the fix should probably audit both the static template files and the wizard's item/project integration rather than modifying only `TemplatedControl.cpp`.

I am intentionally keeping C++/WinRT 3.x C++20 module support out of this issue. The XAML compiler currently has a separate module-integration problem, and I will track that independently.

Related: microsoft/microsoft-ui-xaml#6583 microsoft/microsoft-ui-xaml#10889 https://developercommunity.visualstudio.com/t/WinUI-templated-control-template-generat/11101152

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the affected Blank Window, Blank Page, User Control, and Templated Control item templates, including the TemplatedControl wizard and its TemplatedControl.cpp integration. Trace how the selected project folder reaches the template replacements and compare those values with C++/WinRT's calculated component filename. Done means nested-folder items retain leaf source filenames while namespaces and generated-component includes match the selected folder and configured prefix convention.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
desktop, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.