Skip to content

Window styling and Theming API #8575

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,17 @@ CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerSerializationVi
CannotChangeAttribute : Attribute 'System.Windows.LocalizabilityAttribute' on 'System.Windows.Window' changed from '[LocalizabilityAttribute(16)]' in the contract to '[LocalizabilityAttribute(LocalizationCategory.Ignore)]' in the implementation.
CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerSerializationVisibilityAttribute' on 'System.Windows.Window.DialogResult' changed from '[DesignerSerializationVisibilityAttribute(0)]' in the contract to '[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]' in the implementation.
CannotChangeAttribute : Attribute 'System.Windows.LocalizabilityAttribute' on 'System.Windows.Window.Title' changed from '[LocalizabilityAttribute(2)]' in the contract to '[LocalizabilityAttribute(LocalizationCategory.Title)]' in the implementation.
TypesMustExist : Type 'System.Windows.WindowBackdropType' does not exist in the implementation but it does exist in the contract.
CannotChangeAttribute : Attribute 'System.Windows.LocalizabilityAttribute' on 'System.Windows.WindowStartupLocation' changed from '[LocalizabilityAttribute(0, Readability=0)]' in the contract to '[LocalizabilityAttribute(LocalizationCategory.None, Readability=Readability.Unreadable)]' in the implementation.
CannotRemoveAttribute : Attribute 'System.Xml.Serialization.XmlRootAttribute' exists on 'System.Windows.Annotations.Annotation' in the contract but not the implementation.
CannotRemoveAttribute : Attribute 'System.Xml.Serialization.XmlRootAttribute' exists on 'System.Windows.Annotations.AnnotationResource' in the contract but not the implementation.
CannotRemoveAttribute : Attribute 'System.Xml.Serialization.XmlRootAttribute' exists on 'System.Windows.Annotations.ContentLocator' in the contract but not the implementation.
CannotRemoveAttribute : Attribute 'System.Xml.Serialization.XmlRootAttribute' exists on 'System.Windows.Annotations.ContentLocatorGroup' in the contract but not the implementation.
TypesMustExist : Type 'System.Windows.Appearance.ApplicationAccentColorManager' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Windows.Appearance.ApplicationTheme' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Windows.Appearance.ApplicationThemeManager' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Windows.Appearance.SystemTheme' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Windows.Appearance.ThemeChangedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'internal System.Collections.Generic.List<System.Windows.Automation.Peers.AutomationPeer> System.Windows.Automation.Peers.ContentTextAutomationPeer.GetAutomationPeersFromRange(System.Windows.Documents.ITextPointer, System.Windows.Documents.ITextPointer)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'internal System.Collections.Generic.List<System.Windows.Automation.Peers.AutomationPeer> System.Windows.Automation.Peers.TextAutomationPeer.GetAutomationPeersFromRange(System.Windows.Documents.ITextPointer, System.Windows.Documents.ITextPointer)' does not exist in the implementation but it does exist in the contract.
CannotChangeAttribute : Attribute 'System.Windows.LocalizabilityAttribute' on 'System.Windows.Controls.AccessText.FontFamily' changed from '[LocalizabilityAttribute(14, Modifiability=0)]' in the contract to '[LocalizabilityAttribute(LocalizationCategory.Font, Modifiability=Modifiability.Unmodifiable)]' in the implementation.
Expand Down Expand Up @@ -304,4 +310,4 @@ CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerSerializationVi
CannotChangeAttribute : Attribute 'System.Windows.Markup.DesignerSerializationOptionsAttribute' on 'System.Windows.Markup.XmlAttributeProperties.GetXmlSpace(System.Windows.DependencyObject)' changed from '[DesignerSerializationOptionsAttribute(1)]' in the contract to '[DesignerSerializationOptionsAttribute(DesignerSerializationOptions.SerializeAsAttribute)]' in the implementation.
CannotChangeAttribute : Attribute 'System.ComponentModel.DesignerSerializationVisibilityAttribute' on 'System.Windows.Media.Animation.Storyboard.GetTarget(System.Windows.DependencyObject)' changed from '[DesignerSerializationVisibilityAttribute(0)]' in the contract to '[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]' in the implementation.
CannotChangeAttribute : Attribute 'System.Windows.LocalizabilityAttribute' on 'System.Windows.Shapes.Shape' changed from '[LocalizabilityAttribute(0, Readability=0)]' in the contract to '[LocalizabilityAttribute(LocalizationCategory.None, Readability=Readability.Unreadable)]' in the implementation.
Total Issues: 305
Total Issues: 311
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,24 @@
<Compile Include="MS\Win32\UxThemeWrapper.cs" />
<Compile Include="OtherAssemblyAttrs.cs" />
<Compile Include="System\ComponentModel\DesignerProperties.cs" />
<Compile Include="System\Windows\Appearance\ApplicationAccentColorManager.cs" />
<Compile Include="System\Windows\Appearance\ApplicationTheme.cs" />
<Compile Include="System\Windows\Appearance\ApplicationThemeManager.cs" />
<Compile Include="System\Windows\Appearance\ObservedWindow.cs" />
<Compile Include="System\Windows\Appearance\ResourceDictionaryManager.cs" />
<Compile Include="System\Windows\Appearance\SystemTheme.cs" />
<Compile Include="System\Windows\Appearance\SystemThemeManager.cs" />
<Compile Include="System\Windows\Appearance\SystemThemeWatcher.cs" />
<Compile Include="System\Windows\Appearance\ThemeChangedEvent.cs" />
<Compile Include="System\Windows\Appearance\WindowBackgroundManager.cs" />
<Compile Include="System\Windows\Appearance\WindowBackdrop.cs" />
<Compile Include="System\Windows\Extensions\ColorExtensions.cs" />
<Compile Include="System\Windows\Hardware\DisplayDpi.cs" />
<Compile Include="System\Windows\WindowBackdropType.cs" />
<Compile Include="System\Windows\Interop\Dwapi.cs" />
<Compile Include="System\Windows\Interop\Libraries.cs" />
<Compile Include="System\Windows\Interop\UnsafeNativeMethodsWindow.cs" />
<Compile Include="System\Windows\Interop\UnsafeReflection.cs" />
<Compile Include="System\Windows\Annotations\AnchorInfo.cs" />
<Compile Include="System\Windows\Annotations\Annotation.cs" />
<Compile Include="System\Windows\Annotations\AnnotationAuthorChangedEventArgs.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

using System.Windows.Extensions;
using System.Windows.Media;
using System.Windows.Interop;

namespace System.Windows.Appearance;

/// <summary>
/// Allows updating the accents used by controls in the application by swapping dynamic resources.
/// </summary>
/// <example>
/// <code lang="csharp">
/// ApplicationAccentColorManager.Apply(
/// Color.FromArgb(0xFF, 0xEE, 0x00, 0xBB),
/// ApplicationTheme.Dark,
/// false
/// );
/// </code>
/// <code lang="csharp">
/// ApplicationAccentColorManager.Apply(
/// ApplicationAccentColorManager.GetColorizationColor(),
/// ApplicationTheme.Dark,
/// false
/// );
/// </code>
/// </example>
public static class ApplicationAccentColorManager
{
/// <summary>
/// The maximum value of the background HSV brightness after which the text on the accent will be turned dark.
/// </summary>
private const double BackgroundBrightnessThresholdValue = 80d;

/// <summary>
/// Gets the SystemAccentColor.
/// </summary>
public static Color SystemAccent
{
get
{
var resource = Application.Current.Resources["SystemAccentColor"];

if (resource is Color color)
{
return color;
}

return Colors.Transparent;
}
}

/// <summary>
/// Gets the <see cref="Brush"/> of the SystemAccentColor.
/// </summary>
public static Brush SystemAccentBrush => new SolidColorBrush(SystemAccent);

/// <summary>
/// Gets the SystemAccentColorPrimary.
/// </summary>
public static Color PrimaryAccent
{
get
{
var resource = Application.Current.Resources["SystemAccentColorPrimary"];

if (resource is Color color)
{
return color;
}

return Colors.Transparent;
}
}

/// <summary>
/// Gets the <see cref="Brush"/> of the SystemAccentColorPrimary.
/// </summary>
public static Brush PrimaryAccentBrush => new SolidColorBrush(PrimaryAccent);

/// <summary>
/// Gets the SystemAccentColorSecondary.
/// </summary>
public static Color SecondaryAccent
{
get
{
var resource = Application.Current.Resources["SystemAccentColorSecondary"];

if (resource is Color color)
{
return color;
}

return Colors.Transparent;
}
}

/// <summary>
/// Gets the <see cref="Brush"/> of the SystemAccentColorSecondary.
/// </summary>
public static Brush SecondaryAccentBrush => new SolidColorBrush(SecondaryAccent);

/// <summary>
/// Gets the SystemAccentColorTertiary.
/// </summary>
public static Color TertiaryAccent
{
get
{
var resource = Application.Current.Resources["SystemAccentColorTertiary"];

if (resource is Color color)
{
return color;
}

return Colors.Transparent;
}
}

/// <summary>
/// Gets the <see cref="Brush"/> of the SystemAccentColorTertiary.
/// </summary>
public static Brush TertiaryAccentBrush => new SolidColorBrush(TertiaryAccent);

/// <summary>
/// Changes the color accents of the application based on the color entered.
/// </summary>
/// <param name="systemAccent">Primary accent color.</param>
/// <param name="applicationTheme">If <see cref="ApplicationTheme.Dark"/>, the colors will be different.</param>
/// <param name="systemGlassColor">If the color is taken from the Glass Color System, its brightness will be increased with the help of the operations on HSV space.</param>
public static void Apply(
Color systemAccent,
ApplicationTheme applicationTheme = ApplicationTheme.Light,
bool systemGlassColor = false
)
{
if (systemGlassColor)
{
// WindowGlassColor is little darker than accent color
systemAccent = systemAccent.UpdateBrightness(6f);
}

Color primaryAccent;
Color secondaryAccent;
Color tertiaryAccent;

if (applicationTheme == ApplicationTheme.Dark)
{
primaryAccent = systemAccent.Update(15f, -12f);
secondaryAccent = systemAccent.Update(30f, -24f);
tertiaryAccent = systemAccent.Update(45f, -36f);
}
else
{
primaryAccent = systemAccent.UpdateBrightness(-5f);
secondaryAccent = systemAccent.UpdateBrightness(-10f);
tertiaryAccent = systemAccent.UpdateBrightness(-15f);
}

UpdateColorResources(systemAccent, primaryAccent, secondaryAccent, tertiaryAccent);
}

/// <summary>
/// Changes the color accents of the application based on the entered colors.
/// </summary>
/// <param name="systemAccent">Primary color.</param>
/// <param name="primaryAccent">Alternative light or dark color.</param>
/// <param name="secondaryAccent">Second alternative light or dark color (most used).</param>
/// <param name="tertiaryAccent">Third alternative light or dark color.</param>
public static void Apply(
Color systemAccent,
Color primaryAccent,
Color secondaryAccent,
Color tertiaryAccent
)
{
UpdateColorResources(systemAccent, primaryAccent, secondaryAccent, tertiaryAccent);
}

/// <summary>
/// Applies system accent color to the application.
/// </summary>
public static void ApplySystemAccent()
{
Apply(GetColorizationColor(), ApplicationThemeManager.GetAppTheme());
}

/// <summary>
/// Gets current Desktop Window Manager colorization color.
/// <para>It should be the color defined in the system Personalization.</para>
/// </summary>
public static Color GetColorizationColor()
{
return UnsafeNativeMethodsWindow.GetDwmColor();
}

/// <summary>
/// Updates application resources.
/// </summary>
private static void UpdateColorResources(
Color systemAccent,
Color primaryAccent,
Color secondaryAccent,
Color tertiaryAccent
)
{
#if DEBUG
System.Diagnostics.Debug.WriteLine("INFO | SystemAccentColor: " + systemAccent, "System.Windows.Accent");
System
.Diagnostics
.Debug
.WriteLine("INFO | SystemAccentColorPrimary: " + primaryAccent, "System.Windows.Accent");
System
.Diagnostics
.Debug
.WriteLine("INFO | SystemAccentColorSecondary: " + secondaryAccent, "System.Windows.Accent");
System
.Diagnostics
.Debug
.WriteLine("INFO | SystemAccentColorTertiary: " + tertiaryAccent, "System.Windows.Accent");
#endif

if (secondaryAccent.GetBrightness() > BackgroundBrightnessThresholdValue)
{
#if DEBUG
System.Diagnostics.Debug.WriteLine("INFO | Text on accent is DARK", "System.Windows.Accent");
#endif
Application.Current.Resources["TextOnAccentFillColorPrimary"] = Color.FromArgb(
0xFF,
0x00,
0x00,
0x00
);
Application.Current.Resources["TextOnAccentFillColorSecondary"] = Color.FromArgb(
0x80,
0x00,
0x00,
0x00
);
Application.Current.Resources["TextOnAccentFillColorDisabled"] = Color.FromArgb(
0x77,
0x00,
0x00,
0x00
);
Application.Current.Resources["TextOnAccentFillColorSelectedText"] = Color.FromArgb(
0x00,
0x00,
0x00,
0x00
);
Application.Current.Resources["AccentTextFillColorDisabled"] = Color.FromArgb(
0x5D,
0x00,
0x00,
0x00
);
}
else
{
#if DEBUG
System.Diagnostics.Debug.WriteLine("INFO | Text on accent is LIGHT", "System.Windows.Accent");
#endif
Application.Current.Resources["TextOnAccentFillColorPrimary"] = Color.FromArgb(
0xFF,
0xFF,
0xFF,
0xFF
);
Application.Current.Resources["TextOnAccentFillColorSecondary"] = Color.FromArgb(
0x80,
0xFF,
0xFF,
0xFF
);
Application.Current.Resources["TextOnAccentFillColorDisabled"] = Color.FromArgb(
0x87,
0xFF,
0xFF,
0xFF
);
Application.Current.Resources["TextOnAccentFillColorSelectedText"] = Color.FromArgb(
0xFF,
0xFF,
0xFF,
0xFF
);
Application.Current.Resources["AccentTextFillColorDisabled"] = Color.FromArgb(
0x5D,
0xFF,
0xFF,
0xFF
);
}

Application.Current.Resources["SystemAccentColor"] = systemAccent;
Application.Current.Resources["SystemAccentColorPrimary"] = primaryAccent;
Application.Current.Resources["SystemAccentColorSecondary"] = secondaryAccent;
Application.Current.Resources["SystemAccentColorTertiary"] = tertiaryAccent;

Application.Current.Resources["SystemAccentBrush"] = systemAccent.ToBrush();
Application.Current.Resources["SystemFillColorAttentionBrush"] = secondaryAccent.ToBrush();
Application.Current.Resources["AccentTextFillColorPrimaryBrush"] = tertiaryAccent.ToBrush();
Application.Current.Resources["AccentTextFillColorSecondaryBrush"] = tertiaryAccent.ToBrush();
Application.Current.Resources["AccentTextFillColorTertiaryBrush"] = secondaryAccent.ToBrush();
Application.Current.Resources["AccentFillColorSelectedTextBackgroundBrush"] = systemAccent.ToBrush();
Application.Current.Resources["AccentFillColorDefaultBrush"] = secondaryAccent.ToBrush();

Application.Current.Resources["AccentFillColorSecondaryBrush"] = secondaryAccent.ToBrush(0.9);
Application.Current.Resources["AccentFillColorTertiaryBrush"] = secondaryAccent.ToBrush(0.8);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
// All Rights Reserved.

namespace System.Windows.Appearance;

/// <summary>
/// Theme in which an application using WPF UI is displayed.
/// </summary>
public enum ApplicationTheme
{
/// <summary>
/// Unknown application theme.
/// </summary>
Unknown,

/// <summary>
/// Dark application theme.
/// </summary>
Dark,

/// <summary>
/// Light application theme.
/// </summary>
Light,

/// <summary>
/// High contract application theme.
/// </summary>
HighContrast
}
Loading