diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index 01bf303a20..4f9c4d7720 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -99,6 +99,7 @@
AssemblyFile="@(IntermediateAssembly)"
ReferencesFilePath="$(AvaloniaXamlReferencesTemporaryFilePath)"
OriginalCopyPath="$(AvaloniaXamlOriginalCopyFilePath)"
+ RefAssemblyFile="@(IntermediateRefAssembly)"
ProjectDirectory="$(MSBuildProjectDirectory)"
VerifyIl="$(AvaloniaXamlIlVerifyIl)"
ReportImportance="$(AvaloniaXamlReportImportance)"
diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml
index 5570ada27b..8f32fa01dd 100644
--- a/samples/ControlCatalog/App.xaml
+++ b/samples/ControlCatalog/App.xaml
@@ -9,6 +9,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs
index 750c1082a6..6c99eb5289 100644
--- a/samples/ControlCatalog/App.xaml.cs
+++ b/samples/ControlCatalog/App.xaml.cs
@@ -3,85 +3,46 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
-using Avalonia.Markup.Xaml.Styling;
using Avalonia.Styling;
using Avalonia.Themes.Simple;
using Avalonia.Themes.Fluent;
+using ControlCatalog.Models;
using ControlCatalog.ViewModels;
namespace ControlCatalog
{
public class App : Application
{
+ private readonly Styles _themeStylesContainer = new();
+ private FluentTheme? _fluentTheme;
+ private SimpleTheme? _simpleTheme;
+ private IResourceDictionary? _fluentBaseLightColors, _fluentBaseDarkColors;
+ private IStyle? _colorPickerFluent, _colorPickerSimple;
+ private IStyle? _dataGridFluent, _dataGridSimple;
+
public App()
{
DataContext = new ApplicationViewModel();
}
- public static readonly StyleInclude ColorPickerFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
- {
- Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml")
- };
-
- public static readonly StyleInclude ColorPickerSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
- {
- Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml")
- };
-
- public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
- {
- Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml")
- };
-
- public static readonly StyleInclude DataGridSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
- {
- Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml")
- };
-
- public static FluentTheme Fluent = new FluentTheme(new Uri("avares://ControlCatalog/Styles"));
-
- public static SimpleTheme Simple = new SimpleTheme(new Uri("avares://ControlCatalog/Styles"));
-
- public static Styles SimpleLight = new Styles
- {
- new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
- {
- Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml")
- },
- new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
- {
- Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
- },
- new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
- {
- Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
- },
- Simple
- };
-
- public static Styles SimpleDark = new Styles
- {
- new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
- {
- Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml")
- },
- new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
- {
- Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
- },
- new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
- {
- Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml")
- },
- Simple
- };
-
public override void Initialize()
{
- Styles.Insert(0, Fluent);
- Styles.Insert(1, ColorPickerFluent);
- Styles.Insert(2, DataGridFluent);
+ Styles.Add(_themeStylesContainer);
+
AvaloniaXamlLoader.Load(this);
+
+ _fluentTheme = new FluentTheme();
+ _simpleTheme = new SimpleTheme();
+ _simpleTheme.Resources.MergedDictionaries.Add((IResourceDictionary)Resources["FluentAccentColors"]!);
+ _simpleTheme.Resources.MergedDictionaries.Add((IResourceDictionary)Resources["FluentBaseColors"]!);
+ _colorPickerFluent = (IStyle)Resources["ColorPickerFluent"]!;
+ _colorPickerSimple = (IStyle)Resources["ColorPickerSimple"]!;
+ _dataGridFluent = (IStyle)Resources["DataGridFluent"]!;
+ _dataGridSimple = (IStyle)Resources["DataGridSimple"]!;
+ _fluentBaseLightColors = (IResourceDictionary)Resources["FluentBaseLightColors"]!;
+ _fluentBaseDarkColors = (IResourceDictionary)Resources["FluentBaseDarkColors"]!;
+
+ SetThemeVariant(CatalogTheme.FluentLight);
}
public override void OnFrameworkInitializationCompleted()
@@ -97,5 +58,78 @@ namespace ControlCatalog
base.OnFrameworkInitializationCompleted();
}
+
+ private CatalogTheme _prevTheme;
+ public static CatalogTheme CurrentTheme => ((App)Current!)._prevTheme;
+ public static void SetThemeVariant(CatalogTheme theme)
+ {
+ var app = (App)Current!;
+ var prevTheme = app._prevTheme;
+ app._prevTheme = theme;
+ var shouldReopenWindow = theme switch
+ {
+ CatalogTheme.FluentLight => prevTheme is CatalogTheme.SimpleDark or CatalogTheme.SimpleLight,
+ CatalogTheme.FluentDark => prevTheme is CatalogTheme.SimpleDark or CatalogTheme.SimpleLight,
+ CatalogTheme.SimpleLight => prevTheme is CatalogTheme.FluentDark or CatalogTheme.FluentLight,
+ CatalogTheme.SimpleDark => prevTheme is CatalogTheme.FluentDark or CatalogTheme.FluentLight,
+ _ => throw new ArgumentOutOfRangeException(nameof(theme), theme, null)
+ };
+
+ if (app._themeStylesContainer.Count == 0)
+ {
+ app._themeStylesContainer.Add(new Style());
+ app._themeStylesContainer.Add(new Style());
+ app._themeStylesContainer.Add(new Style());
+ }
+
+ if (theme == CatalogTheme.FluentLight)
+ {
+ app._fluentTheme!.Mode = FluentThemeMode.Light;
+ app._themeStylesContainer[0] = app._fluentTheme;
+ app._themeStylesContainer[1] = app._colorPickerFluent!;
+ app._themeStylesContainer[2] = app._dataGridFluent!;
+ }
+ else if (theme == CatalogTheme.FluentDark)
+ {
+ app._fluentTheme!.Mode = FluentThemeMode.Dark;
+ app._themeStylesContainer[0] = app._fluentTheme;
+ app._themeStylesContainer[1] = app._colorPickerFluent!;
+ app._themeStylesContainer[2] = app._dataGridFluent!;
+ }
+ else if (theme == CatalogTheme.SimpleLight)
+ {
+ app._simpleTheme!.Mode = SimpleThemeMode.Light;
+ app._simpleTheme.Resources.MergedDictionaries.Remove(app._fluentBaseDarkColors!);
+ app._simpleTheme.Resources.MergedDictionaries.Add(app._fluentBaseLightColors!);
+ app._themeStylesContainer[0] = app._simpleTheme;
+ app._themeStylesContainer[1] = app._colorPickerSimple!;
+ app._themeStylesContainer[2] = app._dataGridSimple!;
+ }
+ else if (theme == CatalogTheme.SimpleDark)
+ {
+ app._simpleTheme!.Mode = SimpleThemeMode.Dark;
+ app._simpleTheme.Resources.MergedDictionaries.Remove(app._fluentBaseLightColors!);
+ app._simpleTheme.Resources.MergedDictionaries.Add(app._fluentBaseDarkColors!);
+ app._themeStylesContainer[0] = app._simpleTheme;
+ app._themeStylesContainer[1] = app._colorPickerSimple!;
+ app._themeStylesContainer[2] = app._dataGridSimple!;
+ }
+
+ if (shouldReopenWindow)
+ {
+ if (app.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
+ {
+ var oldWindow = desktopLifetime.MainWindow;
+ var newWindow = new MainWindow();
+ desktopLifetime.MainWindow = newWindow;
+ newWindow.Show();
+ oldWindow?.Close();
+ }
+ else if (app.ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime)
+ {
+ singleViewLifetime.MainView = new MainView();
+ }
+ }
+ }
}
}
diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs
index 7001eb41ea..15e666ae7b 100644
--- a/samples/ControlCatalog/MainView.xaml.cs
+++ b/samples/ControlCatalog/MainView.xaml.cs
@@ -6,7 +6,6 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Media.Immutable;
-using Avalonia.Themes.Fluent;
using ControlCatalog.Models;
using ControlCatalog.Pages;
@@ -31,46 +30,12 @@ namespace ControlCatalog
}
var themes = this.Get("Themes");
+ themes.SelectedItem = App.CurrentTheme;
themes.SelectionChanged += (sender, e) =>
{
if (themes.SelectedItem is CatalogTheme theme)
{
- var themeStyle = Application.Current!.Styles[0];
- if (theme == CatalogTheme.FluentLight)
- {
- if (App.Fluent.Mode != FluentThemeMode.Light)
- {
- App.Fluent.Mode = FluentThemeMode.Light;
- }
- Application.Current.Styles[0] = App.Fluent;
- Application.Current.Styles[1] = App.ColorPickerFluent;
- Application.Current.Styles[2] = App.DataGridFluent;
- }
- else if (theme == CatalogTheme.FluentDark)
- {
-
- if (App.Fluent.Mode != FluentThemeMode.Dark)
- {
- App.Fluent.Mode = FluentThemeMode.Dark;
- }
- Application.Current.Styles[0] = App.Fluent;
- Application.Current.Styles[1] = App.ColorPickerFluent;
- Application.Current.Styles[2] = App.DataGridFluent;
- }
- else if (theme == CatalogTheme.SimpleLight)
- {
- App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Light;
- Application.Current.Styles[0] = App.SimpleLight;
- Application.Current.Styles[1] = App.ColorPickerSimple;
- Application.Current.Styles[2] = App.DataGridSimple;
- }
- else if (theme == CatalogTheme.SimpleDark)
- {
- App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Dark;
- Application.Current.Styles[0] = App.SimpleDark;
- Application.Current.Styles[1] = App.ColorPickerSimple;
- Application.Current.Styles[2] = App.DataGridSimple;
- }
+ App.SetThemeVariant(theme);
}
};
diff --git a/src/Avalonia.Base/Animation/Animatable.cs b/src/Avalonia.Base/Animation/Animatable.cs
index b045a32cd1..edaa76233e 100644
--- a/src/Avalonia.Base/Animation/Animatable.cs
+++ b/src/Avalonia.Base/Animation/Animatable.cs
@@ -235,7 +235,7 @@ namespace Avalonia.Animation
private object? GetAnimationBaseValue(AvaloniaProperty property)
{
- var value = this.GetBaseValue(property, BindingPriority.LocalValue);
+ var value = this.GetBaseValue(property);
if (value == AvaloniaProperty.UnsetValue)
{
diff --git a/src/Avalonia.Base/Animation/AnimationInstance`1.cs b/src/Avalonia.Base/Animation/AnimationInstance`1.cs
index 52cd4b324f..0881fde988 100644
--- a/src/Avalonia.Base/Animation/AnimationInstance`1.cs
+++ b/src/Avalonia.Base/Animation/AnimationInstance`1.cs
@@ -229,7 +229,7 @@ namespace Avalonia.Animation
private void UpdateNeutralValue()
{
var property = _animator.Property ?? throw new InvalidOperationException("Animator has no property specified.");
- var baseValue = _targetControl.GetBaseValue(property, BindingPriority.LocalValue);
+ var baseValue = _targetControl.GetBaseValue(property);
_neutralValue = baseValue != AvaloniaProperty.UnsetValue ?
(T)baseValue! : (T)_targetControl.GetValue(property)!;
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index d8fcce803f..29e0b0a7c9 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -28,6 +28,7 @@
+
@@ -39,6 +40,7 @@
+
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 6633eabb5d..68c8f19f19 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Runtime.CompilerServices;
using Avalonia.Data;
using Avalonia.Diagnostics;
using Avalonia.Logging;
using Avalonia.PropertyStore;
-using Avalonia.Reactive;
using Avalonia.Threading;
namespace Avalonia
@@ -18,13 +18,11 @@ namespace Avalonia
///
public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged
{
+ private readonly ValueStore _values;
private AvaloniaObject? _inheritanceParent;
- private List? _directBindings;
private PropertyChangedEventHandler? _inpcChanged;
private EventHandler? _propertyChanged;
private List? _inheritanceChildren;
- private ValueStore? _values;
- private bool _batchUpdate;
///
/// Initializes a new instance of the class.
@@ -32,6 +30,7 @@ namespace Avalonia
public AvaloniaObject()
{
VerifyAccess();
+ _values = new ValueStore(this);
}
///
@@ -59,7 +58,7 @@ namespace Avalonia
///
/// The inheritance parent.
///
- protected AvaloniaObject? InheritanceParent
+ protected internal AvaloniaObject? InheritanceParent
{
get
{
@@ -72,28 +71,10 @@ namespace Avalonia
if (_inheritanceParent != value)
{
- var oldParent = _inheritanceParent;
- var valuestore = _values;
-
_inheritanceParent?.RemoveInheritanceChild(this);
_inheritanceParent = value;
-
- var properties = AvaloniaPropertyRegistry.Instance.GetRegisteredInherited(GetType());
- var propertiesCount = properties.Count;
-
- for (var i = 0; i < propertiesCount; i++)
- {
- var property = properties[i];
- if (valuestore?.IsSet(property) == true)
- {
- // If local value set there can be no change.
- continue;
- }
-
- property.RouteInheritanceParentChanged(this, oldParent);
- }
-
_inheritanceParent?.AddInheritanceChild(this);
+ _values.SetInheritanceParent(value);
}
}
}
@@ -118,24 +99,15 @@ namespace Avalonia
set { this.Bind(binding.Property!, value); }
}
- private ValueStore Values
- {
- get
- {
- if (_values is null)
- {
- _values = new ValueStore(this);
-
- if (_batchUpdate)
- _values.BeginBatchUpdate();
- }
-
- return _values;
- }
- }
-
+ ///
+ /// Returns a value indicating whether the current thread is the UI thread.
+ ///
+ /// true if the current thread is the UI thread; otherwise false.
public bool CheckAccess() => Dispatcher.UIThread.CheckAccess();
+ ///
+ /// Checks that the current thread is the UI thread and throws if not.
+ ///
public void VerifyAccess() => Dispatcher.UIThread.VerifyAccess();
///
@@ -144,9 +116,9 @@ namespace Avalonia
/// The property.
public void ClearValue(AvaloniaProperty property)
{
- property = property ?? throw new ArgumentNullException(nameof(property));
-
- property.RouteClearValue(this);
+ _ = property ?? throw new ArgumentNullException(nameof(property));
+ VerifyAccess();
+ _values.ClearLocalValue(property);
}
///
@@ -234,9 +206,12 @@ namespace Avalonia
/// The value.
public object? GetValue(AvaloniaProperty property)
{
- property = property ?? throw new ArgumentNullException(nameof(property));
+ _ = property ?? throw new ArgumentNullException(nameof(property));
- return property.RouteGetValue(this);
+ if (property.IsDirect)
+ return property.RouteGetValue(this);
+ else
+ return _values.GetValue(property);
}
///
@@ -247,10 +222,9 @@ namespace Avalonia
/// The value.
public T GetValue(StyledPropertyBase property)
{
- property = property ?? throw new ArgumentNullException(nameof(property));
+ _ = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess();
-
- return GetValueOrInheritedOrDefault(property);
+ return _values.GetValue(property);
}
///
@@ -269,18 +243,11 @@ namespace Avalonia
}
///
- public Optional GetBaseValue(StyledPropertyBase property, BindingPriority maxPriority)
+ public Optional GetBaseValue(StyledPropertyBase property)
{
- property = property ?? throw new ArgumentNullException(nameof(property));
+ _ = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess();
-
- if (_values is object &&
- _values.TryGetValue(property, maxPriority, out var value))
- {
- return value;
- }
-
- return default;
+ return _values.GetBaseValue(property);
}
///
@@ -346,26 +313,20 @@ namespace Avalonia
T value,
BindingPriority priority = BindingPriority.LocalValue)
{
- property = property ?? throw new ArgumentNullException(nameof(property));
+ _ = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess();
+ ValidatePriority(priority);
- LogPropertySet(property, value, priority);
+ LogPropertySet(property, value, BindingPriority.LocalValue);
if (value is UnsetValueType)
{
if (priority == BindingPriority.LocalValue)
- {
- Values.ClearLocalValue(property);
- }
- else
- {
- throw new NotSupportedException(
- "Cannot set property to Unset at non-local value priority.");
- }
+ _values.ClearLocalValue(property);
}
- else if (!(value is DoNothingType))
+ else if (value is not DoNothingType)
{
- return Values.SetValue(property, value, priority);
+ return _values.SetValue(property, value, priority);
}
return null;
@@ -382,6 +343,7 @@ namespace Avalonia
property = property ?? throw new ArgumentNullException(nameof(property));
VerifyAccess();
+ property = AvaloniaPropertyRegistry.Instance.GetRegisteredDirect(this, property);
LogPropertySet(property, value, BindingPriority.LocalValue);
SetDirectValueUnchecked(property, value);
}
@@ -398,12 +360,52 @@ namespace Avalonia
public IDisposable Bind(
AvaloniaProperty property,
IObservable