Background:
WPF's theme assemblies (PresentationFramework.Aero, PresentationFramework.Aero2, PresentationFramework.AeroLite, PresentationFramework.Classic, PresentationFramework.Luna and PresentationFramework.Royale) expose the same types.
For e.g., DataGridHeaderBorder.
These assemblies can be used in two ways.
Typically, they are used as resource assemblies. i.e., they are not referenced from the project, and instead, XAML resources within them are consumed as resources using <ResourceDictionary ..>. When used this way, all of these assemblies can (and often are) used in to supply themes contextually (i.e., depending on the system theme, for e.g., or some other user preference).
Less commonly, they are directly referenced and their types are either consumed in XAML or directly in code. When used this way, only one assembly can be referenced directly. If more than one assembly needs to be referenced, namespace aliasing has to be used.
Problem:
The current SDK/FrameworkReference design in .NET Core _always_ enables references to each of these assemblies. This makes them virtually unusable. We need a better scheme/solution.
Related:
Note:
Part of the solution lies in the SDK, and part of the solution would likely have to be implemented in Microsoft.NET.Sdk.WindowsDesktop targets.
/cc @miguep, @rladuca
This is a blocker for me too!
Can we use the exisiting PackageReference for the Theme assemblies instead of FrameworkReference?
@Nirmal4G, Can you outline your exact scenario?
Specifically, what are you trying to do, and what scenario related to PresentationFramework.* theme assemblies is failing in .NET Core that used to work in .NET Framework?
This is not a bug in WPF itself, and the solution will come from https://github.com/dotnet/sdk/issues/3265.
Removing label:bug and marking it as lable:tracking_external_issue
We don't expect to fix this in the SDK in 3.1 timeframe. We are suggesting the following target as a workaround for accessing WPF's theme assembly types from C# projects.
We will revisit this in .NET 5 if there is sufficient interest.
<!--
Usage:
To reference a specific WPF theme assembly, set one or more of these properties to true.
If no property is set to true, then all theme assemblies are referenced (default).
Setting one property to 'true' implies that other properties are 'false' by default (unless they are
explicitly set to true).
<PropertyGroup>
<ReferenceWpfAeroTheme>true</ReferenceWpfAeroTheme>
<ReferenceWpfAero2Theme>true</ReferenceWpfAero2Theme>
<ReferenceWpfAeroLiteTheme>true</ReferenceWpfAeroLiteTheme>
<ReferenceWpfClassicTheme>true</ReferenceWpfClassicTheme>
<ReferenceWpfLunaTheme>true</ReferenceWpfLunaTheme>
<ReferenceWpfRoyaleTheme>true</ReferenceWpfRoyaleTheme>
</PropertyGroup>
Advanced Usage:
To reference more than one theme assembly at the same time, use an 'alias'
See 'Aliases' in the documentation for [MSBuild Reference element](https://docs.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items?view=vs-2019)
and the [C# compiler commandline documentation for -reference](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/reference-compiler-option)
This only works in C#.
<PropertyGroup>
<WpfAeroThemeAliases>namespace</WpfAeroThemeAliases>
<WpfAero2ThemeAliases>namespace</WpfAero2ThemeAliases>
<WpfAeroLiteThemeAliases>nameaspace</WpfAeroLiteThemeAliases>
<WpfClassicThemeAliases>namespace</WpfClassicThemeAliases>
<WpfLunaThemeAliases>namespace</WpfLunaThemeAliases>
<WpfRoyaleThemeAliases>namespace</WpfRoyaleThemeAliases>
</PropertyGroup>
-->
<Target Name="SelectWpfThemeAssembly"
AfterTargets="ResolveAssemblyReferences"
Condition="'$(ReferenceWpfAeroTheme)' == 'true' Or
'$(ReferenceWpfAero2Theme)' == 'true' Or
'$(ReferenceWpfAeroLiteTheme)' == 'true' Or
'$(ReferenceWpfClassicTheme)' == 'true' Or
'$(ReferenceWpfLunaTheme)' == 'true' Or
'$(ReferenceWpfRoyaleTheme)' == 'true'">
<ItemGroup>
<_WpfThemeAssemblies Include="@(ReferencePath)"
Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' And
'%(ReferencePath.FileName)'=='PresentationFramework.Aero'">
<Aliases Condition="'$(WpfAeroThemeAliases)'!=''">$(WpfAeroThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)"
Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' And
'%(ReferencePath.FileName)'=='PresentationFramework.Aero2'">
<Aliases Condition="'$(WpfAero2ThemeAliases)'!=''">$(WpfAero2ThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)"
Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' And
'%(ReferencePath.FileName)'=='PresentationFramework.AeroLite'">
<Aliases Condition="'$(WpfAeroLiteThemeAliases)'!=''">$(WpfAeroLiteThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)"
Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' And
'%(ReferencePath.FileName)'=='PresentationFramework.Classic'">
<Aliases Condition="'$(WpfClassicThemeAliases)'!=''">$(WpfClassicThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)"
Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' And
'%(ReferencePath.FileName)'=='PresentationFramework.Luna'">
<Aliases Condition="'$(WpfLunaThemeAliases)'!=''">$(WpfLunaThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)"
Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' And
'%(ReferencePath.FileName)'=='PresentationFramework.Royale'">
<Aliases Condition="'$(WpfRoyaleThemeAliases)'!=''">$(WpfRoyaleThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<ReferencePath Remove="@(_WpfThemeAssemblies)" />
<ReferencePath Include="@(_WpfThemeAssemblies)"
Condition="('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero' And '$(ReferenceWpfAeroTheme)'=='true') Or
('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero2' And '$(ReferenceWpfAero2Theme)'=='true') Or
('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.AeroLite' And '$(ReferenceWpfAeroLiteTheme)'=='true') Or
('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Classic' And '$(ReferenceWpfClassicTheme)'=='true') Or
('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Luna' And '$(ReferenceWpfLunaTheme)'=='true') Or
('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Royale' And '$(ReferenceWpfRoyaleTheme)'=='true')" />
</ItemGroup>
</Target>
We will revisit this in .NET 5 if there is sufficient interest.
What do you mean by sufficient interest?
What do you mean by sufficient interest?
Please continue posting here about your scenarios/use-cases and migration problems.
We currently think that almost all applications should be able to workaround the current SDK design to access theme assembly types. If the feedback here tells us otherwise, we will work with the SDK team to re-think our approach.
OK. Will do.
We have some tools that uses theming. I'll try to migrate them, and see what works.
Thanks so much.
I have this problem in a control library. I use ButtonChrome and DataGridHeaderBorder to create aero-like controls, which means direct-referencing a theme assembly. The target-based workaround works for me when targetting netcoreapp3.1, but not when my TargetFramework is net472.
targetting netcoreapp3.1, but not when my TargetFramework is net472
You don't need this workaround for net472 - references for .NET Framework are always obtained using Reference items. If you are using SDK style projects, we just pre-populate some standard WPF and WinForms references, but you have to add additional references on your own in the project file. And if you are multi-targeting both netcoreapp3.1 and net472, then you have to take care to condition those additional Reference items for just .NET Framework Builds.
This is how we do it for common references:
i see, so when using the SDK to build for netfx it's using the existing assemblies from .net framework rather than new copies delivered with the sdk?
i see, so when using the SDK to build for netfx it's using the existing assemblies from .net framework rather than new copies delivered with the sdk?
Correct. Which means that if you target net4xy, you'll need to have .NET 4.X.Y targeting pack/developer pack installed (somehow).
The SDK that provides support for SDK style projects for WPF/WinForms is a .NET Core SDK; it supports .NET Framework targeting to enable migration (from netfx to netcore) scenarios - our _primary_ goal was not about delivering a new/improved feature (namely SDK style project support) for WPF/WinForms on .NET Framework, and so we did not re-package and ship ref-assemblies for .NET Framework along with the .NET Core 3.0/.NET Core 3.1 SDK. This is not planned for .NET 5 either.
Migration is exactly why I'm using multitargeting. Thanks for the explanation, as long as it can be made to work that's good enough (and using <Reference> conditional on TFM does work).