Browse Source

Merge branch 'master' into fixes/ncrunch

pull/9498/head
Max Katz 3 years ago
committed by GitHub
parent
commit
1c2f06d4b9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 51
      samples/ControlCatalog/Pages/DataGridPage.xaml
  2. 3
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  3. 129
      src/Avalonia.Base/StyledElement.cs
  4. 27
      src/Avalonia.Base/Styling/ControlTheme.cs
  5. 10
      src/Avalonia.Base/Styling/IStyle.cs
  6. 5
      src/Avalonia.Base/Styling/IStyleable.cs
  7. 8
      src/Avalonia.Base/Styling/IStyler.cs
  8. 54
      src/Avalonia.Base/Styling/Style.cs
  9. 30
      src/Avalonia.Base/Styling/StyleBase.cs
  10. 58
      src/Avalonia.Base/Styling/StyleCache.cs
  11. 35
      src/Avalonia.Base/Styling/Styler.cs
  12. 47
      src/Avalonia.Base/Styling/Styles.cs
  13. 89
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  14. 39
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  15. 15
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  16. 25
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  17. 14
      src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
  18. 72
      src/Avalonia.Controls.DataGrid/DataGridRows.cs
  19. 7
      src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
  20. 5
      src/Avalonia.Controls.DataGrid/Themes/Simple.xaml
  21. 2
      src/Avalonia.Controls/Application.cs
  22. 3
      src/Avalonia.Controls/TopLevel.cs
  23. 2
      src/Avalonia.Controls/Window.cs
  24. 1
      src/Avalonia.Controls/WindowBase.cs
  25. 2
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  26. 2
      tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs
  27. 194
      tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs
  28. 78
      tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs
  29. 21
      tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs
  30. 1
      tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs
  31. 47
      tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs
  32. 126
      tests/Avalonia.Benchmarks/Styling/Style_Apply_Detach_Complex.cs
  33. 1
      tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs
  34. 1
      tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
  35. 63
      tests/Avalonia.Controls.UnitTests/ContentControlTests.cs
  36. 10
      tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
  37. 4
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  38. 3
      tests/Avalonia.Controls.UnitTests/MenuItemTests.cs
  39. 1
      tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs
  40. 2
      tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs
  41. 2
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  42. 12
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  43. 249
      tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs
  44. 28
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  45. 8
      tests/Avalonia.Controls.UnitTests/ToolTipTests.cs
  46. 2
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
  47. 25
      tests/Avalonia.Controls.UnitTests/UserControlTests.cs
  48. 21
      tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs
  49. 6
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  50. 7
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  51. 14
      tests/Avalonia.UnitTests/TestServices.cs
  52. 5
      tests/Avalonia.UnitTests/UnitTestApplication.cs

51
samples/ControlCatalog/Pages/DataGridPage.xaml

@ -14,22 +14,6 @@
<Setter Property="Background" Value="{Binding Path=GDP, Mode=OneWay, Converter={StaticResource GDPConverter}}" />
</ControlTheme>
</UserControl.Resources>
<UserControl.Styles>
<Style Selector="DataGridColumnHeader:nth-last-child(1)">
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="DataGridCell:nth-last-child(1)">
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="DataGrid#dataGridGrouping DataGridRow:nth-child(5n+3)">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="DataGrid#dataGridGrouping DataGridRow:nth-last-child(5n+1)">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</UserControl.Styles>
<Grid RowDefinitions="Auto,Auto,*">
<StackPanel Orientation="Vertical" Spacing="4" Grid.Row="0">
<TextBlock Classes="h2">A control for displaying and interacting with a data source.</TextBlock>
@ -45,8 +29,7 @@
<CheckBox x:Name="ShowGDP" IsChecked="True" Content="Toggle GDP Column Visibility"
DockPanel.Dock="Top"/>
<DataGrid Name="dataGrid1" Margin="12" CanUserResizeColumns="True" CanUserReorderColumns="True" CanUserSortColumns="True" HeadersVisibility="All"
RowBackground="#1000"
AlternatingRowBackground="#1fff">
RowBackground="#1000">
<DataGrid.Columns>
<!-- Using HeaderTemplate -->
<DataGridTextColumn Header="Country" HeaderTemplate="{StaticResource Demo.DataTemplates.CountryHeader}" Binding="{Binding Name}" Width="6*" x:DataType="local:Country" />
@ -59,6 +42,24 @@
IsVisible="{Binding #ShowGDP.IsChecked}"
x:DataType="local:Country" />
</DataGrid.Columns>
<DataGrid.CellTheme>
<ControlTheme TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<ControlTheme.Children>
<Style Selector="^:nth-child(1)">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</ControlTheme.Children>
</ControlTheme>
</DataGrid.CellTheme>
<DataGrid.ColumnHeaderTheme>
<ControlTheme TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<ControlTheme.Children>
<Style Selector="^:nth-child(1)">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</ControlTheme.Children>
</ControlTheme>
</DataGrid.ColumnHeaderTheme>
</DataGrid>
</DockPanel>
</TabItem>
@ -71,6 +72,20 @@
<DataGridTextColumn DisplayIndex="2" Header="Area" Binding="{Binding Area}" Width="3*" x:DataType="local:Country" />
<DataGridTextColumn Header="GDP" Binding="{Binding GDP}" Width="3*" x:DataType="local:Country" />
</DataGrid.Columns>
<DataGrid.RowTheme>
<ControlTheme TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<ControlTheme.Children>
<Style Selector="^:nth-child(5n+3)">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="^:nth-last-child(5n+1)">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</ControlTheme.Children>
</ControlTheme>
</DataGrid.RowTheme>
</DataGrid>
</TabItem>
<TabItem x:Name="EditableTab" Header="Editable">

3
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);

129
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.
/// </summary>
/// <remarks>
/// The styling system will automatically apply styling when required, so it should not
/// usually be necessary to call this method manually.
/// </remarks>
/// <returns>
/// A value indicating whether styling is now applied to the control.
/// </returns>
protected bool ApplyStyling()
public bool ApplyStyling()
{
if (_initCount == 0 && !_styled)
{
var styler = AvaloniaLocator.Current.GetService<IStyler>();
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<IStyle> 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))

27
src/Avalonia.Base/Styling/ControlTheme.cs

@ -29,34 +29,27 @@ namespace Avalonia.Styling
/// </summary>
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;
}
}
}

10
src/Avalonia.Base/Styling/IStyle.cs

@ -14,15 +14,5 @@ namespace Avalonia.Styling
/// Gets a collection of child styles.
/// </summary>
IReadOnlyList<IStyle> Children { get; }
/// <summary>
/// Attaches the style and any child styles to a control if the style's selector matches.
/// </summary>
/// <param name="target">The control to attach to.</param>
/// <param name="host">The element that hosts the style.</param>
/// <returns>
/// A <see cref="SelectorMatchResult"/> describing how the style matches the control.
/// </returns>
SelectorMatchResult TryAttach(IStyleable target, object? host);
}
}

5
src/Avalonia.Base/Styling/IStyleable.cs

@ -25,11 +25,6 @@ namespace Avalonia.Styling
/// </summary>
ITemplatedControl? TemplatedParent { get; }
/// <summary>
/// Gets the effective theme for the control as used by the syling system.
/// </summary>
ControlTheme? GetEffectiveTheme();
void DetachStyles();
}
}

8
src/Avalonia.Base/Styling/IStyler.cs

@ -1,8 +0,0 @@
namespace Avalonia.Styling
{
public interface IStyler
{
void ApplyStyles(IStyleable control);
}
}

54
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;
}
/// <summary>
/// Returns a string representation of the style.
/// </summary>
@ -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)

30
src/Avalonia.Base/Styling/StyleBase.cs

@ -18,7 +18,6 @@ namespace Avalonia.Styling
private IResourceDictionary? _resources;
private List<ISetter>? _setters;
private List<IAnimation>? _animations;
private StyleCache? _childCache;
private StyleInstance? _sharedInstance;
public IList<IStyle> Children => _children ??= new(this);
@ -67,6 +66,7 @@ namespace Avalonia.Styling
bool IResourceNode.HasResources => _resources?.Count > 0;
IReadOnlyList<IStyle> IStyle.Children => (IReadOnlyList<IStyle>?)_children ?? Array.Empty<IStyle>();
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)

58
src/Avalonia.Base/Styling/StyleCache.cs

@ -1,58 +0,0 @@
using System;
using System.Collections.Generic;
namespace Avalonia.Styling
{
/// <summary>
/// Simple cache for improving performance of applying styles.
/// </summary>
/// <remarks>
/// Maps <see cref="IStyleable.StyleKey"/> to a list of styles that are known be be possible
/// matches.
/// </remarks>
internal class StyleCache : Dictionary<Type, List<IStyle>?>
{
public SelectorMatchResult TryAttach(IList<IStyle> 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<IStyle>? matches = null;
foreach (var child in styles)
{
if (child.TryAttach(target, host) != SelectorMatchResult.NeverThisType)
{
matches ??= new List<IStyle>();
matches.Add(child);
}
}
Add(target.StyleKey, matches);
return matches is null ?
SelectorMatchResult.NeverThisType :
SelectorMatchResult.AlwaysThisType;
}
}
}
}

35
src/Avalonia.Base/Styling/Styler.cs

@ -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);
}
}
}

47
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
{
/// <summary>
@ -20,7 +18,6 @@ namespace Avalonia.Styling
private readonly AvaloniaList<IStyle> _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);
}
/// <inheritdoc/>
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<T> ToReadOnlyList<T>(ICollection list)
{
if (list is IReadOnlyList<T> 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<IStyle>(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<IStyle>(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;
}

89
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<DataGrid, DataGridLength>(nameof(ColumnWidth), defaultValue: DataGridLength.Auto);
/// <summary>
/// Gets or sets the standard width or automatic sizing mode of columns in the control.
/// Identifies the <see cref="RowTheme"/> dependency property.
/// </summary>
public DataGridLength ColumnWidth
public static readonly StyledProperty<ControlTheme> RowThemeProperty =
AvaloniaProperty.Register<DataGrid, ControlTheme>(nameof(RowTheme));
/// <summary>
/// Gets or sets the theme applied to all rows.
/// </summary>
public ControlTheme RowTheme
{
get { return GetValue(ColumnWidthProperty); }
set { SetValue(ColumnWidthProperty, value); }
get { return GetValue(RowThemeProperty); }
set { SetValue(RowThemeProperty, value); }
}
public static readonly StyledProperty<IBrush> AlternatingRowBackgroundProperty =
AvaloniaProperty.Register<DataGrid, IBrush>(nameof(AlternatingRowBackground));
/// <summary>
/// Identifies the <see cref="CellTheme"/> dependency property.
/// </summary>
public static readonly StyledProperty<ControlTheme> CellThemeProperty =
AvaloniaProperty.Register<DataGrid, ControlTheme>(nameof(CellTheme));
/// <summary>
/// Gets or sets the <see cref="T:System.Windows.Media.Brush" /> that is used to paint the background of odd-numbered rows.
/// Gets or sets the theme applied to all cells.
/// </summary>
/// <returns>
/// The brush that is used to paint the background of odd-numbered rows. The default is a
/// <see cref="T:System.Windows.Media.SolidColorBrush" /> with a
/// <see cref="P:System.Windows.Media.SolidColorBrush.Color" /> value of white (ARGB value #00FFFFFF).
/// </returns>
public IBrush AlternatingRowBackground
public ControlTheme CellTheme
{
get { return GetValue(CellThemeProperty); }
set { SetValue(CellThemeProperty, value); }
}
/// <summary>
/// Identifies the <see cref="ColumnHeaderTheme"/> dependency property.
/// </summary>
public static readonly StyledProperty<ControlTheme> ColumnHeaderThemeProperty =
AvaloniaProperty.Register<DataGrid, ControlTheme>(nameof(ColumnHeaderTheme));
/// <summary>
/// Gets or sets the theme applied to all column headers.
/// </summary>
public ControlTheme ColumnHeaderTheme
{
get { return GetValue(ColumnHeaderThemeProperty); }
set { SetValue(ColumnHeaderThemeProperty, value); }
}
/// <summary>
/// Identifies the <see cref="RowGroupTheme"/> dependency property.
/// </summary>
public static readonly StyledProperty<ControlTheme> RowGroupThemeProperty =
AvaloniaProperty.Register<DataGrid, ControlTheme>(nameof(RowGroupTheme));
/// <summary>
/// Gets or sets the theme applied to all row groups.
/// </summary>
public ControlTheme RowGroupTheme
{
get { return GetValue(AlternatingRowBackgroundProperty); }
set { SetValue(AlternatingRowBackgroundProperty, value); }
get { return GetValue(RowGroupThemeProperty); }
set { SetValue(RowGroupThemeProperty, value); }
}
/// <summary>
/// Gets or sets the standard width or automatic sizing mode of columns in the control.
/// </summary>
public DataGridLength ColumnWidth
{
get { return GetValue(ColumnWidthProperty); }
set { SetValue(ColumnWidthProperty, value); }
}
public static readonly StyledProperty<int> 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;
}
/// <inheritdoc/>
protected override void OnDataContextBeginUpdate()
{
@ -2183,7 +2227,7 @@ namespace Avalonia.Controls
NotifyDataContextPropertyForAllRowCells(GetAllRows(), false);
}
/// <summary>
/// Raises the BeginningEdit event.
/// </summary>
@ -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)

39
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -231,7 +231,7 @@ namespace Avalonia.Controls
}
/// <summary>
/// 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.
/// </summary>
/// <returns>
@ -260,15 +260,15 @@ namespace Avalonia.Controls
/// </returns>
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
/// </returns>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// When setting this property, the specified value is less than -1 or equal to <see cref="F:System.Int32.MaxValue" />.
///
///
/// -or-
///
///
/// When setting this property on a column in a <see cref="T:Avalonia.Controls.DataGrid" />, the specified value is less than zero or greater than or equal to the number of columns in the <see cref="T:Avalonia.Controls.DataGrid" />.
/// </exception>
/// <exception cref="T:System.InvalidOperationException">
/// When setting this property, the <see cref="T:Avalonia.Controls.DataGrid" /> is already making <see cref="P:Avalonia.Controls.DataGridColumn.DisplayIndex" /> adjustments. For example, this exception is thrown when you attempt to set <see cref="P:Avalonia.Controls.DataGridColumn.DisplayIndex" /> in a <see cref="E:Avalonia.Controls.DataGrid.ColumnDisplayIndexChanged" /> 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.
/// </exception>
public int DisplayIndex
@ -401,7 +401,7 @@ namespace Avalonia.Controls
}
}
}
/// <summary>
/// Backing field for CellTheme property.
/// </summary>
@ -412,7 +412,7 @@ namespace Avalonia.Controls
(o, v) => o.CellTheme = v);
/// <summary>
/// Gets or sets the <see cref="DataGridColumnHeader"/> cell theme.
/// Gets or sets the <see cref="DataGridColumnHeader"/> cell theme.
/// </summary>
public ControlTheme CellTheme
{
@ -430,14 +430,14 @@ namespace Avalonia.Controls
(o, v) => o.Header = v);
/// <summary>
/// Gets or sets the <see cref="DataGridColumnHeader"/> content
/// Gets or sets the <see cref="DataGridColumnHeader"/> content
/// </summary>
public object Header
{
get { return _header; }
set { SetAndRaise(HeaderProperty, ref _header, value); }
}
/// <summary>
/// Backing field for Header property
/// </summary>
@ -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);
/// <summary>
/// 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
/// <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
/// </summary>
/// <param name="cell">
@ -765,7 +765,7 @@ namespace Avalonia.Controls
protected abstract IControl GenerateElement(DataGridCell cell, object dataItem);
/// <summary>
/// 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.
/// </summary>
/// <param name="propertyName">Indicates which property changed and caused this call</param>
@ -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;
}

15
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@ -76,7 +76,7 @@ namespace Avalonia.Controls
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeader" /> class.
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeader" /> class.
/// </summary>
//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
}
/// <summary>
/// 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))
{

25
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -89,7 +89,7 @@ namespace Avalonia.Controls
o => o.IsValid);
/// <summary>
/// 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.
/// </summary>
public bool IsValid
{
@ -130,7 +130,7 @@ namespace Avalonia.Controls
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridRow" /> class.
/// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridRow" /> class.
/// </summary>
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
}
/// <summary>
/// Measures the children of a <see cref="T:Avalonia.Controls.DataGridRow" /> to
/// Measures the children of a <see cref="T:Avalonia.Controls.DataGridRow" /> to
/// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
/// </summary>
/// <param name="availableSize">
@ -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
}

14
src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs

@ -53,7 +53,7 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<DataGridRowGroupHeader, string>(nameof(PropertyName));
/// <summary>
/// Gets or sets the name of the property that this <see cref="T:Avalonia.Controls.DataGrid" /> row is bound to.
/// Gets or sets the name of the property that this <see cref="T:Avalonia.Controls.DataGrid" /> row is bound to.
/// </summary>
public string PropertyName
{
@ -85,8 +85,8 @@ namespace Avalonia.Controls
}
/// <summary>
/// Gets or sets a value that indicates the amount that the
/// children of the <see cref="T:Avalonia.Controls.RowGroupHeader" /> are indented.
/// Gets or sets a value that indicates the amount that the
/// children of the <see cref="T:Avalonia.Controls.RowGroupHeader" /> are indented.
/// </summary>
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)

72
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
}
/// <summary>
/// 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.
/// </summary>
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
/// <summary>
/// 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.
/// </summary>
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

7
src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml

@ -232,7 +232,7 @@
<Setter Property="Data" Value="{StaticResource DataGridSortIconDescendingPath}" />
</Style>
</ControlTheme>
<ControlTheme x:Key="DataGridTopLeftColumnHeader" TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="Template">
<ControlTemplate>
@ -337,10 +337,6 @@
</ControlTemplate>
</Setter>
<Style Selector="^:nth-child(even)">
<Setter Property="Background" Value="{Binding $parent[DataGrid].AlternatingRowBackground}" />
</Style>
<Style Selector="^:invalid">
<Style Selector="^ /template/ Rectangle#InvalidVisualElement">
<Setter Property="Opacity" Value="0.4" />
@ -484,7 +480,6 @@
<ControlTheme x:Key="{x:Type DataGrid}" TargetType="DataGrid">
<Setter Property="RowBackground" Value="Transparent" />
<Setter Property="AlternatingRowBackground" Value="Transparent" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />

5
src/Avalonia.Controls.DataGrid/Themes/Simple.xaml

@ -200,10 +200,6 @@
</ControlTemplate>
</Setter>
<Style Selector="^:nth-child(even)">
<Setter Property="Background" Value="{Binding $parent[DataGrid].AlternatingRowBackground}" />
</Style>
<Style Selector="^ /template/ Rectangle#BackgroundRectangle">
<Setter Property="IsVisible" Value="False" />
<Setter Property="Fill" Value="{DynamicResource HighlightBrush}" />
@ -300,7 +296,6 @@
<ControlTheme x:Key="{x:Type DataGrid}"
TargetType="DataGrid">
<Setter Property="RowBackground" Value="{DynamicResource ThemeAccentBrush4}" />
<Setter Property="AlternatingRowBackground" Value="#00FFFFFF" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />

2
src/Avalonia.Controls/Application.cs

@ -39,7 +39,6 @@ namespace Avalonia
private readonly Lazy<IClipboard?> _clipboard =
new Lazy<IClipboard?>(() => (IClipboard?)AvaloniaLocator.Current.GetService(typeof(IClipboard)));
private readonly Styler _styler = new Styler();
private Styles? _styles;
private IResourceDictionary? _resources;
private bool _notifyingResourcesChanged;
@ -232,7 +231,6 @@ namespace Avalonia
.Bind<IFocusManager>().ToConstant(FocusManager)
.Bind<IInputManager>().ToConstant(InputManager)
.Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
.Bind<IStyler>().ToConstant(_styler)
.Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance)
.Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance);

3
src/Avalonia.Controls/TopLevel.cs

@ -145,7 +145,6 @@ namespace Avalonia.Controls
_actualTransparencyLevel = PlatformImpl.TransparencyLevel;
dependencyResolver = dependencyResolver ?? AvaloniaLocator.Current;
var styler = TryGetService<IStyler>(dependencyResolver);
_accessKeyHandler = TryGetService<IAccessKeyHandler>(dependencyResolver);
_inputManager = TryGetService<IInputManager>(dependencyResolver);
@ -183,8 +182,6 @@ namespace Avalonia.Controls
_globalStyles.GlobalStylesRemoved += ((IStyleHost)this).StylesRemoved;
}
styler?.ApplyStyles(this);
ClientSize = impl.ClientSize;
FrameSize = impl.FrameSize;

2
src/Avalonia.Controls/Window.cs

@ -647,6 +647,7 @@ namespace Avalonia.Controls
RaiseEvent(new RoutedEventArgs(WindowOpenedEvent));
EnsureInitialized();
ApplyStyling();
IsVisible = true;
var initialSize = new Size(
@ -726,6 +727,7 @@ namespace Avalonia.Controls
RaiseEvent(new RoutedEventArgs(WindowOpenedEvent));
EnsureInitialized();
ApplyStyling();
IsVisible = true;
var initialSize = new Size(

1
src/Avalonia.Controls/WindowBase.cs

@ -149,6 +149,7 @@ namespace Avalonia.Controls
try
{
EnsureInitialized();
ApplyStyling();
IsVisible = true;
if (!_hasExecutedInitialLayoutPass)

2
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@ -82,8 +82,6 @@ namespace Avalonia.Markup.Xaml.Styling
}
}
public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host);
public bool TryGetResource(object key, out object? value)
{
if (!_isLoading)

2
tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs

@ -498,7 +498,7 @@ namespace Avalonia.Base.UnitTests.Animation
private static IDisposable Start()
{
var clock = new MockGlobalClock();
var services = TestServices.RealStyler.With(globalClock: clock);
var services = new TestServices(globalClock: clock);
return UnitTestApplication.Start(services);
}

194
tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs

@ -5,7 +5,6 @@ using Avalonia.Base.UnitTests.Animation;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Diagnostics;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Moq;
@ -301,8 +300,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Attach()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -337,8 +334,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Bindings_Should_Not_Be_Made_Active_During_Style_Attach()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -380,8 +375,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Detach()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -417,8 +410,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Detach_2()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -454,8 +445,6 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Inactive_Bindings_Should_Not_Be_Made_Active_During_Style_Detach()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var root = new TestRoot
{
Styles =
@ -598,41 +587,36 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Removing_Style_Should_Detach_From_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
{
Styles =
{
new Style(x => x.OfType<Border>())
Styles =
{
new Style(x => x.OfType<Border>())
{
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
},
Child = border,
};
}
},
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Styles.RemoveAt(0);
Assert.Equal(new Thickness(0), border.BorderThickness);
}
root.Styles.RemoveAt(0);
Assert.Equal(new Thickness(0), border.BorderThickness);
}
[Fact]
public void Adding_Style_Should_Attach_To_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
{
Styles =
Styles =
{
new Style(x => x.OfType<Border>())
{
@ -642,34 +626,31 @@ namespace Avalonia.Base.UnitTests.Styling
}
}
},
Child = border,
};
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Styles.Add(new Style(x => x.OfType<Border>())
{
Setters =
root.Styles.Add(new Style(x => x.OfType<Border>())
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
});
});
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
}
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
}
[Fact]
public void Removing_Style_With_Nested_Style_Should_Detach_From_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
{
Styles =
Styles =
{
new Styles
{
@ -682,96 +663,89 @@ namespace Avalonia.Base.UnitTests.Styling
}
}
},
Child = border,
};
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Styles.RemoveAt(0);
Assert.Equal(new Thickness(0), border.BorderThickness);
}
root.Styles.RemoveAt(0);
Assert.Equal(new Thickness(0), border.BorderThickness);
}
[Fact]
public void Adding_Nested_Style_Should_Attach_To_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
Styles =
{
Styles =
new Styles
{
new Styles
new Style(x => x.OfType<Border>())
{
new Style(x => x.OfType<Border>())
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
}
},
Child = border,
};
}
},
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
((Styles)root.Styles[0]).Add(new Style(x => x.OfType<Border>())
((Styles)root.Styles[0]).Add(new Style(x => x.OfType<Border>())
{
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
});
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
});
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
}
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
}
[Fact]
public void Removing_Nested_Style_Should_Detach_From_Control()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var border = new Border();
var root = new TestRoot
{
var border = new Border();
var root = new TestRoot
Styles =
{
Styles =
new Styles
{
new Styles
new Style(x => x.OfType<Border>())
{
new Style(x => x.OfType<Border>())
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
},
new Style(x => x.OfType<Border>())
new Setter(Border.BorderThicknessProperty, new Thickness(4)),
}
},
new Style(x => x.OfType<Border>())
{
Setters =
{
Setters =
{
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
},
}
},
Child = border,
};
new Setter(Border.BorderThicknessProperty, new Thickness(6)),
}
},
}
},
Child = border,
};
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(6), border.BorderThickness);
((Styles)root.Styles[0]).RemoveAt(1);
((Styles)root.Styles[0]).RemoveAt(1);
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
}
root.Measure(Size.Infinity);
Assert.Equal(new Thickness(4), border.BorderThickness);
}
[Fact]

78
tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs

@ -249,65 +249,67 @@ namespace Avalonia.Base.UnitTests.Styling
}
[Fact]
public void Adding_Tree_To_IStyleRoot_Should_Style_Controls()
public void Adding_Tree_To_Root_Should_Style_Controls()
{
using (AvaloniaLocator.EnterScope())
var root = new TestRoot
{
var root = new TestRoot();
var parent = new Border();
var child = new Border();
var grandchild = new Control();
var styler = new Mock<IStyler>();
AvaloniaLocator.CurrentMutable.Bind<IStyler>().ToConstant(styler.Object);
Styles =
{
new Style(x => x.Is<Control>())
{
Setters = { new Setter(Control.TagProperty, "foo") }
}
}
};
parent.Child = child;
child.Child = grandchild;
var grandchild = new Control();
var child = new Border { Child = grandchild };
var parent = new Border { Child = child };
styler.Verify(x => x.ApplyStyles(It.IsAny<IStyleable>()), Times.Never());
Assert.Null(parent.Tag);
Assert.Null(child.Tag);
Assert.Null(grandchild.Tag);
root.Child = parent;
root.Child = parent;
styler.Verify(x => x.ApplyStyles(parent), Times.Once());
styler.Verify(x => x.ApplyStyles(child), Times.Once());
styler.Verify(x => x.ApplyStyles(grandchild), Times.Once());
}
Assert.Equal("foo", parent.Tag);
Assert.Equal("foo", child.Tag);
Assert.Equal("foo", grandchild.Tag);
}
[Fact]
public void Styles_Not_Applied_Until_Initialization_Finished()
{
using (AvaloniaLocator.EnterScope())
var root = new TestRoot
{
var root = new TestRoot();
var child = new Border();
var styler = new Mock<IStyler>();
Styles =
{
new Style(x => x.Is<Control>())
{
Setters = { new Setter(Control.TagProperty, "foo") }
}
}
};
AvaloniaLocator.CurrentMutable.Bind<IStyler>().ToConstant(styler.Object);
var child = new Border();
((ISupportInitialize)child).BeginInit();
root.Child = child;
styler.Verify(x => x.ApplyStyles(It.IsAny<IStyleable>()), Times.Never());
((ISupportInitialize)child).BeginInit();
root.Child = child;
Assert.Null(child.Tag);
((ISupportInitialize)child).EndInit();
styler.Verify(x => x.ApplyStyles(child), Times.Once());
}
((ISupportInitialize)child).EndInit();
Assert.Equal("foo", child.Tag);
}
[Fact]
public void Name_Cannot_Be_Set_After_Added_To_Logical_Tree()
{
using (AvaloniaLocator.EnterScope())
{
var root = new TestRoot();
var child = new Border();
AvaloniaLocator.CurrentMutable.BindToSelf<IStyler>(new Styler());
var root = new TestRoot();
var child = new Border();
root.Child = child;
root.Child = child;
Assert.Throws<InvalidOperationException>(() => child.Name = "foo");
}
Assert.Throws<InvalidOperationException>(() => child.Name = "foo");
}
[Fact]
@ -328,7 +330,7 @@ namespace Avalonia.Base.UnitTests.Styling
[Fact]
public void Style_Is_Removed_When_Control_Removed_From_Logical_Tree()
{
var app = UnitTestApplication.Start(TestServices.RealStyler);
var app = UnitTestApplication.Start();
var target = new Border();
var root = new TestRoot
{

21
tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs

@ -19,7 +19,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Applied_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
Assert.Null(target.Template);
@ -37,7 +36,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Applied_To_Derived_Class_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = new DerivedThemedControl
{
Theme = CreateTheme(),
@ -58,7 +56,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Detached_When_Theme_Property_Cleared()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var root = CreateRoot(target);
@ -71,8 +68,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Detached_From_Template_Controls_When_Theme_Property_Cleared()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var theme = new ControlTheme
{
TargetType = typeof(ThemedControl),
@ -105,7 +100,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Applied_On_Layout_After_Theme_Property_Changes()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = new ThemedControl();
var root = CreateRoot(target);
@ -124,7 +118,6 @@ public class StyledElementTests_Theming
[Fact]
public void BasedOn_Theme_Is_Applied_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget(CreateDerivedTheme());
Assert.Null(target.Template);
@ -163,7 +156,6 @@ public class StyledElementTests_Theming
[Fact]
public void Implicit_Theme_Is_Applied_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var root = CreateRoot(target);
Assert.NotNull(target.Template);
@ -178,21 +170,19 @@ public class StyledElementTests_Theming
[Fact]
public void Implicit_Theme_Is_Cleared_When_Removed_From_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var root = CreateRoot(target);
Assert.NotNull(((IStyleable)target).GetEffectiveTheme());
Assert.NotNull(target.GetEffectiveTheme());
root.Child = null;
Assert.Null(((IStyleable)target).GetEffectiveTheme());
Assert.Null(target.GetEffectiveTheme());
}
[Fact]
public void Nested_Style_Can_Override_Property_In_Inner_Templated_Control()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = new ThemedControl2
{
Theme = new ControlTheme(typeof(ThemedControl2))
@ -236,7 +226,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Is_Applied_When_Attached_To_Logical_Tree()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
Assert.Null(target.Theme);
@ -248,16 +237,15 @@ public class StyledElementTests_Theming
Assert.NotNull(target.Template);
var border = Assert.IsType<Border>(target.VisualChild);
Assert.Equal(border.Background, Brushes.Red);
Assert.Equal(Brushes.Red, border.Background);
target.Classes.Add("foo");
Assert.Equal(border.Background, Brushes.Green);
Assert.Equal(Brushes.Green, border.Background);
}
[Fact]
public void Theme_Can_Be_Changed_By_Style_Class()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var theme1 = CreateTheme();
var theme2 = new ControlTheme(typeof(ThemedControl));
@ -290,7 +278,6 @@ public class StyledElementTests_Theming
[Fact]
public void Theme_Can_Be_Set_To_LocalValue_While_Updating_Due_To_Style_Class()
{
using var app = UnitTestApplication.Start(TestServices.RealStyler);
var target = CreateTarget();
var theme1 = CreateTheme();
var theme2 = new ControlTheme(typeof(ThemedControl));

1
tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs

@ -22,7 +22,6 @@ namespace Avalonia.Benchmarks.Styling
platform: new AppBuilder().RuntimePlatform,
renderInterface: new MockPlatformRenderInterface(),
standardCursorFactory: Mock.Of<ICursorFactory>(),
styler: new Styler(),
theme: () => CreateTheme(),
threadingInterface: new NullThreadingPlatform(),
fontManagerImpl: new MockFontManagerImpl(),

47
tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs

@ -1,47 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.UnitTests;
using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Styling
{
[MemoryDiagnoser]
public class StyleAttachBenchmark : IDisposable
{
private readonly IDisposable _app;
private readonly TestRoot _root;
private readonly TextBox _control;
public StyleAttachBenchmark()
{
_app = UnitTestApplication.Start(
TestServices.StyledWindow.With(
renderInterface: new NullRenderingPlatform(),
threadingInterface: new NullThreadingPlatform()));
_root = new TestRoot(true, null)
{
Renderer = new NullRenderer(),
};
_control = new TextBox();
}
[Benchmark]
[MethodImpl(MethodImplOptions.NoInlining)]
public void AttachTextBoxStyles()
{
var styles = UnitTestApplication.Current.Styles;
styles.TryAttach(_control, UnitTestApplication.Current);
((IStyleable)_control).DetachStyles();
}
public void Dispose()
{
_app.Dispose();
}
}
}

126
tests/Avalonia.Benchmarks/Styling/Style_Apply_Detach_Complex.cs

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
using Avalonia.Styling;
using Avalonia.UnitTests;
using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Styling
{
[MemoryDiagnoser]
public class Style_Apply_Detach_Complex : IDisposable
{
private readonly IDisposable _app;
private readonly TestRoot _root;
private readonly TextBox _control;
public Style_Apply_Detach_Complex()
{
_app = UnitTestApplication.Start(
TestServices.StyledWindow.With(
renderInterface: new NullRenderingPlatform(),
threadingInterface: new NullThreadingPlatform()));
// Simulate an application with a lot of styles by creating a tree of nested panels,
// each with a bunch of styles applied.
var (rootPanel, leafPanel) = CreateNestedPanels(10);
// We're benchmarking how long it takes to apply styles to a TextBox in this situation.
_control = new TextBox();
leafPanel.Children.Add(_control);
_root = new TestRoot(true, rootPanel)
{
Renderer = new NullRenderer(),
};
}
[Benchmark]
[MethodImpl(MethodImplOptions.NoInlining)]
public void Apply_Detach_Styles()
{
// Styles will have already been attached when attached to the logical tree, so remove
// the styles first.
if ((string)_control.Tag != "TextBox")
throw new Exception("Invalid benchmark state");
((IStyleable)_control).DetachStyles();
if (_control.Tag is not null)
throw new Exception("Invalid benchmark state");
// Then re-apply the styles.
_control.ApplyStyling();
}
public void Dispose()
{
_app.Dispose();
}
private static (Panel, Panel) CreateNestedPanels(int count)
{
var root = new Panel();
var last = root;
for (var i = 0; i < count; ++i)
{
var panel = new Panel();
panel.Styles.AddRange(CreateStyles());
last.Children.Add(panel);
last = panel;
}
return (root, last);
}
private static IEnumerable<IStyle> CreateStyles()
{
var types = new[]
{
typeof(Border),
typeof(Button),
typeof(ButtonSpinner),
typeof(Carousel),
typeof(CheckBox),
typeof(ComboBox),
typeof(ContentControl),
typeof(Expander),
typeof(ItemsControl),
typeof(Label),
typeof(ListBox),
typeof(ProgressBar),
typeof(RadioButton),
typeof(RepeatButton),
typeof(ScrollViewer),
typeof(Slider),
typeof(Spinner),
typeof(SplitView),
typeof(TextBox),
typeof(ToggleSwitch),
typeof(TreeView),
typeof(Viewbox),
typeof(Window),
};
foreach (var type in types)
{
yield return new Style(x => x.OfType(type))
{
Setters = { new Setter(Control.TagProperty, type.Name) }
};
yield return new Style(x => x.OfType(type).Class("foo"))
{
Setters = { new Setter(Control.TagProperty, type.Name + " foo") }
};
yield return new Style(x => x.OfType(type).Class("bar"))
{
Setters = { new Setter(Control.TagProperty, type.Name + " bar") }
};
}
}
}
}

1
tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs

@ -46,7 +46,6 @@ namespace Avalonia.Benchmarks.Themes
platform: new AppBuilder().RuntimePlatform,
renderInterface: new MockPlatformRenderInterface(),
standardCursorFactory: Mock.Of<ICursorFactory>(),
styler: new Styler(),
theme: () => LoadFluentTheme(),
threadingInterface: new NullThreadingPlatform(),
fontManagerImpl: new MockFontManagerImpl(),

1
tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs

@ -1056,6 +1056,7 @@ namespace Avalonia.Controls.UnitTests
control.Items = CreateSimpleStringArray();
TextBox textBox = GetTextBox(control);
var window = new Window {Content = control};
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
Dispatcher.UIThread.RunJobs();

63
tests/Avalonia.Controls.UnitTests/ContentControlTests.cs

@ -37,11 +37,19 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Templated_Children_Should_Be_Styled()
{
var root = new TestRoot();
var root = new TestRoot
{
Styles =
{
new Style(x => x.Is<Control>())
{
Setters = { new Setter(Control.TagProperty, "foo") }
}
}
};
var target = new ContentControl();
var styler = new Mock<IStyler>();
AvaloniaLocator.CurrentMutable.Bind<IStyler>().ToConstant(styler.Object);
target.Content = "Foo";
target.Template = GetTemplate();
root.Child = target;
@ -49,10 +57,8 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
styler.Verify(x => x.ApplyStyles(It.IsAny<ContentControl>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<Border>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<ContentPresenter>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once());
foreach (Control child in target.GetTemplateChildren())
Assert.Equal("foo", child.Tag);
}
[Fact]
@ -332,40 +338,37 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Set_Child_LogicalParent_After_Removing_And_Adding_Back_To_Logical_Tree()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var target = new ContentControl();
var root = new TestRoot
{
var target = new ContentControl();
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<ContentControl>())
{
new Style(x => x.OfType<ContentControl>())
Setters =
{
Setters =
{
new Setter(ContentControl.TemplateProperty, GetTemplate()),
}
new Setter(ContentControl.TemplateProperty, GetTemplate()),
}
},
Child = target
};
}
},
Child = target
};
target.Content = "Foo";
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.Content = "Foo";
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Equal(target, target.Presenter.Child.LogicalParent);
Assert.Equal(target, target.Presenter.Child.LogicalParent);
root.Child = null;
root.Child = null;
Assert.Null(target.Template);
Assert.Null(target.Template);
target.Content = null;
root.Child = target;
target.Content = "Bar";
target.Content = null;
root.Child = target;
target.Content = "Bar";
Assert.Equal(target, target.Presenter.Child.LogicalParent);
}
Assert.Equal(target, target.Presenter.Child.LogicalParent);
}
private FuncControlTemplate GetTemplate()

10
tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

@ -29,6 +29,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -61,6 +62,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -130,6 +132,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -158,6 +161,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -186,6 +190,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -207,6 +212,7 @@ namespace Avalonia.Controls.UnitTests
};
var window = new Window { Content = target };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -390,7 +396,8 @@ namespace Avalonia.Controls.UnitTests
var sp = new StackPanel { Children = { target1, target2 } };
var window = new Window { Content = sp };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -594,6 +601,7 @@ namespace Avalonia.Controls.UnitTests
windowImpl.Setup(x => x.CreateRenderer(It.IsAny<IRenderRoot>())).Returns(renderer.Object);
var w = new Window(windowImpl.Object) { Content = content };
w.ApplyStyling();
w.ApplyTemplate();
w.Presenter.ApplyTemplate();
return w;

4
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -99,7 +99,7 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var items = new[] { "Foo", "Bar", "Baz " };
var theme = new ControlTheme();
var theme = new ControlTheme(typeof(ListBoxItem));
var target = new ListBox
{
Template = ListBoxTemplate(),
@ -121,7 +121,7 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var items = new[] { "Foo", "Bar", "Baz " };
var theme = new ControlTheme();
var theme = new ControlTheme(typeof(ListBoxItem));
var target = new ListBox
{
Template = ListBoxTemplate(),

3
tests/Avalonia.Controls.UnitTests/MenuItemTests.cs

@ -193,6 +193,7 @@ namespace Avalonia.Controls.UnitTests
var target = new MenuItem();
var contextMenu = new ContextMenu { Items = new AvaloniaList<MenuItem> { target } };
var window = new Window { Content = new Panel { ContextMenu = contextMenu } };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -232,6 +233,7 @@ namespace Avalonia.Controls.UnitTests
var flyout = new MenuFlyout { Items = new AvaloniaList<MenuItem> { target } };
var button = new Button { Flyout = flyout };
var window = new Window { Content = button };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -271,6 +273,7 @@ namespace Avalonia.Controls.UnitTests
var parentMenuItem = new MenuItem { Items = new AvaloniaList<MenuItem> { target } };
var contextMenu = new ContextMenu { Items = new AvaloniaList<MenuItem> { parentMenuItem } };
var window = new Window { Content = new Panel { ContextMenu = contextMenu } };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
contextMenu.Open();

1
tests/Avalonia.Controls.UnitTests/NumericUpDownTests.cs

@ -50,6 +50,7 @@ namespace Avalonia.Controls.UnitTests
var control = CreateControl();
TextBox textBox = GetTextBox(control);
var window = new Window { Content = control };
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
Dispatcher.UIThread.RunJobs();

2
tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs

@ -51,6 +51,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
};
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
target.ApplyTemplate();
@ -177,6 +178,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
};
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
target.ApplyTemplate();

2
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -569,6 +569,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
windowImpl.Setup(x => x.CreateRenderer(It.IsAny<IRenderRoot>())).Returns(renderer.Object);
var window = new Window(windowImpl.Object);
window.ApplyStyling();
window.ApplyTemplate();
var target = new Popup()
@ -1090,6 +1091,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
private Window PreparedWindow(object content = null)
{
var w = new Window { Content = content };
w.ApplyStyling();
w.ApplyTemplate();
return w;
}

12
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -998,6 +998,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Order_Of_Setting_Items_And_SelectedIndex_During_Initialization_Should_Not_Matter()
{
using var app = Start();
var items = new[] { "Foo", "Bar" };
var target = new SelectingItemsControl();
@ -1015,6 +1016,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Order_Of_Setting_Items_And_SelectedItem_During_Initialization_Should_Not_Matter()
{
using var app = Start();
var items = new[] { "Foo", "Bar" };
var target = new SelectingItemsControl();
@ -1847,6 +1849,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
public void Preserves_SelectedItem_When_Items_Changed()
{
// Issue #4048
using var app = Start();
var target = new SelectingItemsControl
{
Items = new[] { "foo", "bar", "baz"},
@ -1867,6 +1870,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Setting_SelectedItems_Raises_PropertyChanged()
{
using var app = Start();
var target = new TestSelector
{
Items = new[] { "foo", "bar", "baz" },
@ -1895,6 +1899,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Setting_Selection_Raises_SelectedItems_PropertyChanged()
{
using var app = Start();
var target = new TestSelector
{
Items = new[] { "foo", "bar", "baz" },
@ -2050,6 +2055,13 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
}
private static IDisposable Start()
{
return UnitTestApplication.Start(new TestServices(
fontManagerImpl: new MockFontManagerImpl(),
textShaperImpl: new MockTextShaperImpl()));
}
private static void Prepare(SelectingItemsControl target)
{
var root = new TestRoot

249
tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs

@ -170,36 +170,41 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Templated_Children_Should_Be_Styled()
{
using (UnitTestApplication.Start(TestServices.MockStyler))
{
TestTemplatedControl target;
TestTemplatedControl target;
var root = new TestRoot
var root = new TestRoot
{
Styles =
{
Child = target = new TestTemplatedControl
new Style(x => x.Is<Control>())
{
Template = new FuncControlTemplate((_, __) =>
Setters =
{
return new StackPanel
{
Children =
new Setter(Control.TagProperty, "foo")
}
}
},
Child = target = new TestTemplatedControl
{
Template = new FuncControlTemplate((_, __) =>
{
return new StackPanel
{
Children =
{
new TextBlock
{
}
}
};
}),
}
};
};
}),
}
};
target.ApplyTemplate();
target.ApplyTemplate();
var styler = Mock.Get(UnitTestApplication.Current.Services.Styler);
styler.Verify(x => x.ApplyStyles(It.IsAny<TestTemplatedControl>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<StackPanel>()), Times.Once());
styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once());
}
foreach (Control child in target.GetTemplateChildren())
Assert.Equal("foo", child.Tag);
}
[Fact]
@ -351,166 +356,154 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Removing_From_LogicalTree_Should_Not_Remove_Child()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
Border templateChild = new Border();
TestTemplatedControl target;
var root = new TestRoot
{
Border templateChild = new Border();
TestTemplatedControl target;
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<TestTemplatedControl>())
{
new Style(x => x.OfType<TestTemplatedControl>())
Setters =
{
Setters =
{
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
},
Child = target = new TestTemplatedControl()
};
}
},
Child = target = new TestTemplatedControl()
};
Assert.NotNull(target.Template);
target.ApplyTemplate();
Assert.NotNull(target.Template);
target.ApplyTemplate();
root.Child = null;
root.Child = null;
Assert.Null(target.Template);
Assert.IsType<Decorator>(target.GetVisualChildren().Single());
}
Assert.Null(target.Template);
Assert.IsType<Decorator>(target.GetVisualChildren().Single());
}
[Fact]
public void Re_adding_To_Same_LogicalTree_Should_Not_Recreate_Template()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
TestTemplatedControl target;
var root = new TestRoot
{
TestTemplatedControl target;
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<TestTemplatedControl>())
{
new Style(x => x.OfType<TestTemplatedControl>())
Setters =
{
Setters =
{
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
},
Child = target = new TestTemplatedControl()
};
}
},
Child = target = new TestTemplatedControl()
};
Assert.NotNull(target.Template);
target.ApplyTemplate();
var expected = (Decorator)target.GetVisualChildren().Single();
Assert.NotNull(target.Template);
target.ApplyTemplate();
var expected = (Decorator)target.GetVisualChildren().Single();
root.Child = null;
root.Child = target;
target.ApplyTemplate();
root.Child = null;
root.Child = target;
target.ApplyTemplate();
Assert.Same(expected, target.GetVisualChildren().Single());
}
Assert.Same(expected, target.GetVisualChildren().Single());
}
[Fact]
public void Re_adding_To_Different_LogicalTree_Should_Recreate_Template()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
{
TestTemplatedControl target;
TestTemplatedControl target;
var root = new TestRoot
var root = new TestRoot
{
Styles =
{
Styles =
new Style(x => x.OfType<TestTemplatedControl>())
{
new Style(x => x.OfType<TestTemplatedControl>())
Setters =
{
Setters =
{
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
},
Child = target = new TestTemplatedControl()
};
}
},
Child = target = new TestTemplatedControl()
};
var root2 = new TestRoot
var root2 = new TestRoot
{
Styles =
{
Styles =
new Style(x => x.OfType<TestTemplatedControl>())
{
new Style(x => x.OfType<TestTemplatedControl>())
Setters =
{
Setters =
{
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
new Setter(
TemplatedControl.TemplateProperty,
new FuncControlTemplate((_, __) => new Decorator
{
Child = new Border(),
}))
}
},
};
}
},
};
Assert.NotNull(target.Template);
target.ApplyTemplate();
Assert.NotNull(target.Template);
target.ApplyTemplate();
var expected = (Decorator)target.GetVisualChildren().Single();
var expected = (Decorator)target.GetVisualChildren().Single();
root.Child = null;
root2.Child = target;
target.ApplyTemplate();
root.Child = null;
root2.Child = target;
target.ApplyTemplate();
var child = target.GetVisualChildren().Single();
Assert.NotNull(target.Template);
Assert.NotNull(child);
Assert.NotSame(expected, child);
}
var child = target.GetVisualChildren().Single();
Assert.NotNull(target.Template);
Assert.NotNull(child);
Assert.NotSame(expected, child);
}
[Fact]
public void Moving_To_New_LogicalTree_Should_Detach_Attach_Template_Child()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
TestTemplatedControl target;
var root = new TestRoot
{
TestTemplatedControl target;
var root = new TestRoot
Child = target = new TestTemplatedControl
{
Child = target = new TestTemplatedControl
{
Template = new FuncControlTemplate((_, __) => new Decorator()),
}
};
Template = new FuncControlTemplate((_, __) => new Decorator()),
}
};
Assert.NotNull(target.Template);
target.ApplyTemplate();
Assert.NotNull(target.Template);
target.ApplyTemplate();
var templateChild = (ILogical)target.GetVisualChildren().Single();
Assert.True(templateChild.IsAttachedToLogicalTree);
var templateChild = (ILogical)target.GetVisualChildren().Single();
Assert.True(templateChild.IsAttachedToLogicalTree);
root.Child = null;
Assert.False(templateChild.IsAttachedToLogicalTree);
root.Child = null;
Assert.False(templateChild.IsAttachedToLogicalTree);
var newRoot = new TestRoot { Child = target };
Assert.True(templateChild.IsAttachedToLogicalTree);
}
var newRoot = new TestRoot { Child = target };
Assert.True(templateChild.IsAttachedToLogicalTree);
}
[Fact]

28
tests/Avalonia.Controls.UnitTests/TabControlTests.cs

@ -227,28 +227,24 @@ namespace Avalonia.Controls.UnitTests
};
var template = new FuncControlTemplate<TabItem>((x, __) => new Decorator());
using (UnitTestApplication.Start(TestServices.RealStyler))
var root = new TestRoot
{
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<TabItem>())
{
new Style(x => x.OfType<TabItem>())
Setters =
{
Setters =
{
new Setter(TemplatedControl.TemplateProperty, template)
}
new Setter(TemplatedControl.TemplateProperty, template)
}
},
Child = new TabControl
{
Template = TabControlTemplate(),
Items = collection,
}
};
}
},
Child = new TabControl
{
Template = TabControlTemplate(),
Items = collection,
}
};
Assert.Same(collection[0].Template, template);
Assert.Same(collection[1].Template, template);

8
tests/Avalonia.Controls.UnitTests/ToolTipTests.cs

@ -51,6 +51,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = panel;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -114,6 +115,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -140,6 +142,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -183,6 +186,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -215,6 +219,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = decorator;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -237,6 +242,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = decorator;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -261,6 +267,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = decorator;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
@ -286,6 +293,7 @@ namespace Avalonia.Controls.UnitTests
window.Content = target;
window.ApplyStyling();
window.ApplyTemplate();
window.Presenter.ApplyTemplate();

2
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

@ -77,7 +77,7 @@ namespace Avalonia.Controls.UnitTests
public void Items_Should_Be_Created_Using_ItemConatinerTheme_If_Present()
{
TreeView target;
var theme = new ControlTheme();
var theme = new ControlTheme(typeof(TreeViewItem));
var root = new TestRoot
{

25
tests/Avalonia.Controls.UnitTests/UserControlTests.cs

@ -13,26 +13,23 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Be_Styled_As_UserControl()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
var target = new UserControl();
var root = new TestRoot
{
var target = new UserControl();
var root = new TestRoot
Styles =
{
Styles =
new Style(x => x.OfType<UserControl>())
{
new Style(x => x.OfType<UserControl>())
Setters =
{
Setters =
{
new Setter(TemplatedControl.TemplateProperty, GetTemplate())
}
new Setter(TemplatedControl.TemplateProperty, GetTemplate())
}
},
Child = target,
};
}
},
Child = target,
};
Assert.NotNull(target.Template);
}
Assert.NotNull(target.Template);
}
private FuncControlTemplate GetTemplate()

21
tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs

@ -23,11 +23,8 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture1 = new KeyGesture(Key.A, KeyModifiers.Control);
var gesture2 = new KeyGesture(Key.B, KeyModifiers.Control);
@ -67,13 +64,11 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var commandResult = 0;
var expectedParameter = 1;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
@ -112,12 +107,10 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var isExecuted = false;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
@ -154,12 +147,10 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var clickExecutedCount = 0;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);
@ -208,13 +199,11 @@ namespace Avalonia.Controls.UnitTests.Utils
{
using (AvaloniaLocator.EnterScope())
{
var styler = new Mock<Styler>();
var target = new KeyboardDevice();
var clickExecutedCount = 0;
var commandExecutedCount = 0;
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToConstant(styler.Object);
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock());
var gesture = new KeyGesture(Key.A, KeyModifiers.Control);

6
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -5,6 +5,7 @@ using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reactive.Subjects;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
@ -26,6 +27,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{
public class CompiledBindingExtensionTests
{
static CompiledBindingExtensionTests()
{
RuntimeHelpers.RunClassConstructor(typeof(RelativeSource).TypeHandle);
}
[Fact]
public void ResolvesClrPropertyBasedOnDataContextType()
{

7
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -720,7 +720,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
//ensure binding is set and operational first
Assert.Equal(100.0, tracker.Tag);
Assert.Equal("EndInit 0", tracker.Order.Last());
// EndInit should be second-to-last operation, as last operation will be
// caused by styling being applied on EndInit.
Assert.Equal("EndInit 0", tracker.Order[tracker.Order.Count - 2]);
// Caused by styling.
Assert.Equal("Property Foreground Changed", tracker.Order[tracker.Order.Count - 1]);
}
}

14
tests/Avalonia.UnitTests/TestServices.cs

@ -23,7 +23,6 @@ namespace Avalonia.UnitTests
platform: new AppBuilder().RuntimePlatform,
renderInterface: new MockPlatformRenderInterface(),
standardCursorFactory: Mock.Of<ICursorFactory>(),
styler: new Styler(),
theme: () => CreateSimpleTheme(),
threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true),
fontManagerImpl: new MockFontManagerImpl(),
@ -39,9 +38,6 @@ namespace Avalonia.UnitTests
public static readonly TestServices MockPlatformWrapper = new TestServices(
platform: Mock.Of<IRuntimePlatform>());
public static readonly TestServices MockStyler = new TestServices(
styler: Mock.Of<IStyler>());
public static readonly TestServices MockThreadingInterface = new TestServices(
threadingInterface: Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true));
@ -58,9 +54,6 @@ namespace Avalonia.UnitTests
fontManagerImpl: new MockFontManagerImpl(),
textShaperImpl: new MockTextShaperImpl());
public static readonly TestServices RealStyler = new TestServices(
styler: new Styler());
public static readonly TestServices TextServices = new TestServices(
assetLoader: new AssetLoader(),
renderInterface: new MockPlatformRenderInterface(),
@ -80,7 +73,6 @@ namespace Avalonia.UnitTests
IRenderTimer renderLoop = null,
IScheduler scheduler = null,
ICursorFactory standardCursorFactory = null,
IStyler styler = null,
Func<IStyle> theme = null,
IPlatformThreadingInterface threadingInterface = null,
IFontManagerImpl fontManagerImpl = null,
@ -101,7 +93,6 @@ namespace Avalonia.UnitTests
TextShaperImpl = textShaperImpl;
Scheduler = scheduler;
StandardCursorFactory = standardCursorFactory;
Styler = styler;
Theme = theme;
ThreadingInterface = threadingInterface;
WindowImpl = windowImpl;
@ -121,7 +112,6 @@ namespace Avalonia.UnitTests
public ITextShaperImpl TextShaperImpl { get; }
public IScheduler Scheduler { get; }
public ICursorFactory StandardCursorFactory { get; }
public IStyler Styler { get; }
public Func<IStyle> Theme { get; }
public IPlatformThreadingInterface ThreadingInterface { get; }
public IWindowImpl WindowImpl { get; }
@ -140,8 +130,7 @@ namespace Avalonia.UnitTests
IRenderTimer renderLoop = null,
IScheduler scheduler = null,
ICursorFactory standardCursorFactory = null,
IStyler styler = null,
Func<Styles> theme = null,
Func<IStyle> theme = null,
IPlatformThreadingInterface threadingInterface = null,
IFontManagerImpl fontManagerImpl = null,
ITextShaperImpl textShaperImpl = null,
@ -162,7 +151,6 @@ namespace Avalonia.UnitTests
textShaperImpl: textShaperImpl ?? TextShaperImpl,
scheduler: scheduler ?? Scheduler,
standardCursorFactory: standardCursorFactory ?? StandardCursorFactory,
styler: styler ?? Styler,
theme: theme ?? Theme,
threadingInterface: threadingInterface ?? ThreadingInterface,
windowingPlatform: windowingPlatform ?? WindowingPlatform,

5
tests/Avalonia.UnitTests/UnitTestApplication.cs

@ -68,14 +68,13 @@ namespace Avalonia.UnitTests
.Bind<IPlatformThreadingInterface>().ToConstant(Services.ThreadingInterface)
.Bind<IScheduler>().ToConstant(Services.Scheduler)
.Bind<ICursorFactory>().ToConstant(Services.StandardCursorFactory)
.Bind<IStyler>().ToConstant(Services.Styler)
.Bind<IWindowingPlatform>().ToConstant(Services.WindowingPlatform)
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
var theme = Services.Theme?.Invoke();
if (theme is Styles styles)
if (theme is Style styles)
{
Styles.AddRange(styles);
Styles.AddRange(styles.Children);
}
else if (theme is not null)
{

Loading…
Cancel
Save