Skip to content
Open
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 @@ -51,21 +51,21 @@ static SettingsCard()
DefaultStyleKeyProperty.OverrideMetadata(typeof(SettingsCard), new FrameworkPropertyMetadata(typeof(SettingsCard)));
}

internal static readonly DependencyPropertyDescriptor IsPressedPropertyDescriptior = DependencyPropertyDescriptor.FromProperty(IsPressedProperty, typeof(SettingsCard));
internal static readonly DependencyPropertyDescriptor IsMouseOverPropertyDescriptior = DependencyPropertyDescriptor.FromProperty(IsMouseOverProperty, typeof(SettingsCard));

/// <summary>
/// Creates a new instance of the <see cref="SettingsCard"/> class.
/// </summary>
public SettingsCard()
{
IsPressedPropertyDescriptior.AddValueChanged(this, PointerStateProperties_ValueChanged);
IsMouseOverPropertyDescriptior.AddValueChanged(this, PointerStateProperties_ValueChanged);
}

private void PointerStateProperties_ValueChanged(object sender, EventArgs e)
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
this.UpdatePointerState();
base.OnPropertyChanged(e);

if (e.Property == IsPressedProperty || e.Property == IsMouseOverProperty)
{
this.UpdatePointerState();
}
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ static MessageBox()
DefaultStyleKeyProperty.OverrideMetadata(typeof(MessageBox), new FrameworkPropertyMetadata(typeof(MessageBox)));
}

public static readonly DependencyPropertyDescriptor SystemBackdropTypeProperty_Descriptor = DependencyPropertyDescriptor.FromProperty(WindowHelper.SystemBackdropTypeProperty, typeof(MessageBox));

public MessageBox()
{
CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, new ExecutedRoutedEventHandler(ExecuteCopy)));
Expand All @@ -60,9 +58,17 @@ public MessageBox()

Loaded += On_Loaded;

SystemBackdropTypeProperty_Descriptor.AddValueChanged(this, SystemBackdropTypeProperty_ValueChanged);
ThemeManager.AddActualThemeChangedHandler(this, ThemeManager_AddActualThemeChanged);
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);

if (e.Property == WindowHelper.SystemBackdropTypeProperty)
{
SystemBackdropTypeProperty_ValueChanged(this, EventArgs.Empty);
}
}

private void ExecuteCopy(object sender, ExecutedRoutedEventArgs e)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,16 @@ static InfoBar()
public InfoBar()
{
SetValue(TemplateSettingsPropertyKey, new InfoBarTemplateSettings());
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(ForegroundProperty, typeof(InfoBar));
descriptor.AddValueChanged(this, (sender, e) => UpdateForeground());
}

protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);

if (e.Property == ForegroundProperty)
{
UpdateForeground();
}
}

protected override AutomationPeer OnCreateAutomationPeer()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@

using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Navigation;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using iNKORE.UI.WPF.Modern.Gallery.Helpers;
using iNKORE.UI.WPF.Modern;

Expand All @@ -19,26 +17,16 @@ namespace iNKORE.UI.WPF.Modern.Gallery.Pages.Controls.Foundation
/// </summary>
public sealed partial class TypographyPage : Page
{
private DispatcherTimer _themeMonitorTimer;
private ElementTheme _lastKnownTheme = ElementTheme.Default;

public TypographyPage()
{
this.InitializeComponent();
Loaded += TypographyPage_Loaded;

ThemeManager.Current.ActualApplicationThemeChanged += OnThemeChanged;
ThemeManager.AddActualThemeChangedHandler(this, OnElementThemeChanged);

DependencyPropertyDescriptor.FromProperty(ThemeManager.RequestedThemeProperty, typeof(FrameworkElement))
?.AddValueChanged(this, OnRequestedThemeChanged);

_themeMonitorTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(200)
};
_themeMonitorTimer.Tick += ThemeMonitorTimer_Tick;
_themeMonitorTimer.Start();

// The header image sits inside the ControlExample, whose theme the gallery's toggle theme button
// flips on its own, so watching the theme of the page is not enough. ActualTheme is kept in sync on
// every element of the tree, so watching the image itself catches the toggle and application level
// theme changes alike. No need to detach: the handler belongs to the page the image is part of
ThemeManager.AddActualThemeChangedHandler(TypographyHeaderImage, OnImageActualThemeChanged);
}

private void TypographyPage_Loaded(object sender, RoutedEventArgs e)
Expand All @@ -47,67 +35,23 @@ private void TypographyPage_Loaded(object sender, RoutedEventArgs e)
{
NavigationRootPage.Current.NavigationView.Header = "Typography";
}

UpdateTypographyImage();

UpdateExampleCode();
}

//FAILED TRIALS but keeping for reference - The image should switch when toggle theme clicked
private void OnImageActualThemeChanged(object sender, RoutedEventArgs e)
{
UpdateTypographyImage();
}

private void UpdateTypographyImage()
{
if (TypographyHeaderImage == null) return;

// Multi-level theme detection to catch both application and element-level changes
var pageTheme = ThemeManager.GetActualTheme(this);
var parentTheme = ElementTheme.Default;
var controlExampleTheme = ElementTheme.Default;

// Check parent elements for theme overrides (catches toggle theme changes)
var parentElement = this.Parent as FrameworkElement;
while (parentElement != null)
{
var currentParentTheme = ThemeManager.GetActualTheme(parentElement);
if (currentParentTheme != ElementTheme.Default)
{
parentTheme = currentParentTheme;
break;
}
parentElement = parentElement.Parent as FrameworkElement;
}

// Check ControlExample elements for theme changes (this is where toggle theme applies changes)
if (Example1 != null)
{
try
{
var exampleTheme = ThemeManager.GetActualTheme(Example1);
if (exampleTheme != ElementTheme.Default)
{
controlExampleTheme = exampleTheme;
}
else if (Example1.ExampleContainer != null)
{
var containerTheme = ThemeManager.GetActualTheme(Example1.ExampleContainer);
if (containerTheme != ElementTheme.Default)
{
controlExampleTheme = containerTheme;
}
}
}
catch (Exception ex)
{
// Handle potential issues with theme detection during control initialization
System.Diagnostics.Debug.WriteLine($"Theme detection error: {ex.Message}");
}
}

// Use the most specific theme available (ControlExample > Page > Parent > Application)
var effectiveTheme = controlExampleTheme != ElementTheme.Default ? controlExampleTheme :
pageTheme != ElementTheme.Default ? pageTheme : parentTheme;
var isDarkTheme = effectiveTheme == ElementTheme.Dark ||
(effectiveTheme == ElementTheme.Default && ThemeHelper.IsDarkTheme());

var isDarkTheme = ThemeManager.GetActualTheme(TypographyHeaderImage) == ElementTheme.Dark;

var imageName = isDarkTheme ? "Typography.dark.png" : "Typography.light.png";
var uri = new System.Uri($"pack://application:,,,/iNKORE.UI.WPF.Modern.Gallery;component/Assets/Design/{imageName}");

Expand All @@ -122,8 +66,6 @@ private void UpdateTypographyImage()
bitmapImage.EndInit();
bitmapImage.Freeze();
TypographyHeaderImage.Source = bitmapImage;

System.Diagnostics.Debug.WriteLine($"Typography image updated to: {imageName} (Page: {pageTheme}, Parent: {parentTheme}, ControlExample: {controlExampleTheme}, Effective: {effectiveTheme}, IsDark: {isDarkTheme})");
}
catch (Exception ex)
{
Expand All @@ -134,85 +76,6 @@ private void UpdateTypographyImage()
}
}

private void OnThemeChanged(ThemeManager sender, object args)
{
// Update the image when theme changes
UpdateTypographyImage();
}

private void OnElementThemeChanged(object sender, RoutedEventArgs e)
{
// Update the image when element theme changes (for theme toggle)
UpdateTypographyImage();
}

private void ThemeMonitorTimer_Tick(object sender, EventArgs e)
{
// Check if the theme has changed by monitoring our current actual theme
var currentTheme = ThemeManager.GetActualTheme(this);
if (currentTheme != _lastKnownTheme)
{
_lastKnownTheme = currentTheme;
UpdateTypographyImage();
System.Diagnostics.Debug.WriteLine($"Theme change detected: {currentTheme}");
}

// Also check for element-level theme changes by examining parent elements
// This catches toggle theme changes that affect control examples
var parentElement = this.Parent as FrameworkElement;
while (parentElement != null)
{
var parentTheme = ThemeManager.GetActualTheme(parentElement);
if (parentTheme != currentTheme)
{
// Found a parent with different theme - this indicates element-level theme change
UpdateTypographyImage();
System.Diagnostics.Debug.WriteLine($"Element-level theme change detected: Parent={parentTheme}, Current={currentTheme}");
break;
}
parentElement = parentElement.Parent as FrameworkElement;
}

if (Example1 != null)
{
try
{
var controlExampleTheme = ThemeManager.GetActualTheme(Example1);
var containerTheme = ElementTheme.Default;

if (Example1.ExampleContainer != null)
{
containerTheme = ThemeManager.GetActualTheme(Example1.ExampleContainer);
}

if (controlExampleTheme != currentTheme || containerTheme != currentTheme)
{
UpdateTypographyImage();
System.Diagnostics.Debug.WriteLine($"ControlExample theme change detected: ControlExample={controlExampleTheme}, Container={containerTheme}, Page={currentTheme}");
}
}
catch (Exception ex)
{
// Handle potential issues with theme detection during control initialization
System.Diagnostics.Debug.WriteLine($"Theme monitor error: {ex.Message}");
}
}
}

private void OnRequestedThemeChanged(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new System.Action(() => {
UpdateTypographyImage();
}), System.Windows.Threading.DispatcherPriority.ApplicationIdle);

var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
timer.Tick += (s, args) => {
timer.Stop();
UpdateTypographyImage();
};
timer.Start();
}

private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
Expand Down
33 changes: 30 additions & 3 deletions source/iNKORE.UI.WPF.Modern/Controls/AnimatedIcon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class AnimatedIcon : IconElement
public AnimatedIcon()
{
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}

#region FallbackIconSource
Expand Down Expand Up @@ -229,6 +230,9 @@ private void OnStatePropertyChanged()
}
}

private DependencyObject _ancestorWithState;
private EventHandler _ancestorStateChangedHandler;

private void OnLoaded(object UnnamedParameter, RoutedEventArgs UnnamedParameter2)
{
// AnimatedIcon might get added to a UI which has already set the State property on an ancestor.
Expand Down Expand Up @@ -259,10 +263,16 @@ private void OnLoaded(object UnnamedParameter, RoutedEventArgs UnnamedParameter2
SetValue(property, stateValue);
}

if (ancestorWithState != null)
DetachFromAncestorWithState();

if (ancestorWithState is DependencyObject ancestor)
{
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(property, typeof(AnimatedIcon));
descriptor.AddValueChanged(ancestorWithState, (sender, e) => OnAncestorAnimatedIconStatePropertyChanged(ancestorWithState, property));
// The descriptor is cached for the lifetime of the process and keeps a strong reference to the
// ancestor, so the handler has to be removed again when this icon is unloaded
_ancestorWithState = ancestor;
_ancestorStateChangedHandler = (sender, e) => OnAncestorAnimatedIconStatePropertyChanged(ancestor, property);
DependencyPropertyDescriptor.FromProperty(property, typeof(AnimatedIcon))
.AddValueChanged(ancestor, _ancestorStateChangedHandler);
}

// Wait until loaded to apply the fallback icon source property because we need the icon source
Expand All @@ -271,6 +281,23 @@ private void OnLoaded(object UnnamedParameter, RoutedEventArgs UnnamedParameter2
OnFallbackIconSourcePropertyChanged(new DependencyPropertyChangedEventArgs());
}

private void OnUnloaded(object sender, RoutedEventArgs e)
{
DetachFromAncestorWithState();
}

private void DetachFromAncestorWithState()
{
if (_ancestorWithState != null)
{
DependencyPropertyDescriptor.FromProperty(StateProperty, typeof(AnimatedIcon))
.RemoveValueChanged(_ancestorWithState, _ancestorStateChangedHandler);

_ancestorWithState = null;
_ancestorStateChangedHandler = null;
}
}

private void OnAncestorAnimatedIconStatePropertyChanged(object sender, DependencyProperty args)
{
SetValue(StateProperty, ((DependencyObject)sender).GetValue(args));
Expand Down
Loading
Loading