diff --git a/demo/Ursa.Demo/Models/MenuKeys.cs b/demo/Ursa.Demo/Models/MenuKeys.cs
index 2283b6a..60d6ef2 100644
--- a/demo/Ursa.Demo/Models/MenuKeys.cs
+++ b/demo/Ursa.Demo/Models/MenuKeys.cs
@@ -25,5 +25,6 @@ public static class MenuKeys
public const string MenuKeyTagInput = "TagInput";
public const string MenuKeyTimeline = "Timeline";
public const string MenuKeyTwoTonePathIcon = "TwoTonePathIcon";
+ public const string MenuKeyThemeToggler = "ThemeToggler";
}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml b/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml
new file mode 100644
index 0000000..021c289
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs b/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs
new file mode 100644
index 0000000..a8e48c6
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Ursa.Demo.Pages;
+
+public partial class ThemeTogglerDemo : UserControl
+{
+ public ThemeTogglerDemo()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
index 0206b1d..6797172 100644
--- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
@@ -47,6 +47,7 @@ public class MainViewViewModel : ViewModelBase
MenuKeys.MenuKeyTagInput => new TagInputDemoViewModel(),
MenuKeys.MenuKeyTimeline => new TimelineDemoViewModel(),
MenuKeys.MenuKeyTwoTonePathIcon => new TwoTonePathIconDemoViewModel(),
+ MenuKeys.MenuKeyThemeToggler => new ThemeTogglerDemoViewModel(),
};
}
}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
index 74b5ff7..cf807af 100644
--- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
@@ -32,6 +32,7 @@ public class MenuViewModel: ViewModelBase
new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination },
new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider, Status = "New"},
new() { MenuHeader = "TagInput", Key = MenuKeys.MenuKeyTagInput },
+ new() { MenuHeader = "Theme Toggler", Key = MenuKeys.MenuKeyThemeToggler },
new() { MenuHeader = "Timeline", Key = MenuKeys.MenuKeyTimeline, Status = "Updated" },
new() { MenuHeader = "TwoTonePathIcon", Key = MenuKeys.MenuKeyTwoTonePathIcon, Status = "New"},
};
diff --git a/demo/Ursa.Demo/ViewModels/ThemeTogglerDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/ThemeTogglerDemoViewModel.cs
new file mode 100644
index 0000000..66a65da
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/ThemeTogglerDemoViewModel.cs
@@ -0,0 +1,6 @@
+namespace Ursa.Demo.ViewModels;
+
+public class ThemeTogglerDemoViewModel
+{
+
+}
\ No newline at end of file
diff --git a/src/Ursa/Controls/ThemeSelector/ThemeSelectorBase.cs b/src/Ursa/Controls/ThemeSelector/ThemeSelectorBase.cs
index 20f240a..033acc3 100644
--- a/src/Ursa/Controls/ThemeSelector/ThemeSelectorBase.cs
+++ b/src/Ursa/Controls/ThemeSelector/ThemeSelectorBase.cs
@@ -3,11 +3,13 @@ using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.LogicalTree;
using Avalonia.Styling;
+using Ursa.Common;
namespace Ursa.Controls;
public abstract class ThemeSelectorBase: TemplatedControl
{
+ private bool _syncFromScope;
private Application? _application;
private ThemeVariantScope? _scope;
@@ -33,17 +35,81 @@ public abstract class ThemeSelectorBase: TemplatedControl
static ThemeSelectorBase()
{
SelectedThemeProperty.Changed.AddClassHandler((s, e) => s.OnSelectedThemeChanged(e));
+ TargetScopeProperty.Changed.AddClassHandler((s, e) => s.OnTargetScopeChanged(e));
+ }
+
+ private void OnTargetScopeChanged(AvaloniaPropertyChangedEventArgs args)
+ {
+ var target = args.NewValue.Value;
+ if (target is not null)
+ {
+ SyncThemeFromScope(target.ActualThemeVariant);
+ target.ActualThemeVariantChanged += OnScopeThemeChanged;
+ }
+ }
+
+ private void OnScopeThemeChanged(object sender, EventArgs e)
+ {
+ _syncFromScope = true;
+ if (this.TargetScope is { } target)
+ {
+ SyncThemeFromScope(target.ActualThemeVariant);
+ }
+ else if (this._scope is { } scope)
+ {
+ SyncThemeFromScope(scope.ActualThemeVariant);
+ }
+ else if (_application is { } app)
+ {
+ SyncThemeFromScope(app.ActualThemeVariant);
+ }
+ _syncFromScope = false;
+ }
+
+ protected internal virtual void SyncThemeFromScope(ThemeVariant? theme)
+ {
+ this.SelectedTheme = theme;
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
_application = Application.Current;
+ if (_application is not null)
+ {
+ _application.ActualThemeVariantChanged += OnScopeThemeChanged;
+ SyncThemeFromScope(_application.ActualThemeVariant);
+ }
_scope = this.GetLogicalAncestors().FirstOrDefault(a => a is ThemeVariantScope) as ThemeVariantScope;
+ if (_scope is not null)
+ {
+ _scope.ActualThemeVariantChanged += OnScopeThemeChanged;
+ SyncThemeFromScope(_scope.ActualThemeVariant);
+ }
+
+ if (TargetScope is not null)
+ {
+ SyncThemeFromScope(TargetScope.ActualThemeVariant);
+ }
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ if (_application is not null)
+ {
+ _application.ActualThemeVariantChanged -= OnScopeThemeChanged;
+ }
+
+ if (_scope is not null)
+ {
+ _scope.ActualThemeVariantChanged -= OnScopeThemeChanged;
+ }
}
protected virtual void OnSelectedThemeChanged(AvaloniaPropertyChangedEventArgs args)
{
+ if (_syncFromScope) return;
ThemeVariant? newTheme = args.NewValue.Value;
if (newTheme is null) return;
if (TargetScope is not null)
diff --git a/src/Ursa/Controls/ThemeSelector/ThemeToggleButton.cs b/src/Ursa/Controls/ThemeSelector/ThemeToggleButton.cs
index 80cead7..db1188e 100644
--- a/src/Ursa/Controls/ThemeSelector/ThemeToggleButton.cs
+++ b/src/Ursa/Controls/ThemeSelector/ThemeToggleButton.cs
@@ -28,18 +28,22 @@ public class ThemeToggleButton: ThemeSelectorBase
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
- EventHelper.UnregisterEvent(ToggleButton.IsCheckedChangedEvent, OnButtonCheckedChanged, _button);
+ EventHelper.UnregisterEvent(Button.ClickEvent, OnButtonClickedChanged, _button);
_button = e.NameScope.Get(PART_ThemeToggleButton);
- EventHelper.RegisterEvent(ToggleButton.IsCheckedChangedEvent, OnButtonCheckedChanged, _button);
+ EventHelper.RegisterEvent(Button.ClickEvent, OnButtonClickedChanged, _button);
PropertyHelper.SetValue(ToggleButton.IsCheckedProperty, _currentTheme == ThemeVariant.Light, _button);
}
- private void OnButtonCheckedChanged(object sender, RoutedEventArgs e)
+ private void OnButtonClickedChanged(object sender, RoutedEventArgs e)
{
var newTheme = (sender as ToggleButton)!.IsChecked;
if (newTheme is null) return;
- SelectedTheme = newTheme.Value ? ThemeVariant.Light : ThemeVariant.Dark;
+ SetCurrentValue(SelectedThemeProperty, newTheme.Value ? ThemeVariant.Light : ThemeVariant.Dark);
+ }
+
+ protected internal override void SyncThemeFromScope(ThemeVariant? theme)
+ {
+ base.SyncThemeFromScope(theme);
+ PropertyHelper.SetValue(ToggleButton.IsCheckedProperty, theme == ThemeVariant.Light, _button);
}
-
-
}
\ No newline at end of file