diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml
index f1fcd8035a..4c3c211ca5 100644
--- a/samples/ControlCatalog/Pages/DataGridPage.xaml
+++ b/samples/ControlCatalog/Pages/DataGridPage.xaml
@@ -14,22 +14,6 @@
-
-
-
-
-
-
A control for displaying and interacting with a data source.
@@ -45,8 +29,7 @@
+ RowBackground="#1000">
@@ -59,6 +42,24 @@
IsVisible="{Binding #ShowGDP.IsChecked}"
x:DataType="local:Country" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -71,6 +72,20 @@
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs
index 8790991182..d858e30212 100644
--- a/src/Avalonia.Base/PropertyStore/ValueStore.cs
+++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs
@@ -610,8 +610,7 @@ namespace Avalonia.PropertyStore
private int InsertFrame(ValueFrame frame)
{
- // Uncomment this line when #8549 is fixed.
- //Debug.Assert(!_frames.Contains(frame));
+ Debug.Assert(!_frames.Contains(frame));
var index = BinarySearchFrame(frame.Priority);
_frames.Insert(index, frame);
diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs
index bba9685ed8..c72f398fd9 100644
--- a/src/Avalonia.Base/StyledElement.cs
+++ b/src/Avalonia.Base/StyledElement.cs
@@ -344,29 +344,30 @@ namespace Avalonia
/// Applies styling to the control if the control is initialized and styling is not
/// already applied.
///
+ ///
+ /// The styling system will automatically apply styling when required, so it should not
+ /// usually be necessary to call this method manually.
+ ///
///
/// A value indicating whether styling is now applied to the control.
///
- protected bool ApplyStyling()
+ public bool ApplyStyling()
{
if (_initCount == 0 && !_styled)
{
- var styler = AvaloniaLocator.Current.GetService();
var hasPromotedTheme = _hasPromotedTheme;
- if (styler is object)
- {
- GetValueStore().BeginStyling();
+ GetValueStore().BeginStyling();
- try
- {
- styler.ApplyStyles(this);
- }
- finally
- {
- _styled = true;
- GetValueStore().EndStyling();
- }
+ try
+ {
+ ApplyControlTheme();
+ ApplyStyles(this);
+ }
+ finally
+ {
+ _styled = true;
+ GetValueStore().EndStyling();
}
if (hasPromotedTheme)
@@ -505,31 +506,6 @@ namespace Avalonia
};
}
- ControlTheme? IStyleable.GetEffectiveTheme()
- {
- var theme = Theme;
-
- // Explitly set Theme property takes precedence.
- if (theme is not null)
- return theme;
-
- // If the Theme property is not set, try to find a ControlTheme resource with our StyleKey.
- if (_implicitTheme is null)
- {
- var key = ((IStyleable)this).StyleKey;
-
- if (this.TryFindResource(key, out var value) && value is ControlTheme t)
- _implicitTheme = t;
- else
- _implicitTheme = s_invalidTheme;
- }
-
- if (_implicitTheme != s_invalidTheme)
- return _implicitTheme;
-
- return null;
- }
-
void IStyleable.DetachStyles() => DetachStyles();
void IStyleHost.StylesAdded(IReadOnlyList styles)
@@ -666,6 +642,31 @@ namespace Avalonia
{
}
+ internal ControlTheme? GetEffectiveTheme()
+ {
+ var theme = Theme;
+
+ // Explitly set Theme property takes precedence.
+ if (theme is not null)
+ return theme;
+
+ // If the Theme property is not set, try to find a ControlTheme resource with our StyleKey.
+ if (_implicitTheme is null)
+ {
+ var key = ((IStyleable)this).StyleKey;
+
+ if (this.TryFindResource(key, out var value) && value is ControlTheme t)
+ _implicitTheme = t;
+ else
+ _implicitTheme = s_invalidTheme;
+ }
+
+ if (_implicitTheme != s_invalidTheme)
+ return _implicitTheme;
+
+ return null;
+ }
+
private static void DataContextNotifying(IAvaloniaObject o, bool updateStarted)
{
if (o is StyledElement element)
@@ -730,6 +731,56 @@ namespace Avalonia
}
}
+ private void ApplyControlTheme()
+ {
+ var theme = GetEffectiveTheme();
+
+ if (theme is not null)
+ ApplyControlTheme(theme);
+
+ if (TemplatedParent is StyledElement styleableParent &&
+ styleableParent.GetEffectiveTheme() is { } parentTheme)
+ {
+ ApplyControlTheme(parentTheme);
+ }
+ }
+
+ private void ApplyControlTheme(ControlTheme theme)
+ {
+ if (theme.BasedOn is ControlTheme basedOn)
+ ApplyControlTheme(basedOn);
+
+ theme.TryAttach(this, null);
+
+ if (theme.HasChildren)
+ {
+ foreach (var child in theme.Children)
+ ApplyStyle(child, null);
+ }
+ }
+
+ private void ApplyStyles(IStyleHost host)
+ {
+ var parent = host.StylingParent;
+ if (parent != null)
+ ApplyStyles(parent);
+
+ if (host.IsStylesInitialized)
+ {
+ foreach (var style in host.Styles)
+ ApplyStyle(style, host);
+ }
+ }
+
+ private void ApplyStyle(IStyle style, IStyleHost? host)
+ {
+ if (style is Style s)
+ s.TryAttach(this, host);
+
+ foreach (var child in style.Children)
+ ApplyStyle(child, host);
+ }
+
private void OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs e)
{
if (this.GetLogicalParent() == null && !(this is ILogicalRoot))
diff --git a/src/Avalonia.Base/Styling/ControlTheme.cs b/src/Avalonia.Base/Styling/ControlTheme.cs
index 46a3267f70..2971703c95 100644
--- a/src/Avalonia.Base/Styling/ControlTheme.cs
+++ b/src/Avalonia.Base/Styling/ControlTheme.cs
@@ -29,34 +29,27 @@ namespace Avalonia.Styling
///
public ControlTheme? BasedOn { get; set; }
- public override SelectorMatchResult TryAttach(IStyleable target, object? host)
+ public override string ToString() => TargetType?.Name ?? "ControlTheme";
+
+ internal override void SetParent(StyleBase? parent)
+ {
+ throw new InvalidOperationException("ControlThemes cannot be added as a nested style.");
+ }
+
+ internal override SelectorMatchResult TryAttach(IStyleable target, object? host)
{
_ = target ?? throw new ArgumentNullException(nameof(target));
if (TargetType is null)
throw new InvalidOperationException("ControlTheme has no TargetType.");
- var result = BasedOn?.TryAttach(target, host) ?? SelectorMatchResult.NeverThisType;
-
if (HasSettersOrAnimations && TargetType.IsAssignableFrom(target.StyleKey))
{
Attach(target, null);
- result = SelectorMatchResult.AlwaysThisType;
+ return SelectorMatchResult.AlwaysThisType;
}
- var childResult = TryAttachChildren(target, host);
-
- if (childResult > result)
- result = childResult;
-
- return result;
- }
-
- public override string ToString() => TargetType?.Name ?? "ControlTheme";
-
- internal override void SetParent(StyleBase? parent)
- {
- throw new InvalidOperationException("ControlThemes cannot be added as a nested style.");
+ return SelectorMatchResult.NeverThisType;
}
}
}
diff --git a/src/Avalonia.Base/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs
index 417739fb28..2dbaf963ee 100644
--- a/src/Avalonia.Base/Styling/IStyle.cs
+++ b/src/Avalonia.Base/Styling/IStyle.cs
@@ -14,15 +14,5 @@ namespace Avalonia.Styling
/// Gets a collection of child styles.
///
IReadOnlyList Children { get; }
-
- ///
- /// Attaches the style and any child styles to a control if the style's selector matches.
- ///
- /// The control to attach to.
- /// The element that hosts the style.
- ///
- /// A describing how the style matches the control.
- ///
- SelectorMatchResult TryAttach(IStyleable target, object? host);
}
}
diff --git a/src/Avalonia.Base/Styling/IStyleable.cs b/src/Avalonia.Base/Styling/IStyleable.cs
index e94fc5c4e6..dcc3988280 100644
--- a/src/Avalonia.Base/Styling/IStyleable.cs
+++ b/src/Avalonia.Base/Styling/IStyleable.cs
@@ -25,11 +25,6 @@ namespace Avalonia.Styling
///
ITemplatedControl? TemplatedParent { get; }
- ///
- /// Gets the effective theme for the control as used by the syling system.
- ///
- ControlTheme? GetEffectiveTheme();
-
void DetachStyles();
}
}
diff --git a/src/Avalonia.Base/Styling/IStyler.cs b/src/Avalonia.Base/Styling/IStyler.cs
deleted file mode 100644
index d6477d169e..0000000000
--- a/src/Avalonia.Base/Styling/IStyler.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-namespace Avalonia.Styling
-{
- public interface IStyler
- {
- void ApplyStyles(IStyleable control);
- }
-}
diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs
index 913c437bc4..aad91824d3 100644
--- a/src/Avalonia.Base/Styling/Style.cs
+++ b/src/Avalonia.Base/Styling/Style.cs
@@ -1,5 +1,4 @@
using System;
-using Avalonia.PropertyStore;
namespace Avalonia.Styling
{
@@ -35,35 +34,6 @@ namespace Avalonia.Styling
set => _selector = ValidateSelector(value);
}
- public override SelectorMatchResult TryAttach(IStyleable target, object? host)
- {
- _ = target ?? throw new ArgumentNullException(nameof(target));
-
- var result = SelectorMatchResult.NeverThisType;
-
- if (HasSettersOrAnimations)
- {
- var match = Selector?.Match(target, Parent, true) ??
- (target == host ?
- SelectorMatch.AlwaysThisInstance :
- SelectorMatch.NeverThisInstance);
-
- if (match.IsMatch)
- {
- Attach(target, match.Activator);
- }
-
- result = match.Result;
- }
-
- var childResult = TryAttachChildren(target, host);
-
- if (childResult > result)
- result = childResult;
-
- return result;
- }
-
///
/// Returns a string representation of the style.
///
@@ -88,6 +58,30 @@ namespace Avalonia.Styling
base.SetParent(parent);
}
+ internal override SelectorMatchResult TryAttach(IStyleable target, object? host)
+ {
+ _ = target ?? throw new ArgumentNullException(nameof(target));
+
+ var result = SelectorMatchResult.NeverThisType;
+
+ if (HasSettersOrAnimations)
+ {
+ var match = Selector?.Match(target, Parent, true) ??
+ (target == host ?
+ SelectorMatch.AlwaysThisInstance :
+ SelectorMatch.NeverThisInstance);
+
+ if (match.IsMatch)
+ {
+ Attach(target, match.Activator);
+ }
+
+ result = match.Result;
+ }
+
+ return result;
+ }
+
private static Selector? ValidateSelector(Selector? selector)
{
if (selector is TemplateSelector)
diff --git a/src/Avalonia.Base/Styling/StyleBase.cs b/src/Avalonia.Base/Styling/StyleBase.cs
index c914fbf8cc..dba80df2e5 100644
--- a/src/Avalonia.Base/Styling/StyleBase.cs
+++ b/src/Avalonia.Base/Styling/StyleBase.cs
@@ -18,7 +18,6 @@ namespace Avalonia.Styling
private IResourceDictionary? _resources;
private List? _setters;
private List? _animations;
- private StyleCache? _childCache;
private StyleInstance? _sharedInstance;
public IList Children => _children ??= new(this);
@@ -67,6 +66,7 @@ namespace Avalonia.Styling
bool IResourceNode.HasResources => _resources?.Count > 0;
IReadOnlyList IStyle.Children => (IReadOnlyList?)_children ?? Array.Empty();
+ internal bool HasChildren => _children?.Count > 0;
internal bool HasSettersOrAnimations => _setters?.Count > 0 || _animations?.Count > 0;
public void Add(ISetter setter) => Setters.Add(setter);
@@ -74,14 +74,26 @@ namespace Avalonia.Styling
public event EventHandler? OwnerChanged;
- public abstract SelectorMatchResult TryAttach(IStyleable target, object? host);
-
public bool TryGetResource(object key, out object? result)
{
- result = null;
- return _resources?.TryGetResource(key, out result) ?? false;
+ if (_resources is not null && _resources.TryGetResource(key, out result))
+ return true;
+
+ if (_children is not null)
+ {
+ for (var i = 0; i < _children.Count; ++i)
+ {
+ if (_children[i].TryGetResource(key, out result))
+ return true;
+ }
+ }
+
+ result= null;
+ return false;
}
+ internal abstract SelectorMatchResult TryAttach(IStyleable target, object? host);
+
internal ValueFrame Attach(IStyleable target, IStyleActivator? activator)
{
if (target is not AvaloniaObject ao)
@@ -124,14 +136,6 @@ namespace Avalonia.Styling
return instance;
}
- internal SelectorMatchResult TryAttachChildren(IStyleable target, object? host)
- {
- if (_children is null || _children.Count == 0)
- return SelectorMatchResult.NeverThisType;
- _childCache ??= new StyleCache();
- return _childCache.TryAttach(_children, target, host);
- }
-
internal virtual void SetParent(StyleBase? parent) => Parent = parent;
void IResourceProvider.AddOwner(IResourceHost owner)
diff --git a/src/Avalonia.Base/Styling/StyleCache.cs b/src/Avalonia.Base/Styling/StyleCache.cs
deleted file mode 100644
index 81196f6a27..0000000000
--- a/src/Avalonia.Base/Styling/StyleCache.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Avalonia.Styling
-{
- ///
- /// Simple cache for improving performance of applying styles.
- ///
- ///
- /// Maps to a list of styles that are known be be possible
- /// matches.
- ///
- internal class StyleCache : Dictionary?>
- {
- public SelectorMatchResult TryAttach(IList styles, IStyleable target, object? host)
- {
- if (TryGetValue(target.StyleKey, out var cached))
- {
- if (cached is object)
- {
- var result = SelectorMatchResult.NeverThisType;
-
- foreach (var style in cached)
- {
- var childResult = style.TryAttach(target, host);
- if (childResult > result)
- result = childResult;
- }
-
- return result;
- }
- else
- {
- return SelectorMatchResult.NeverThisType;
- }
- }
- else
- {
- List? matches = null;
-
- foreach (var child in styles)
- {
- if (child.TryAttach(target, host) != SelectorMatchResult.NeverThisType)
- {
- matches ??= new List();
- matches.Add(child);
- }
- }
-
- Add(target.StyleKey, matches);
-
- return matches is null ?
- SelectorMatchResult.NeverThisType :
- SelectorMatchResult.AlwaysThisType;
- }
- }
- }
-}
diff --git a/src/Avalonia.Base/Styling/Styler.cs b/src/Avalonia.Base/Styling/Styler.cs
deleted file mode 100644
index ad5c1cd102..0000000000
--- a/src/Avalonia.Base/Styling/Styler.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-
-namespace Avalonia.Styling
-{
- public class Styler : IStyler
- {
- public void ApplyStyles(IStyleable target)
- {
- _ = target ?? throw new ArgumentNullException(nameof(target));
-
- // Apply the control theme.
- target.GetEffectiveTheme()?.TryAttach(target, target);
-
- // If the control has a themed templated parent then apply the styles from the
- // templated parent theme.
- if (target.TemplatedParent is IStyleable styleableParent)
- styleableParent.GetEffectiveTheme()?.TryAttach(target, styleableParent);
-
- // Apply styles from the rest of the tree.
- if (target is IStyleHost styleHost)
- ApplyStyles(target, styleHost);
- }
-
- private void ApplyStyles(IStyleable target, IStyleHost host)
- {
- var parent = host.StylingParent;
-
- if (parent != null)
- ApplyStyles(target, parent);
-
- if (host.IsStylesInitialized)
- host.Styles.TryAttach(target, host);
- }
- }
-}
diff --git a/src/Avalonia.Base/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs
index c213475bb7..76271b9748 100644
--- a/src/Avalonia.Base/Styling/Styles.cs
+++ b/src/Avalonia.Base/Styling/Styles.cs
@@ -5,8 +5,6 @@ using System.Collections.Specialized;
using Avalonia.Collections;
using Avalonia.Controls;
-#nullable enable
-
namespace Avalonia.Styling
{
///
@@ -20,7 +18,6 @@ namespace Avalonia.Styling
private readonly AvaloniaList _styles = new();
private IResourceHost? _owner;
private IResourceDictionary? _resources;
- private StyleCache? _cache;
public Styles()
{
@@ -116,12 +113,6 @@ namespace Avalonia.Styling
set => _styles[index] = value;
}
- public SelectorMatchResult TryAttach(IStyleable target, object? host)
- {
- _cache ??= new StyleCache();
- return _cache.TryAttach(this, target, host);
- }
-
///
public bool TryGetResource(object key, out object? value)
{
@@ -234,6 +225,22 @@ namespace Avalonia.Styling
}
}
+ internal SelectorMatchResult TryAttach(IStyleable target, object? host)
+ {
+ var result = SelectorMatchResult.NeverThisType;
+
+ foreach (var s in this)
+ {
+ if (s is not Style style)
+ continue;
+ var r = style.TryAttach(target, host);
+ if (r > result)
+ result = r;
+ }
+
+ return result;
+ }
+
private static IReadOnlyList ToReadOnlyList(ICollection list)
{
if (list is IReadOnlyList readOnlyList)
@@ -246,7 +253,7 @@ namespace Avalonia.Styling
return result;
}
- private static void InternalAdd(IList items, IResourceHost? owner, ref StyleCache? cache)
+ private static void InternalAdd(IList items, IResourceHost? owner)
{
if (owner is not null)
{
@@ -260,14 +267,9 @@ namespace Avalonia.Styling
(owner as IStyleHost)?.StylesAdded(ToReadOnlyList(items));
}
-
- if (items.Count > 0)
- {
- cache = null;
- }
}
- private static void InternalRemove(IList items, IResourceHost? owner, ref StyleCache? cache)
+ private static void InternalRemove(IList items, IResourceHost? owner)
{
if (owner is not null)
{
@@ -281,11 +283,6 @@ namespace Avalonia.Styling
(owner as IStyleHost)?.StylesRemoved(ToReadOnlyList(items));
}
-
- if (items.Count > 0)
- {
- cache = null;
- }
}
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
@@ -300,14 +297,14 @@ namespace Avalonia.Styling
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
- InternalAdd(e.NewItems!, currentOwner, ref _cache);
+ InternalAdd(e.NewItems!, currentOwner);
break;
case NotifyCollectionChangedAction.Remove:
- InternalRemove(e.OldItems!, currentOwner, ref _cache);
+ InternalRemove(e.OldItems!, currentOwner);
break;
case NotifyCollectionChangedAction.Replace:
- InternalRemove(e.OldItems!, currentOwner, ref _cache);
- InternalAdd(e.NewItems!, currentOwner, ref _cache);
+ InternalRemove(e.OldItems!, currentOwner);
+ InternalAdd(e.NewItems!, currentOwner);
break;
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index c6cc9bf278..a9f2e889b9 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -26,6 +26,7 @@ using Avalonia.Controls.Utils;
using Avalonia.Layout;
using Avalonia.Controls.Metadata;
using Avalonia.Input.GestureRecognizers;
+using Avalonia.Styling;
namespace Avalonia.Controls
{
@@ -238,29 +239,72 @@ namespace Avalonia.Controls
AvaloniaProperty.Register(nameof(ColumnWidth), defaultValue: DataGridLength.Auto);
///
- /// Gets or sets the standard width or automatic sizing mode of columns in the control.
+ /// Identifies the dependency property.
///
- public DataGridLength ColumnWidth
+ public static readonly StyledProperty RowThemeProperty =
+ AvaloniaProperty.Register(nameof(RowTheme));
+
+ ///
+ /// Gets or sets the theme applied to all rows.
+ ///
+ public ControlTheme RowTheme
{
- get { return GetValue(ColumnWidthProperty); }
- set { SetValue(ColumnWidthProperty, value); }
+ get { return GetValue(RowThemeProperty); }
+ set { SetValue(RowThemeProperty, value); }
}
- public static readonly StyledProperty AlternatingRowBackgroundProperty =
- AvaloniaProperty.Register(nameof(AlternatingRowBackground));
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly StyledProperty CellThemeProperty =
+ AvaloniaProperty.Register(nameof(CellTheme));
///
- /// Gets or sets the that is used to paint the background of odd-numbered rows.
+ /// Gets or sets the theme applied to all cells.
///
- ///
- /// The brush that is used to paint the background of odd-numbered rows. The default is a
- /// with a
- /// value of white (ARGB value #00FFFFFF).
- ///
- public IBrush AlternatingRowBackground
+ public ControlTheme CellTheme
+ {
+ get { return GetValue(CellThemeProperty); }
+ set { SetValue(CellThemeProperty, value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly StyledProperty ColumnHeaderThemeProperty =
+ AvaloniaProperty.Register(nameof(ColumnHeaderTheme));
+
+ ///
+ /// Gets or sets the theme applied to all column headers.
+ ///
+ public ControlTheme ColumnHeaderTheme
+ {
+ get { return GetValue(ColumnHeaderThemeProperty); }
+ set { SetValue(ColumnHeaderThemeProperty, value); }
+ }
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ public static readonly StyledProperty RowGroupThemeProperty =
+ AvaloniaProperty.Register(nameof(RowGroupTheme));
+
+ ///
+ /// Gets or sets the theme applied to all row groups.
+ ///
+ public ControlTheme RowGroupTheme
{
- get { return GetValue(AlternatingRowBackgroundProperty); }
- set { SetValue(AlternatingRowBackgroundProperty, value); }
+ get { return GetValue(RowGroupThemeProperty); }
+ set { SetValue(RowGroupThemeProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the standard width or automatic sizing mode of columns in the control.
+ ///
+ public DataGridLength ColumnWidth
+ {
+ get { return GetValue(ColumnWidthProperty); }
+ set { SetValue(ColumnWidthProperty, value); }
}
public static readonly StyledProperty FrozenColumnCountProperty =
@@ -2058,7 +2102,7 @@ namespace Avalonia.Controls
forceHorizontalScroll: true);
}
}
-
+
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
@@ -2167,7 +2211,7 @@ namespace Avalonia.Controls
return desiredSize;
}
-
+
///
protected override void OnDataContextBeginUpdate()
{
@@ -2183,7 +2227,7 @@ namespace Avalonia.Controls
NotifyDataContextPropertyForAllRowCells(GetAllRows(), false);
}
-
+
///
/// Raises the BeginningEdit event.
///
@@ -3242,7 +3286,6 @@ namespace Avalonia.Controls
}
}
- //TODO Styles
private void AddNewCellPrivate(DataGridRow row, DataGridColumn column)
{
DataGridCell newCell = new DataGridCell();
@@ -3255,8 +3298,11 @@ namespace Avalonia.Controls
{
newCell.OwningColumn = column;
newCell.IsVisible = column.IsVisible;
+ if (row.OwningGrid.CellTheme is {} cellTheme)
+ {
+ newCell.SetValue(ThemeProperty, cellTheme, BindingPriority.TemplatedParent);
+ }
}
- //newCell.EnsureStyle(null);
row.Cells.Insert(column.Index, newCell);
}
@@ -4537,7 +4583,6 @@ namespace Avalonia.Controls
FlushCurrentCellChanged();
}
- //TODO Styles
private void PopulateCellContent(bool isCellEdited,
DataGridColumn dataGridColumn,
DataGridRow dataGridRow,
@@ -4575,7 +4620,7 @@ namespace Avalonia.Controls
dataGridCell.Content = element;
}
-
+
}
private void PreparingCellForEditPrivate(Control editingElement)
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs
index fbdb979e24..191c3fc5b1 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs
@@ -231,7 +231,7 @@ namespace Avalonia.Controls
}
///
- /// Gets or sets a value that indicates whether the user can change the column display position by
+ /// Gets or sets a value that indicates whether the user can change the column display position by
/// dragging the column header.
///
///
@@ -260,15 +260,15 @@ namespace Avalonia.Controls
///
public bool CanUserResize
{
- get
+ get
{
return
CanUserResizeInternal ??
OwningGrid?.CanUserResizeColumns ??
DataGrid.DATAGRID_defaultCanUserResizeColumns;
}
- set
- {
+ set
+ {
CanUserResizeInternal = value;
OwningGrid?.OnColumnCanUserResizeChanged(this);
}
@@ -321,16 +321,16 @@ namespace Avalonia.Controls
///
///
/// When setting this property, the specified value is less than -1 or equal to .
- ///
+ ///
/// -or-
- ///
+ ///
/// When setting this property on a column in a , the specified value is less than zero or greater than or equal to the number of columns in the .
///
///
/// When setting this property, the is already making adjustments. For example, this exception is thrown when you attempt to set in a event handler.
- ///
+ ///
/// -or-
- ///
+ ///
/// When setting this property, the specified value would result in a frozen column being displayed in the range of unfrozen columns, or an unfrozen column being displayed in the range of frozen columns.
///
public int DisplayIndex
@@ -401,7 +401,7 @@ namespace Avalonia.Controls
}
}
}
-
+
///
/// Backing field for CellTheme property.
///
@@ -412,7 +412,7 @@ namespace Avalonia.Controls
(o, v) => o.CellTheme = v);
///
- /// Gets or sets the cell theme.
+ /// Gets or sets the cell theme.
///
public ControlTheme CellTheme
{
@@ -430,14 +430,14 @@ namespace Avalonia.Controls
(o, v) => o.Header = v);
///
- /// Gets or sets the content
+ /// Gets or sets the content
///
public object Header
{
get { return _header; }
set { SetAndRaise(HeaderProperty, ref _header, value); }
}
-
+
///
/// Backing field for Header property
///
@@ -455,7 +455,7 @@ namespace Avalonia.Controls
get { return _headerTemplate; }
set { SetAndRaise(HeaderTemplateProperty, ref _headerTemplate, value); }
}
-
+
public bool IsAutoGenerated
{
get;
@@ -750,7 +750,7 @@ namespace Avalonia.Controls
protected abstract IControl GenerateEditingElement(DataGridCell cell, object dataItem, out ICellEditBinding binding);
///
- /// When overridden in a derived class, gets a read-only element that is bound to the column's
+ /// When overridden in a derived class, gets a read-only element that is bound to the column's
/// property value.
///
///
@@ -765,7 +765,7 @@ namespace Avalonia.Controls
protected abstract IControl GenerateElement(DataGridCell cell, object dataItem);
///
- /// Called by a specific column type when one of its properties changed,
+ /// Called by a specific column type when one of its properties changed,
/// and its current cells need to be updated.
///
/// Indicates which property changed and caused this call
@@ -882,9 +882,8 @@ namespace Avalonia.Controls
{
LayoutRoundedWidth = ActualWidth;
}
- }
+ }
- //TODO Styles
internal virtual DataGridColumnHeader CreateHeader()
{
var result = new DataGridColumnHeader
@@ -893,8 +892,10 @@ namespace Avalonia.Controls
};
result[!ContentControl.ContentProperty] = this[!HeaderProperty];
result[!ContentControl.ContentTemplateProperty] = this[!HeaderTemplateProperty];
-
- //result.EnsureStyle(null);
+ if (OwningGrid.ColumnHeaderTheme is {} columnTheme)
+ {
+ result.SetValue(StyledElement.ThemeProperty, columnTheme, BindingPriority.TemplatedParent);
+ }
return result;
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
index d3bd968d62..740e3516f6 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
@@ -76,7 +76,7 @@ namespace Avalonia.Controls
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
//TODO Implement
public DataGridColumnHeader()
@@ -267,7 +267,7 @@ namespace Avalonia.Controls
else
{
newSort = sort;
- }
+ }
// changing direction should not affect sort order, so we replace this column's
// sort description instead of just adding it to the end of the collection
@@ -603,7 +603,7 @@ namespace Avalonia.Controls
}
///
- /// Returns true if the mouse is
+ /// Returns true if the mouse is
/// - to the left of the element, or within the left half of the element
/// and
/// - within the vertical range of the element, or ignoreVertical == true
@@ -663,16 +663,19 @@ namespace Avalonia.Controls
IsMouseOver = false;
}
- //TODO Styles DragIndicator
private void OnMouseMove_BeginReorder(Point mousePosition)
{
- DataGridColumnHeader dragIndicator = new DataGridColumnHeader
+ var dragIndicator = new DataGridColumnHeader
{
OwningColumn = OwningColumn,
IsEnabled = false,
Content = Content,
ContentTemplate = ContentTemplate
};
+ if (OwningGrid.ColumnHeaderTheme is {} columnHeaderTheme)
+ {
+ dragIndicator.SetValue(ThemeProperty, columnHeaderTheme, BindingPriority.TemplatedParent);
+ }
dragIndicator.PseudoClasses.Add(":dragIndicator");
@@ -720,7 +723,7 @@ namespace Avalonia.Controls
{
return;
}
-
+
//handle entry into reorder mode
if (_dragMode == DragMode.MouseDown && _dragColumn == null && _lastMousePositionHeaders != null && (distanceFromRight > DATAGRIDCOLUMNHEADER_resizeRegionWidth && distanceFromLeft > DATAGRIDCOLUMNHEADER_resizeRegionWidth))
{
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
index 1559763a1b..cd22934ac0 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
@@ -89,7 +89,7 @@ namespace Avalonia.Controls
o => o.IsValid);
///
- /// Gets a value that indicates whether the data in a row is valid.
+ /// Gets a value that indicates whether the data in a row is valid.
///
public bool IsValid
{
@@ -130,7 +130,7 @@ namespace Avalonia.Controls
}
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
public DataGridRow()
{
@@ -240,7 +240,6 @@ namespace Avalonia.Controls
private set;
}
- //TODO Styles
internal DataGridCell FillerCell
{
get
@@ -252,7 +251,10 @@ namespace Avalonia.Controls
IsVisible = false,
OwningRow = this
};
- //_fillerCell.EnsureStyle(null);
+ if (OwningGrid.CellTheme is {} cellTheme)
+ {
+ _fillerCell.SetValue(ThemeProperty, cellTheme, BindingPriority.TemplatedParent);
+ }
if (_cellsElement != null)
{
_cellsElement.Children.Add(_fillerCell);
@@ -506,7 +508,7 @@ namespace Avalonia.Controls
}
///
- /// Measures the children of a to
+ /// Measures the children of a to
/// prepare for arranging them during the pass.
///
///
@@ -709,8 +711,6 @@ namespace Avalonia.Controls
}
}
- // Set the proper style for the Header by walking up the Style hierarchy
- //TODO Styles
internal void EnsureHeaderStyleAndVisibility(Styling.Style previousStyle)
{
if (_headerElement != null && OwningGrid != null)
@@ -785,7 +785,7 @@ namespace Avalonia.Controls
OwningGrid?.OnRowDetailsChanged();
}
- // Returns the actual template that should be sued for Details: either explicity set on this row
+ // Returns the actual template that should be sued for Details: either explicity set on this row
// or inherited from the DataGrid
private IDataTemplate ActualDetailsTemplate
{
@@ -890,7 +890,7 @@ namespace Avalonia.Controls
//TODO Cleanup
double? _previousDetailsHeight = null;
- //TODO Animation
+ //TODO Animation
private void DetailsContent_HeightChanged(double newValue)
{
if (_previousDetailsHeight.HasValue)
@@ -907,7 +907,7 @@ namespace Avalonia.Controls
_detailsElement.ContentHeight = newValue;
- // Calling this when details are not visible invalidates during layout when we have no work
+ // Calling this when details are not visible invalidates during layout when we have no work
// to do. In certain scenarios, this could cause a layout cycle
OnRowDetailsChanged();
}
@@ -1060,7 +1060,7 @@ namespace Avalonia.Controls
}
}
}
-
+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
@@ -1084,7 +1084,4 @@ namespace Avalonia.Controls
}
}
-
- //TODO Styles
-
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
index 69e6766bfd..c746b19cc7 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
@@ -53,7 +53,7 @@ namespace Avalonia.Controls
AvaloniaProperty.Register(nameof(PropertyName));
///
- /// Gets or sets the name of the property that this row is bound to.
+ /// Gets or sets the name of the property that this row is bound to.
///
public string PropertyName
{
@@ -85,8 +85,8 @@ namespace Avalonia.Controls
}
///
- /// Gets or sets a value that indicates the amount that the
- /// children of the are indented.
+ /// Gets or sets a value that indicates the amount that the
+ /// children of the are indented.
///
public double SublevelIndent
{
@@ -327,9 +327,9 @@ namespace Avalonia.Controls
{
double xClip = Math.Round(frozenLeftEdge - childLeftEdge);
var rg = new RectangleGeometry();
- rg.Rect =
- new Rect(xClip, 0,
- Math.Max(0, child.Bounds.Width - xClip),
+ rg.Rect =
+ new Rect(xClip, 0,
+ Math.Max(0, child.Bounds.Width - xClip),
child.Bounds.Height);
child.Clip = rg;
}
@@ -348,8 +348,6 @@ namespace Avalonia.Controls
}
}
- //TODO Styles
- //internal void EnsureHeaderStyleAndVisibility(Style previousStyle)
internal void EnsureHeaderVisibility()
{
if (_headerElement != null && OwningGrid != null)
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs
index f3afe2c42d..17a7eab2e0 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs
@@ -14,6 +14,9 @@ using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
+using Avalonia.Data;
+using Avalonia.Styling;
+using JetBrains.Annotations;
namespace Avalonia.Controls
{
@@ -117,7 +120,7 @@ namespace Avalonia.Controls
detailsCount += GetDetailsCountInclusive(DisplayData.LastScrollingSlot + 1, SlotCount - 1);
}
- //
+ //
double totalDetailsHeight = detailsCount * RowDetailsHeightEstimate;
return totalRowsHeight + totalDetailsHeight;
@@ -163,7 +166,7 @@ namespace Avalonia.Controls
}
///
- /// Clears the entire selection except the indicated row. Displayed rows are deselected explicitly to
+ /// Clears the entire selection except the indicated row. Displayed rows are deselected explicitly to
/// visualize potential transition effects. The row indicated is selected if it is not already.
///
internal void ClearRowSelection(int slotException, bool setAnchorSlot)
@@ -270,10 +273,10 @@ namespace Avalonia.Controls
bool isRow = rowIndex != -1;
if (isCollapsed)
{
- InsertElement(slot,
- element: null,
+ InsertElement(slot,
+ element: null,
updateVerticalScrollBarOnly: true,
- isCollapsed: true,
+ isCollapsed: true,
isRow: isRow);
}
else if (SlotIsDisplayed(slot))
@@ -285,18 +288,18 @@ namespace Avalonia.Controls
}
else
{
- InsertElement(slot, GenerateRowGroupHeader(slot, groupInfo),
+ InsertElement(slot, GenerateRowGroupHeader(slot, groupInfo),
updateVerticalScrollBarOnly: false,
- isCollapsed: false,
+ isCollapsed: false,
isRow: isRow);
}
}
else
{
- InsertElement(slot,
+ InsertElement(slot,
element: null,
updateVerticalScrollBarOnly: _vScrollBar == null || _vScrollBar.IsVisible,
- isCollapsed: false,
+ isCollapsed: false,
isRow: isRow);
}
}
@@ -417,7 +420,7 @@ namespace Avalonia.Controls
if (scrolledHorizontally && DisplayData.FirstScrollingSlot <= slot && DisplayData.LastScrollingSlot >= slot)
{
// If the slot is displayed and we scrolled horizontally, column virtualization could cause the rows to grow.
- // As a result we need to force measure on the rows we're displaying and recalculate our First and Last slots
+ // As a result we need to force measure on the rows we're displaying and recalculate our First and Last slots
// so they're accurate
foreach (DataGridRow row in DisplayData.GetScrollingRows())
{
@@ -455,7 +458,7 @@ namespace Avalonia.Controls
deltaY -= GetSlotElementsHeight(slot, firstFullSlot);
if (DisplayData.FirstScrollingSlot - slot > 1)
{
- //
+ //
ResetDisplayedRows();
}
@@ -519,7 +522,7 @@ namespace Avalonia.Controls
_verticalOffset = NegVerticalOffset;
}
- //
+ //
Debug.Assert(MathUtilities.LessThanOrClose(NegVerticalOffset, _verticalOffset));
SetVerticalOffset(_verticalOffset);
@@ -1025,6 +1028,10 @@ namespace Avalonia.Controls
dataGridRow.Slot = slot;
dataGridRow.OwningGrid = this;
dataGridRow.DataContext = dataContext;
+ if (RowTheme is {} rowTheme)
+ {
+ dataGridRow.SetValue(ThemeProperty, rowTheme, BindingPriority.TemplatedParent);
+ }
CompleteCellsCollection(dataGridRow);
OnLoadingRow(new DataGridRowEventArgs(dataGridRow));
@@ -1104,7 +1111,7 @@ namespace Avalonia.Controls
///
/// Checks if the row for the provided dataContext has been generated and is present
- /// in either the loaded rows, pre-fetched rows, or editing row.
+ /// in either the loaded rows, pre-fetched rows, or editing row.
/// The displayed rows are *not* searched. Returns null if the row does not belong to those 3 categories.
///
private DataGridRow GetGeneratedRow(object dataContext)
@@ -1152,8 +1159,8 @@ namespace Avalonia.Controls
}
else
{
- // If we're grouping, the GroupLevel needs to be fixed later by methods calling this
- // which end up inserting rows. We don't do it here because elements could be inserted
+ // If we're grouping, the GroupLevel needs to be fixed later by methods calling this
+ // which end up inserting rows. We don't do it here because elements could be inserted
// from top to bottom or bottom to up so it's better to do in one pass
slotElement = GenerateRow(RowIndexFromSlot(slot), slot);
}
@@ -1161,7 +1168,6 @@ namespace Avalonia.Controls
return slotElement;
}
- //TODO Styles
private void InsertDisplayedElement(int slot, Control element, bool wasNewlyAdded, bool updateSlotInformation)
{
// We can only support creating new rows that are adjacent to the currently visible rows
@@ -1479,7 +1485,6 @@ namespace Avalonia.Controls
}
}
- //TODO Styles
// Makes sure the row shows the proper visuals for selection, currency, details, etc.
private void LoadRowVisualsForDisplay(DataGridRow row)
{
@@ -1694,7 +1699,7 @@ namespace Avalonia.Controls
{
// Figure out what row we've scrolled down to and update the value for NegVerticalOffset
NegVerticalOffset = 0;
- //
+ //
if (height > 2 * CellsHeight &&
(RowDetailsVisibilityMode != DataGridRowDetailsVisibilityMode.VisibleWhenSelected || RowDetailsTemplate == null))
{
@@ -1755,7 +1760,7 @@ namespace Avalonia.Controls
// Figure out what row we've scrolled up to and update the value for NegVerticalOffset
deltaY = -NegVerticalOffset;
NegVerticalOffset = 0;
- //
+ //
if (height < -2 * CellsHeight &&
(RowDetailsVisibilityMode != DataGridRowDetailsVisibilityMode.VisibleWhenSelected || RowDetailsTemplate == null))
@@ -1813,7 +1818,7 @@ namespace Avalonia.Controls
if (MathUtilities.GreaterThanOrClose(0, newVerticalOffset) && newFirstScrollingSlot != 0)
{
// We've scrolled to the top of the ScrollBar, automatically place the user at the very top
- // of the DataGrid. If this produces very odd behavior, evaluate the RowHeight estimate.
+ // of the DataGrid. If this produces very odd behavior, evaluate the RowHeight estimate.
// strategy. For most data, this should be unnoticeable.
ResetDisplayedRows();
NegVerticalOffset = 0;
@@ -1994,7 +1999,6 @@ namespace Avalonia.Controls
VisibleSlotCount = 0;
}
- //TODO Styles
private void UnloadRow(DataGridRow dataGridRow)
{
Debug.Assert(dataGridRow != null);
@@ -2010,16 +2014,13 @@ namespace Avalonia.Controls
OnUnloadingRow(new DataGridRowEventArgs(dataGridRow));
bool recycleRow = CurrentSlot != dataGridRow.Index;
- // Don't recycle if the row has a custom Style set
- //recycleRow &= (dataGridRow.Style == null || dataGridRow.Style == RowStyle);
-
if (recycleRow)
{
DisplayData.AddRecyclableRow(dataGridRow);
}
else
{
- //
+ //
_rowsPresenter.Children.Remove(dataGridRow);
dataGridRow.DetachFromDataGrid(false);
}
@@ -2240,10 +2241,10 @@ namespace Avalonia.Controls
group.Items.CollectionChanged += CollectionViewGroup_CollectionChanged;
}
var newGroupInfo = new DataGridRowGroupInfo(group, true, parentGroupInfo.Level + 1, insertSlot, insertSlot);
- InsertElementAt(insertSlot,
- rowIndex: -1,
- item: null,
- groupInfo: newGroupInfo,
+ InsertElementAt(insertSlot,
+ rowIndex: -1,
+ item: null,
+ groupInfo: newGroupInfo,
isCollapsed: isCollapsed);
RowGroupHeadersTable.AddValue(insertSlot, newGroupInfo);
}
@@ -2256,9 +2257,9 @@ namespace Avalonia.Controls
{
AutoGenerateColumnsPrivate();
}
- InsertElementAt(insertSlot, rowIndex,
+ InsertElementAt(insertSlot, rowIndex,
item: e.NewItems[0],
- groupInfo: null,
+ groupInfo: null,
isCollapsed: isCollapsed);
}
@@ -2448,13 +2449,12 @@ namespace Avalonia.Controls
VisibleSlotCount = SlotCount;
}
- //TODO Styles
private void RefreshRowGroupHeaders()
{
if (DataConnection.CollectionView != null
&& DataConnection.CollectionView.CanGroup
&& DataConnection.CollectionView.Groups != null
- && DataConnection.CollectionView.IsGrouping
+ && DataConnection.CollectionView.IsGrouping
&& DataConnection.CollectionView.GroupingDepth > 0)
{
// Initialize our array for the height of the RowGroupHeaders by Level.
@@ -2476,7 +2476,7 @@ namespace Avalonia.Controls
double indent;
for (int i = 0; i < groupLevelCount; i++)
{
- indent = DATAGRID_defaultRowGroupSublevelIndent;
+ indent = DATAGRID_defaultRowGroupSublevelIndent;
RowGroupSublevelIndents[i] = indent;
if (i > 0)
{
@@ -2742,6 +2742,10 @@ namespace Avalonia.Controls
groupHeader.RowGroupInfo = rowGroupInfo;
groupHeader.DataContext = rowGroupInfo.CollectionViewGroup;
groupHeader.Level = rowGroupInfo.Level;
+ if (RowGroupTheme is {} rowGroupTheme)
+ {
+ groupHeader.SetValue(ThemeProperty, rowGroupTheme, BindingPriority.TemplatedParent);
+ }
// Set the RowGroupHeader's PropertyName. Unfortunately, CollectionViewGroup doesn't have this
// so we have to set it manually
diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
index 3d98722da5..a3c0ed1d0c 100644
--- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
+++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
@@ -232,7 +232,7 @@
-
+
@@ -337,10 +337,6 @@
-
-
-