Browse Source

Merge remote-tracking branch 'AvaloniaUI/master' into linq-expression-expressionobserver

pull/1667/head
Jeremy Koritzinsky 8 years ago
parent
commit
19ec878dd6
  1. 156
      .editorconfig
  2. 1
      Avalonia.sln
  3. 4
      samples/ControlCatalog/SideBar.xaml
  4. 4
      samples/RenderDemo/SideBar.xaml
  5. 139
      src/Avalonia.Base/AvaloniaObject.cs
  6. 9
      src/Avalonia.Base/IPriorityValueOwner.cs
  7. 4
      src/Avalonia.Base/PriorityValue.cs
  8. 172
      src/Avalonia.Base/ValueStore.cs
  9. 1
      src/Avalonia.Controls/Application.cs
  10. 2
      src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
  11. 2
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
  12. 2
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  13. 1
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  14. 18
      src/Avalonia.Controls/TopLevel.cs
  15. 18
      src/Avalonia.Controls/Window.cs
  16. 5
      src/Avalonia.Controls/WindowBase.cs
  17. 5
      src/Avalonia.Layout/ILayoutRoot.cs
  18. 7
      src/Avalonia.Layout/LayoutManager.cs
  19. 10
      src/Avalonia.Layout/Layoutable.cs
  20. 2
      src/Avalonia.Themes.Default/AutoCompleteBox.xaml
  21. 2
      src/Avalonia.Themes.Default/DropDown.xaml
  22. 4
      src/Avalonia.Themes.Default/MenuItem.xaml
  23. 4
      src/Avalonia.Themes.Default/ScrollBar.xaml
  24. 10
      src/Avalonia.Themes.Default/ScrollViewer.xaml
  25. 7
      src/Avalonia.Themes.Default/Slider.xaml
  26. 4
      src/Avalonia.Themes.Default/TabControl.xaml
  27. 2
      src/Avalonia.Themes.Default/TextBox.xaml
  28. 4
      src/Avalonia.Themes.Default/TreeViewItem.xaml
  29. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  30. 2
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  31. 85
      src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs
  32. 55
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs
  33. 8
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs
  34. 6
      src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
  35. 178
      src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
  36. 2
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
  37. 4
      tests/Avalonia.Base.UnitTests/PriorityValueTests.cs
  38. 14
      tests/Avalonia.Benchmarks/Layout/Measure.cs
  39. 18
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
  40. 123
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  41. 1
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  42. 17
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  43. 2
      tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
  44. 21
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  45. 7
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  46. 325
      tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs
  47. 87
      tests/Avalonia.Layout.UnitTests/LayoutableTests.cs
  48. 28
      tests/Avalonia.LeakTests/ControlTests.cs
  49. 13
      tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs
  50. 14
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs
  51. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  52. 4
      tests/Avalonia.UnitTests/TestRoot.cs
  53. 11
      tests/Avalonia.UnitTests/TestServices.cs
  54. 4
      tests/Avalonia.UnitTests/TestTemplatedRoot.cs
  55. 1
      tests/Avalonia.UnitTests/UnitTestApplication.cs
  56. 29
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs
  57. 20
      tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs

156
.editorconfig

@ -1,11 +1,159 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at http://EditorConfig.org
# editorconfig.org
# top-most EditorConfig file
root = true
# Default settings:
# A newline ending every file
# Use 4 spaces as indentation
[*]
end_of_line = CRLF
insert_final_newline = true
indent_style = space
indent_size = 4
# C# files
[*.cs]
indent_style = space
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current
# avoid this. unless absolutely necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
# prefer var
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
csharp_style_var_elsewhere = true:suggestion
# use language keywords instead of BCL types
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# name all constant fields using PascalCase
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# static fields should have s_ prefix
dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_style.static_prefix_style.required_prefix = s_
dotnet_naming_style.static_prefix_style.capitalization = camel_case
# internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# use accessibility modifiers
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
# Code style defaults
dotnet_sort_system_directives_first = true
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_methods = false:none
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_operators = false:none
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
# Null checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = do_not_ignore
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Xaml files
[*.xaml]
indent_size = 4
# Xml project files
[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
indent_size = 2
# Xml build files
[*.builds]
indent_size = 2
# Xml files
[*.{xml,stylecop,resx,ruleset}]
indent_size = 2
# Xml config files
[*.{props,targets,config,nuspec}]
indent_size = 2
# Shell scripts
[*.sh]
end_of_line = lf
[*.{cmd, bat}]
end_of_line = crlf

1
Avalonia.sln

@ -58,6 +58,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{9B9E
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
src\Shared\SharedAssemblyInfo.cs = src\Shared\SharedAssemblyInfo.cs
EndProjectSection
EndProject

4
samples/ControlCatalog/SideBar.xaml

@ -8,7 +8,7 @@
<TabStrip Name="PART_TabStrip"
MemberSelector="{x:Static TabControl.HeaderSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding Path=SelectedIndex, Mode=TwoWay}">
SelectedIndex="{TemplateBinding SelectedIndex, Mode=TwoWay}">
<TabStrip.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
@ -20,7 +20,7 @@
Margin="8 0 0 0"
MemberSelector="{x:Static TabControl.ContentSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding Path=SelectedIndex}"
SelectedIndex="{TemplateBinding SelectedIndex}"
PageTransition="{TemplateBinding PageTransition}"
Grid.Row="1"/>
</DockPanel>

4
samples/RenderDemo/SideBar.xaml

@ -9,7 +9,7 @@
<TabStrip Name="PART_TabStrip"
MemberSelector="{x:Static TabControl.HeaderSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding Path=SelectedIndex, Mode=TwoWay}">
SelectedIndex="{TemplateBinding SelectedIndex, Mode=TwoWay}">
<TabStrip.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
@ -21,7 +21,7 @@
Margin="8 0 0 0"
MemberSelector="{x:Static TabControl.ContentSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding Path=SelectedIndex}"
SelectedIndex="{TemplateBinding SelectedIndex}"
PageTransition="{TemplateBinding PageTransition}"
Grid.Row="1"/>
</DockPanel>

139
src/Avalonia.Base/AvaloniaObject.cs

@ -29,12 +29,6 @@ namespace Avalonia
/// </summary>
private IAvaloniaObject _inheritanceParent;
/// <summary>
/// The set values/bindings on this object.
/// </summary>
private readonly Dictionary<AvaloniaProperty, PriorityValue> _values =
new Dictionary<AvaloniaProperty, PriorityValue>();
/// <summary>
/// Maintains a list of direct property binding subscriptions so that the binding source
/// doesn't get collected.
@ -52,6 +46,7 @@ namespace Avalonia
private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChanged;
private DeferredSetter<AvaloniaProperty, object> _directDeferredSetter;
private ValueStore _values;
/// <summary>
/// Delayed setter helper for direct properties. Used to fix #855.
@ -228,9 +223,20 @@ namespace Avalonia
{
return ((IDirectPropertyAccessor)GetRegistered(property)).GetValue(this);
}
else if (_values != null)
{
var result = _values.GetValue(property);
if (result == AvaloniaProperty.UnsetValue)
{
result = GetDefaultValue(property);
}
return result;
}
else
{
return GetValueInternal(property);
return GetDefaultValue(property);
}
}
@ -257,7 +263,7 @@ namespace Avalonia
Contract.Requires<ArgumentNullException>(property != null);
VerifyAccess();
return _values.TryGetValue(property, out PriorityValue value) ? value.IsAnimating : false;
return _values?.IsAnimating(property) ?? false;
}
/// <summary>
@ -274,14 +280,7 @@ namespace Avalonia
Contract.Requires<ArgumentNullException>(property != null);
VerifyAccess();
PriorityValue value;
if (_values.TryGetValue(property, out value))
{
return value.Value != AvaloniaProperty.UnsetValue;
}
return false;
return _values?.IsSet(property) ?? false;
}
/// <summary>
@ -369,14 +368,6 @@ namespace Avalonia
}
else
{
PriorityValue v;
if (!_values.TryGetValue(property, out v))
{
v = CreatePriorityValue(property);
_values.Add(property, v);
}
Logger.Verbose(
LogArea.Property,
this,
@ -385,7 +376,12 @@ namespace Avalonia
description,
priority);
return v.Add(source, (int)priority);
if (_values == null)
{
_values = new ValueStore(this);
}
return _values.AddBinding(property, source, priority);
}
}
@ -416,20 +412,12 @@ namespace Avalonia
public void Revalidate(AvaloniaProperty property)
{
VerifyAccess();
PriorityValue value;
if (_values.TryGetValue(property, out value))
{
value.Revalidate();
}
_values?.Revalidate(property);
}
/// <inheritdoc/>
void IPriorityValueOwner.Changed(PriorityValue sender, object oldValue, object newValue)
void IPriorityValueOwner.Changed(AvaloniaProperty property, int priority, object oldValue, object newValue)
{
var property = sender.Property;
var priority = (BindingPriority)sender.ValuePriority;
oldValue = (oldValue == AvaloniaProperty.UnsetValue) ?
GetDefaultValue(property) :
oldValue;
@ -439,7 +427,7 @@ namespace Avalonia
if (!Equals(oldValue, newValue))
{
RaisePropertyChanged(property, oldValue, newValue, priority);
RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)priority);
Logger.Verbose(
LogArea.Property,
@ -448,14 +436,14 @@ namespace Avalonia
property,
oldValue,
newValue,
priority);
(BindingPriority)priority);
}
}
/// <inheritdoc/>
void IPriorityValueOwner.BindingNotificationReceived(PriorityValue sender, BindingNotification notification)
void IPriorityValueOwner.BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
{
UpdateDataValidation(sender.Property, notification);
UpdateDataValidation(property, notification);
}
/// <inheritdoc/>
@ -468,10 +456,7 @@ namespace Avalonia
/// Gets all priority values set on the object.
/// </summary>
/// <returns>A collection of property/value tuples.</returns>
internal IDictionary<AvaloniaProperty, PriorityValue> GetSetValues()
{
return _values;
}
internal IDictionary<AvaloniaProperty, PriorityValue> GetSetValues() => _values?.GetSetValues();
/// <summary>
/// Forces revalidation of properties when a property value changes.
@ -660,68 +645,18 @@ namespace Avalonia
}
}
/// <summary>
/// Creates a <see cref="PriorityValue"/> for a <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The <see cref="PriorityValue"/>.</returns>
private PriorityValue CreatePriorityValue(AvaloniaProperty property)
{
var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(GetType());
Func<object, object> validate2 = null;
if (validate != null)
{
validate2 = v => validate(this, v);
}
PriorityValue result = new PriorityValue(
this,
property,
property.PropertyType,
validate2);
return result;
}
/// <summary>
/// Gets the default value for a property.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The default value.</returns>
private object GetDefaultValue(AvaloniaProperty property)
internal object GetDefaultValue(AvaloniaProperty property)
{
if (property.Inherits && InheritanceParent is AvaloniaObject aobj)
return aobj.GetValueInternal(property);
return aobj.GetValue(property);
return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType());
}
/// <summary>
/// Gets a <see cref="AvaloniaProperty"/> value
/// without check for registered as this can slow getting the value
/// this method is intended for internal usage in AvaloniaObject only
/// it's called only after check the property is registered
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The value.</returns>
private object GetValueInternal(AvaloniaProperty property)
{
object result = AvaloniaProperty.UnsetValue;
PriorityValue value;
if (_values.TryGetValue(property, out value))
{
result = value.Value;
}
if (result == AvaloniaProperty.UnsetValue)
{
result = GetDefaultValue(property);
}
return result;
}
/// <summary>
/// Sets the value of a direct property.
/// </summary>
@ -802,21 +737,13 @@ namespace Avalonia
originalValue?.GetType().FullName ?? "(null)"));
}
PriorityValue v;
if (!_values.TryGetValue(property, out v))
if (_values == null)
{
if (value == AvaloniaProperty.UnsetValue)
{
return;
}
v = CreatePriorityValue(property);
_values.Add(property, v);
_values = new ValueStore(this);
}
LogPropertySet(property, value, priority);
v.SetValue(value, (int)priority);
_values.AddValue(property, value, (int)priority);
}
/// <summary>

9
src/Avalonia.Base/IPriorityValueOwner.cs

@ -13,18 +13,19 @@ namespace Avalonia
/// <summary>
/// Called when a <see cref="PriorityValue"/>'s value changes.
/// </summary>
/// <param name="sender">The source of the change.</param>
/// <param name="property">The the property that has changed.</param>
/// <param name="priority">The priority of the value.</param>
/// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param>
void Changed(PriorityValue sender, object oldValue, object newValue);
void Changed(AvaloniaProperty property, int priority, object oldValue, object newValue);
/// <summary>
/// Called when a <see cref="BindingNotification"/> is received by a
/// <see cref="PriorityValue"/>.
/// </summary>
/// <param name="sender">The source of the change.</param>
/// <param name="property">The the property that has changed.</param>
/// <param name="notification">The notification.</param>
void BindingNotificationReceived(PriorityValue sender, BindingNotification notification);
void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification);
/// <summary>
/// Ensures that the current thread is the UI thread.

4
src/Avalonia.Base/PriorityValue.cs

@ -281,12 +281,12 @@ namespace Avalonia
if (notification == null || notification.HasValue)
{
notify(() => Owner?.Changed(this, old, Value));
notify(() => Owner?.Changed(Property, ValuePriority, old, Value));
}
if (notification != null)
{
Owner?.BindingNotificationReceived(this, notification);
Owner?.BindingNotificationReceived(Property, notification);
}
}
else

172
src/Avalonia.Base/ValueStore.cs

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using Avalonia.Data;
namespace Avalonia
{
internal class ValueStore : IPriorityValueOwner
{
private readonly AvaloniaObject _owner;
private readonly Dictionary<AvaloniaProperty, object> _values =
new Dictionary<AvaloniaProperty, object>();
public ValueStore(AvaloniaObject owner)
{
_owner = owner;
}
public IDisposable AddBinding(
AvaloniaProperty property,
IObservable<object> source,
BindingPriority priority)
{
PriorityValue priorityValue;
if (_values.TryGetValue(property, out var v))
{
priorityValue = v as PriorityValue;
if (priorityValue == null)
{
priorityValue = CreatePriorityValue(property);
priorityValue.SetValue(v, (int)BindingPriority.LocalValue);
_values[property] = priorityValue;
}
}
else
{
priorityValue = CreatePriorityValue(property);
_values.Add(property, priorityValue);
}
return priorityValue.Add(source, (int)priority);
}
public void AddValue(AvaloniaProperty property, object value, int priority)
{
PriorityValue priorityValue;
if (_values.TryGetValue(property, out var v))
{
priorityValue = v as PriorityValue;
if (priorityValue == null)
{
if (priority == (int)BindingPriority.LocalValue)
{
_values[property] = Validate(property, value);
Changed(property, priority, v, value);
return;
}
else
{
priorityValue = CreatePriorityValue(property);
priorityValue.SetValue(v, (int)BindingPriority.LocalValue);
_values[property] = priorityValue;
}
}
}
else
{
if (value == AvaloniaProperty.UnsetValue)
{
return;
}
if (priority == (int)BindingPriority.LocalValue)
{
_values.Add(property, Validate(property, value));
Changed(property, priority, AvaloniaProperty.UnsetValue, value);
return;
}
else
{
priorityValue = CreatePriorityValue(property);
_values.Add(property, priorityValue);
}
}
priorityValue.SetValue(value, priority);
}
public void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
{
((IPriorityValueOwner)_owner).BindingNotificationReceived(property, notification);
}
public void Changed(AvaloniaProperty property, int priority, object oldValue, object newValue)
{
((IPriorityValueOwner)_owner).Changed(property, priority, oldValue, newValue);
}
public IDictionary<AvaloniaProperty, PriorityValue> GetSetValues() => throw new NotImplementedException();
public object GetValue(AvaloniaProperty property)
{
var result = AvaloniaProperty.UnsetValue;
if (_values.TryGetValue(property, out var value))
{
result = (value is PriorityValue priorityValue) ? priorityValue.Value : value;
}
return result;
}
public bool IsAnimating(AvaloniaProperty property)
{
return _values.TryGetValue(property, out var value) ? (value as PriorityValue)?.IsAnimating ?? false : false;
}
public bool IsSet(AvaloniaProperty property)
{
if (_values.TryGetValue(property, out var value))
{
return ((value as PriorityValue)?.Value ?? value) != AvaloniaProperty.UnsetValue;
}
return false;
}
public void Revalidate(AvaloniaProperty property)
{
if (_values.TryGetValue(property, out var value))
{
(value as PriorityValue)?.Revalidate();
}
}
public void VerifyAccess() => _owner.VerifyAccess();
private PriorityValue CreatePriorityValue(AvaloniaProperty property)
{
var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(_owner.GetType());
Func<object, object> validate2 = null;
if (validate != null)
{
validate2 = v => validate(_owner, v);
}
PriorityValue result = new PriorityValue(
this,
property,
property.PropertyType,
validate2);
return result;
}
private object Validate(AvaloniaProperty property, object value)
{
var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(_owner.GetType());
if (validate != null && value != AvaloniaProperty.UnsetValue)
{
return validate(_owner, value);
}
return value;
}
}
}

1
src/Avalonia.Controls/Application.cs

@ -332,7 +332,6 @@ namespace Avalonia
.Bind<IInputManager>().ToConstant(InputManager)
.Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
.Bind<IStyler>().ToConstant(_styler)
.Bind<ILayoutManager>().ToSingleton<LayoutManager>()
.Bind<IApplicationLifecycle>().ToConstant(this)
.Bind<IScheduler>().ToConstant(AvaloniaScheduler.Instance)
.Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance)

2
src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs

@ -28,7 +28,7 @@ namespace Avalonia.Controls.Embedding
{
EnsureInitialized();
ApplyTemplate();
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
LayoutManager.ExecuteInitialLayoutPass(this);
}
private void EnsureInitialized()

2
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs

@ -22,7 +22,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
{
EnsureInitialized();
ApplyTemplate();
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
LayoutManager.ExecuteInitialLayoutPass(this);
}
private void EnsureInitialized()

2
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -518,7 +518,7 @@ namespace Avalonia.Controls.Presenters
}
var container = generator.ContainerFromIndex(index);
var layoutManager = LayoutManager.Instance;
var layoutManager = (Owner.GetVisualRoot() as ILayoutRoot)?.LayoutManager;
// We need to do a layout here because it's possible that the container we moved to
// is only partially visible due to differing item sizes. If the container is only

1
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@ -21,7 +21,6 @@ namespace Avalonia.Controls.Primitives
static AdornerLayer()
{
AdornedElementProperty.Changed.Subscribe(AdornedElementChanged);
IsHitTestVisibleProperty.OverrideDefaultValue(typeof(AdornerLayer), false);
}
public AdornerLayer()

18
src/Avalonia.Controls/TopLevel.cs

@ -54,6 +54,7 @@ namespace Avalonia.Controls
private readonly IApplicationLifecycle _applicationLifecycle;
private readonly IPlatformRenderInterface _renderInterface;
private Size _clientSize;
private ILayoutManager _layoutManager;
/// <summary>
/// Initializes static members of the <see cref="TopLevel"/> class.
@ -147,6 +148,16 @@ namespace Avalonia.Controls
protected set { SetAndRaise(ClientSizeProperty, ref _clientSize, value); }
}
public ILayoutManager LayoutManager
{
get
{
if (_layoutManager == null)
_layoutManager = CreateLayoutManager();
return _layoutManager;
}
}
/// <summary>
/// Gets the platform-specific window implementation.
/// </summary>
@ -235,6 +246,11 @@ namespace Avalonia.Controls
{
return PlatformImpl?.PointToScreen(p) ?? default(Point);
}
/// <summary>
/// Creates the layout manager for this <see cref="TopLevel" />.
/// </summary>
protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager();
/// <summary>
/// Handles a paint notification from <see cref="ITopLevelImpl.Resized"/>.
@ -267,7 +283,7 @@ namespace Avalonia.Controls
ClientSize = clientSize;
Width = clientSize.Width;
Height = clientSize.Height;
LayoutManager.Instance.ExecuteLayoutPass();
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize);
}

18
src/Avalonia.Controls/Window.cs

@ -302,18 +302,17 @@ namespace Avalonia.Controls
internal void Close(bool ignoreCancel)
{
var cancelClosing = false;
try
{
cancelClosing = HandleClosing();
if (!ignoreCancel && HandleClosing())
{
return;
}
}
finally
{
if (ignoreCancel || !cancelClosing)
{
PlatformImpl?.Dispose();
HandleClosed();
}
PlatformImpl?.Dispose();
HandleClosed();
}
}
@ -324,6 +323,7 @@ namespace Avalonia.Controls
{
var args = new CancelEventArgs();
Closing?.Invoke(this, args);
return args.Cancel;
}
@ -374,7 +374,7 @@ namespace Avalonia.Controls
EnsureInitialized();
IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
LayoutManager.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing())
{
@ -416,7 +416,7 @@ namespace Avalonia.Controls
EnsureInitialized();
SetWindowStartupLocation();
IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
LayoutManager.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing())
{

5
src/Avalonia.Controls/WindowBase.cs

@ -179,10 +179,9 @@ namespace Avalonia.Controls
if (!_hasExecutedInitialLayoutPass)
{
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
LayoutManager.ExecuteInitialLayoutPass(this);
_hasExecutedInitialLayoutPass = true;
}
PlatformImpl?.Show();
Renderer?.Start();
}
@ -262,7 +261,7 @@ namespace Avalonia.Controls
Height = clientSize.Height;
}
ClientSize = clientSize;
LayoutManager.Instance.ExecuteLayoutPass();
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize);
}

5
src/Avalonia.Layout/ILayoutRoot.cs

@ -22,5 +22,10 @@ namespace Avalonia.Layout
/// The scaling factor to use in layout.
/// </summary>
double LayoutScaling { get; }
/// <summary>
/// Associated instance of layout manager
/// </summary>
ILayoutManager LayoutManager { get; }
}
}

7
src/Avalonia.Layout/LayoutManager.cs

@ -19,11 +19,6 @@ namespace Avalonia.Layout
private bool _queued;
private bool _running;
/// <summary>
/// Gets the layout manager.
/// </summary>
public static ILayoutManager Instance => AvaloniaLocator.Current.GetService<ILayoutManager>();
/// <inheritdoc/>
public void InvalidateMeasure(ILayoutable control)
{
@ -170,7 +165,7 @@ namespace Avalonia.Layout
{
root.Measure(Size.Infinity);
}
else
else if (control.PreviousMeasure.HasValue)
{
control.Measure(control.PreviousMeasure.Value);
}

10
src/Avalonia.Layout/Layoutable.cs

@ -389,7 +389,7 @@ namespace Avalonia.Layout
if (((ILayoutable)this).IsAttachedToVisualTree)
{
LayoutManager.Instance?.InvalidateMeasure(this);
(VisualRoot as ILayoutRoot)?.LayoutManager.InvalidateMeasure(this);
InvalidateVisual();
}
OnMeasureInvalidated();
@ -406,12 +406,8 @@ namespace Avalonia.Layout
Logger.Verbose(LogArea.Layout, this, "Invalidated arrange");
IsArrangeValid = false;
if (((ILayoutable)this).IsAttachedToVisualTree)
{
LayoutManager.Instance?.InvalidateArrange(this);
InvalidateVisual();
}
(VisualRoot as ILayoutRoot)?.LayoutManager?.InvalidateArrange(this);
InvalidateVisual();
}
}

2
src/Avalonia.Themes.Default/AutoCompleteBox.xaml

@ -16,7 +16,7 @@
DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}" />
<Popup Name="PART_Popup"
MinWidth="{TemplateBinding Bounds.Width}"
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
PlacementTarget="{TemplateBinding}"
StaysOpen="False">

2
src/Avalonia.Themes.Default/DropDown.xaml

@ -32,7 +32,7 @@
</ToggleButton>
<Popup Name="PART_Popup"
IsOpen="{TemplateBinding IsDropDownOpen, Mode=TwoWay}"
MinWidth="{TemplateBinding Bounds.Width}"
MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
PlacementTarget="{TemplateBinding}"
StaysOpen="False">

4
src/Avalonia.Themes.Default/MenuItem.xaml

@ -45,7 +45,7 @@
<Popup Name="PART_Popup"
PlacementMode="Right"
StaysOpen="True"
IsOpen="{TemplateBinding Path=IsSubMenuOpen, Mode=TwoWay}"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"
ObeyScreenEdges="True">
<Border Background="{TemplateBinding Background}"
BorderBrush="{DynamicResource ThemeBorderMidBrush}"
@ -92,7 +92,7 @@
</ContentPresenter.DataTemplates>
</ContentPresenter>
<Popup Name="PART_Popup"
IsOpen="{TemplateBinding Path=IsSubMenuOpen, Mode=TwoWay}"
IsOpen="{TemplateBinding IsSubMenuOpen, Mode=TwoWay}"
StaysOpen="True"
ObeyScreenEdges="True">
<Border Background="{TemplateBinding Background}"

4
src/Avalonia.Themes.Default/ScrollBar.xaml

@ -16,7 +16,7 @@
Grid.Column="1"
Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Path=Value, Mode=TwoWay}"
Value="{TemplateBinding Value, Mode=TwoWay}"
ViewportSize="{TemplateBinding ViewportSize}"
Orientation="{TemplateBinding Orientation}">
<Track.DecreaseButton>
@ -67,7 +67,7 @@
Grid.Column="1"
Minimum="{TemplateBinding Minimum}"
Maximum="{TemplateBinding Maximum}"
Value="{TemplateBinding Path=Value, Mode=TwoWay}"
Value="{TemplateBinding Value, Mode=TwoWay}"
ViewportSize="{TemplateBinding ViewportSize}"
Orientation="{TemplateBinding Orientation}">
<Track.DecreaseButton>

10
src/Avalonia.Themes.Default/ScrollViewer.xaml

@ -9,21 +9,21 @@
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}"
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}"
Content="{TemplateBinding Content}"
Extent="{TemplateBinding Path=Extent, Mode=TwoWay}"
Extent="{TemplateBinding Extent, Mode=TwoWay}"
Margin="{TemplateBinding Padding}"
Offset="{TemplateBinding Path=Offset, Mode=TwoWay}"
Viewport="{TemplateBinding Path=Viewport, Mode=TwoWay}"/>
Offset="{TemplateBinding Offset, Mode=TwoWay}"
Viewport="{TemplateBinding Viewport, Mode=TwoWay}"/>
<ScrollBar Name="horizontalScrollBar"
Orientation="Horizontal"
Maximum="{TemplateBinding HorizontalScrollBarMaximum}"
Value="{TemplateBinding Path=HorizontalScrollBarValue, Mode=TwoWay}"
Value="{TemplateBinding HorizontalScrollBarValue, Mode=TwoWay}"
ViewportSize="{TemplateBinding HorizontalScrollBarViewportSize}"
Visibility="{TemplateBinding HorizontalScrollBarVisibility}"
Grid.Row="1"/>
<ScrollBar Name="verticalScrollBar"
Orientation="Vertical"
Maximum="{TemplateBinding VerticalScrollBarMaximum}"
Value="{TemplateBinding Path=VerticalScrollBarValue, Mode=TwoWay}"
Value="{TemplateBinding VerticalScrollBarValue, Mode=TwoWay}"
ViewportSize="{TemplateBinding VerticalScrollBarViewportSize}"
Visibility="{TemplateBinding VerticalScrollBarVisibility}"
Grid.Column="1"/>

7
src/Avalonia.Themes.Default/Slider.xaml

@ -11,7 +11,7 @@
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Name="TrackBackground" Grid.Row="1" Height="4" Margin="6,0" VerticalAlignment="Center"/>
<Track Name="PART_Track" Grid.Row="1">
<Track Name="PART_Track" Grid.Row="1" Orientation="Horizontal">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton"
Classes="repeattrack" />
@ -46,7 +46,7 @@
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Name="TrackBackground" Grid.Column="1" Width="4" Margin="0,6" HorizontalAlignment="Center"/>
<Track Name="PART_Track" Grid.Column="1">
<Track Name="PART_Track" Grid.Column="1" Orientation="Vertical">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton"
Classes="repeattrack" />
@ -72,8 +72,7 @@
<Style Selector="Slider /template/ Track#PART_Track">
<Setter Property="Minimum" Value="{TemplateBinding Minimum}"/>
<Setter Property="Maximum" Value="{TemplateBinding Maximum}"/>
<Setter Property="Value" Value="{TemplateBinding Path=Value, Mode=TwoWay}"/>
<Setter Property="Orientation" Value="{TemplateBinding Orientation}"/>
<Setter Property="Value" Value="{TemplateBinding Value, Mode=TwoWay}"/>
</Style>
<Style Selector="Slider /template/ Border#TrackBackground">
<Setter Property="BorderThickness" Value="2"/>

4
src/Avalonia.Themes.Default/TabControl.xaml

@ -9,11 +9,11 @@
<TabStrip Name="PART_TabStrip"
MemberSelector="{x:Static TabControl.HeaderSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding Path=SelectedIndex, Mode=TwoWay}"/>
SelectedIndex="{TemplateBinding SelectedIndex, Mode=TwoWay}"/>
<Carousel Name="PART_Content"
MemberSelector="{x:Static TabControl.ContentSelector}"
Items="{TemplateBinding Items}"
SelectedIndex="{TemplateBinding Path=SelectedIndex}"
SelectedIndex="{TemplateBinding SelectedIndex}"
PageTransition="{TemplateBinding PageTransition}"
Grid.Row="1"/>
</DockPanel>

2
src/Avalonia.Themes.Default/TextBox.xaml

@ -36,7 +36,7 @@
<TextBlock Name="watermark"
Opacity="0.5"
Text="{TemplateBinding Watermark}"
IsVisible="{TemplateBinding Path=Text, Converter={x:Static StringConverters.NullOrEmpty}}"/>
IsVisible="{TemplateBinding Text, Converter={x:Static StringConverters.NullOrEmpty}}"/>
<TextPresenter Name="PART_TextPresenter"
Text="{TemplateBinding Text, Mode=TwoWay}"
CaretIndex="{TemplateBinding CaretIndex}"

4
src/Avalonia.Themes.Default/TreeViewItem.xaml

@ -7,14 +7,12 @@
<Grid ColumnDefinitions="16, Auto">
<ToggleButton Name="expander"
Focusable="False"
IsChecked="{TemplateBinding Path=IsExpanded, Mode=TwoWay}"/>
IsChecked="{TemplateBinding IsExpanded, Mode=TwoWay}"/>
<ContentPresenter Name="PART_HeaderPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Header}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"
TemplatedControl.IsTemplateFocusTarget="True"
Grid.Column="1"/>

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -48,7 +48,6 @@
<Compile Include="Converters\SelectorTypeConverter.cs" />
<Compile Include="MarkupExtensions\BindingExtension.cs" />
<Compile Include="MarkupExtensions\RelativeSourceExtension.cs" />
<Compile Include="MarkupExtensions\TemplateBindingExtension.cs" />
<Compile Include="PortableXaml\AvaloniaTypeAttributeProvider.cs" />
<Compile Include="PortableXaml\AvaloniaXamlType.cs" />
<Compile Include="PortableXaml\TypeDescriptorExtensions.cs" />

2
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -139,7 +139,7 @@ namespace Avalonia.Markup.Xaml
{
uriString = new Uri(baseUri, uri).AbsoluteUri;
}
throw new XamlLoadException("Error loading xaml at " + uriString, e);
throw new XamlLoadException("Error loading xaml at " + uriString + ": " + e.Message, e);
}
}
}

85
src/Markup/Avalonia.Markup.Xaml/Converters/AvaloniaPropertyTypeConverter.cs

@ -2,19 +2,21 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Styling;
using Portable.Xaml;
using Portable.Xaml.ComponentModel;
using Portable.Xaml.Markup;
namespace Avalonia.Markup.Xaml.Converters
{
using Avalonia.Styling;
using Portable.Xaml;
using Portable.Xaml.ComponentModel;
using System.ComponentModel;
using Portable.Xaml.Markup;
public class AvaloniaPropertyTypeConverter : TypeConverter
{
private static readonly Regex regex = new Regex(@"^\(?(\w*)\.(\w*)\)?|(.*)$");
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
@ -22,65 +24,58 @@ namespace Avalonia.Markup.Xaml.Converters
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var s = (string)value;
var (owner, propertyName) = ParseProperty((string)value);
var ownerType = TryResolveOwnerByName(context, owner) ??
context.GetFirstAmbientValue<ControlTemplate>()?.TargetType ??
context.GetFirstAmbientValue<Style>()?.Selector?.TargetType;
string typeName;
string propertyName;
Type type = null;
if (ownerType == null)
{
throw new XamlLoadException(
$"Could not determine the owner type for property '{propertyName}'. " +
"Please fully qualify the property name or specify a target type on " +
"the containing template.");
}
ParseProperty(s, out typeName, out propertyName);
var property = AvaloniaPropertyRegistry.Instance.FindRegistered(ownerType, propertyName);
if (typeName == null)
if (property == null)
{
var style = context.GetFirstAmbientValue<Style>();
throw new XamlLoadException($"Could not find AvaloniaProperty '{ownerType.Name}.{propertyName}'.");
}
type = style?.Selector?.TargetType;
return property;
}
if (type == null)
{
throw new Exception(
"Could not determine the target type. Please fully qualify the property name.");
}
}
else
private Type TryResolveOwnerByName(ITypeDescriptorContext context, string owner)
{
if (owner != null)
{
var typeResolver = context.GetService<IXamlTypeResolver>();
type = typeResolver.Resolve(typeName);
var resolver = context.GetService<IXamlTypeResolver>();
var result = resolver.Resolve(owner);
if (type == null)
if (result == null)
{
throw new Exception($"Could not find type '{typeName}'.");
throw new XamlLoadException($"Could not find type '{owner}'.");
}
}
AvaloniaProperty property = AvaloniaPropertyRegistry.Instance.FindRegistered(type, propertyName);
if (property == null)
{
throw new Exception(
$"Could not find AvaloniaProperty '{type.Name}.{propertyName}'.");
return result;
}
return property;
return null;
}
private void ParseProperty(string s, out string typeName, out string propertyName)
private (string owner, string property) ParseProperty(string s)
{
var split = s.Split('.');
var result = regex.Match(s);
if (split.Length == 1)
{
typeName = null;
propertyName = split[0];
}
else if (split.Length == 2)
if (result.Groups[1].Success)
{
typeName = split[0];
propertyName = split[1];
return (result.Groups[1].Value, result.Groups[2].Value);
}
else
{
throw new Exception($"Invalid property name: '{s}'.");
return (null, result.Groups[3].Value);
}
}
}

55
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs

@ -1,55 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Data;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
using System;
using System.ComponentModel;
using Avalonia.Data.Converters;
using Avalonia.Markup.Data;
using Portable.Xaml.ComponentModel;
using Portable.Xaml.Markup;
[MarkupExtensionReturnType(typeof(IBinding))]
public class TemplateBindingExtension : MarkupExtension
{
public TemplateBindingExtension()
{
}
public TemplateBindingExtension(string path)
{
Path = path;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var context = (ITypeDescriptorContext)serviceProvider;
return new Binding
{
Converter = Converter,
ElementName = ElementName,
Mode = Mode,
RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
Path = Path ?? string.Empty,
Priority = Priority,
TypeResolver = context.ResolveType
};
}
public IValueConverter Converter { get; set; }
public string ElementName { get; set; }
public object FallbackValue { get; set; }
public BindingMode Mode { get; set; }
[ConstructorArgument("path")]
public string Path { get; set; }
public BindingPriority Priority { get; set; } = BindingPriority.TemplatedParent;
}
}

8
src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs

@ -44,15 +44,17 @@ namespace Portable.Xaml.ComponentModel
var amb = ctx.GetService<IAmbientProvider>();
var sc = ctx.GetService<IXamlSchemaContextProvider>().SchemaContext;
return amb.GetFirstAmbientValue(sc.GetXamlType(typeof(T))) as T;
// Because GetFirstAmbientValue uses XamlType.CanAssignTo it returns values that
// aren't actually of the correct type. Use GetAllAmbientValues instead.
return amb.GetAllAmbientValues(sc.GetXamlType(typeof(T))).OfType<T>().FirstOrDefault();
}
public static T GetLastOrDefaultAmbientValue<T>(this ITypeDescriptorContext ctx) where T : class
{
return ctx.GetAllambientValues<T>().LastOrDefault() as T;
return ctx.GetAllAmbientValues<T>().LastOrDefault() as T;
}
public static IEnumerable<T> GetAllambientValues<T>(this ITypeDescriptorContext ctx) where T : class
public static IEnumerable<T> GetAllAmbientValues<T>(this ITypeDescriptorContext ctx) where T : class
{
var amb = ctx.GetService<IAmbientProvider>();
var sc = ctx.GetService<IXamlSchemaContextProvider>().SchemaContext;

6
src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs

@ -1,6 +1,7 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Metadata;
@ -14,7 +15,8 @@ namespace Avalonia.Markup.Xaml.Templates
[TemplateContent]
public object Content { get; set; }
public IControl Build(ITemplatedControl control)
=> TemplateContent.Load(Content);
public Type TargetType { get; set; }
public IControl Build(ITemplatedControl control) => TemplateContent.Load(Content);
}
}

178
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs

@ -0,0 +1,178 @@
using System;
using System.Globalization;
using System.Reactive.Subjects;
using Avalonia.Data.Converters;
using Avalonia.Reactive;
namespace Avalonia.Data
{
/// <summary>
/// A XAML binding to a property on a control's templated parent.
/// </summary>
public class TemplateBinding : SingleSubscriberObservableBase<object>,
IBinding,
IDescription,
ISubject<object>
{
private IStyledElement _target;
private Type _targetType;
public TemplateBinding()
{
}
public TemplateBinding(AvaloniaProperty property)
{
Property = property;
}
/// <inheritdoc/>
public InstancedBinding Initiate(
IAvaloniaObject target,
AvaloniaProperty targetProperty,
object anchor = null,
bool enableDataValidation = false)
{
// Usually each `TemplateBinding` will only be instantiated once; in this case we can
// use the `TemplateBinding` object itself as the instanced binding in order to save
// allocating a new object. If the binding *is* instantiated more than once (which can
// happen if it appears in a `Setter` for example, then just make a clone and instantiate
// that.
if (_target == null)
{
_target = (IStyledElement)target;
_targetType = targetProperty?.PropertyType;
return new InstancedBinding(
this,
Mode == BindingMode.Default ? BindingMode.OneWay : Mode,
BindingPriority.TemplatedParent);
}
else
{
var clone = new TemplateBinding
{
Converter = Converter,
ConverterParameter = ConverterParameter,
Property = Property,
};
return clone.Initiate(target, targetProperty, anchor, enableDataValidation);
}
}
/// <summary>
/// Gets or sets the <see cref="IValueConverter"/> to use.
/// </summary>
public IValueConverter Converter { get; set; }
/// <summary>
/// Gets or sets a parameter to pass to <see cref="Converter"/>.
/// </summary>
public object ConverterParameter { get; set; }
/// <summary>
/// Gets or sets the binding mode.
/// </summary>
public BindingMode Mode { get; set; }
/// <summary>
/// Gets or sets the name of the source property on the templated parent.
/// </summary>
public AvaloniaProperty Property { get; set; }
/// <inheritdoc/>
public string Description => "TemplateBinding: " + Property;
void IObserver<object>.OnCompleted() => throw new NotImplementedException();
void IObserver<object>.OnError(Exception error) => throw new NotImplementedException();
void IObserver<object>.OnNext(object value)
{
if (_target.TemplatedParent != null && Property != null)
{
if (Converter != null)
{
value = Converter.ConvertBack(
value,
Property.PropertyType,
ConverterParameter,
CultureInfo.CurrentCulture);
}
_target.TemplatedParent.SetValue(Property, value, BindingPriority.TemplatedParent);
}
}
protected override void Subscribed()
{
TemplatedParentChanged();
_target.PropertyChanged += TargetPropertyChanged;
}
protected override void Unsubscribed()
{
if (_target.TemplatedParent != null)
{
_target.TemplatedParent.PropertyChanged -= TemplatedParentPropertyChanged;
}
_target.PropertyChanged -= TargetPropertyChanged;
}
private void PublishValue()
{
if (_target.TemplatedParent != null)
{
var value = Property != null ?
_target.TemplatedParent.GetValue(Property) :
_target.TemplatedParent;
if (Converter != null)
{
value = Converter.Convert(value, _targetType, ConverterParameter, CultureInfo.CurrentCulture);
}
PublishNext(value);
}
else
{
PublishNext(AvaloniaProperty.UnsetValue);
}
}
private void TemplatedParentChanged()
{
if (_target.TemplatedParent != null)
{
_target.TemplatedParent.PropertyChanged += TemplatedParentPropertyChanged;
}
PublishValue();
}
private void TargetPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == StyledElement.TemplatedParentProperty)
{
var oldValue = (IAvaloniaObject)e.OldValue;
var newValue = (IAvaloniaObject)e.OldValue;
if (oldValue != null)
{
oldValue.PropertyChanged -= TemplatedParentPropertyChanged;
}
TemplatedParentChanged();
}
}
private void TemplatedParentPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == Property)
{
PublishNext(_target.TemplatedParent.GetValue(Property));
}
}
}
}

2
src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs

@ -49,7 +49,7 @@ namespace Avalonia.Win32.Interop.Wpf
protected override void HandleResized(Size clientSize)
{
ClientSize = clientSize;
LayoutManager.Instance.ExecuteLayoutPass();
LayoutManager.ExecuteLayoutPass();
Renderer?.Resized(clientSize);
}

4
tests/Avalonia.Base.UnitTests/PriorityValueTests.cs

@ -167,7 +167,7 @@ namespace Avalonia.Base.UnitTests
target.Add(Single("foo"), 0);
owner.Verify(x => x.Changed(target, AvaloniaProperty.UnsetValue, "foo"));
owner.Verify(x => x.Changed(target.Property, target.ValuePriority, AvaloniaProperty.UnsetValue, "foo"));
}
[Fact]
@ -180,7 +180,7 @@ namespace Avalonia.Base.UnitTests
target.Add(subject, 0);
subject.OnNext("bar");
owner.Verify(x => x.Changed(target, "foo", "bar"));
owner.Verify(x => x.Changed(target.Property, target.ValuePriority, "foo", "bar"));
}
[Fact]

14
tests/Avalonia.Benchmarks/Layout/Measure.cs

@ -8,26 +8,18 @@ using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Layout
{
[MemoryDiagnoser]
public class Measure : IDisposable
public class Measure
{
private IDisposable _app;
private TestRoot root;
private List<Control> controls = new List<Control>();
public Measure()
{
_app = UnitTestApplication.Start(TestServices.RealLayoutManager);
var panel = new StackPanel();
root = new TestRoot { Child = panel };
controls.Add(panel);
CreateChildren(panel, 3, 5);
LayoutManager.Instance.ExecuteInitialLayoutPass(root);
}
public void Dispose()
{
_app.Dispose();
root.LayoutManager.ExecuteInitialLayoutPass(root);
}
[Benchmark]
@ -43,7 +35,7 @@ namespace Avalonia.Benchmarks.Layout
}
}
LayoutManager.Instance.ExecuteLayoutPass();
root.LayoutManager.ExecuteLayoutPass();
}
private void CreateChildren(IPanel parent, int childCount, int iterations)

18
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs

@ -12,6 +12,7 @@ using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Xunit;
namespace Avalonia.Controls.UnitTests.Presenters
@ -219,13 +220,13 @@ namespace Avalonia.Controls.UnitTests.Presenters
[Fact]
public void Changing_VirtualizationMode_None_To_Simple_Should_Add_Correct_Number_Of_Controls()
{
using (UnitTestApplication.Start(TestServices.RealLayoutManager))
using (UnitTestApplication.Start(new TestServices()))
{
var target = CreateTarget(mode: ItemVirtualizationMode.None);
var scroll = (ScrollContentPresenter)target.Parent;
var scroll = (TestScroller)target.Parent;
scroll.Measure(new Size(100, 100));
scroll.Arrange(new Rect(0, 0, 100, 100));
scroll.Width = scroll.Height = 100;
scroll.LayoutManager.ExecuteInitialLayoutPass(scroll);
// Ensure than an intermediate measure pass doesn't add more controls than it
// should. This can happen if target gets measured with Size.Infinity which
@ -237,7 +238,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
};
target.VirtualizationMode = ItemVirtualizationMode.Simple;
LayoutManager.Instance.ExecuteLayoutPass();
((ILayoutRoot)scroll.GetVisualRoot()).LayoutManager.ExecuteLayoutPass();
Assert.Equal(10, target.Panel.Children.Count);
}
@ -315,12 +316,17 @@ namespace Avalonia.Controls.UnitTests.Presenters
});
}
private class TestScroller : ScrollContentPresenter, IRenderRoot
private class TestScroller : ScrollContentPresenter, IRenderRoot, ILayoutRoot
{
public IRenderer Renderer { get; }
public Size ClientSize { get; }
public double RenderScaling => 1;
public Size MaxClientSize => Size.Infinity;
public double LayoutScaling => 1;
public ILayoutManager LayoutManager { get; } = new LayoutManager();
public IRenderTarget CreateRenderTarget()
{
throw new NotImplementedException();

123
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@ -12,6 +12,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.UnitTests;
@ -564,11 +565,10 @@ namespace Avalonia.Controls.UnitTests.Presenters
[Fact]
public void Scrolling_To_Item_In_Zero_Sized_Presenter_Doesnt_Throw()
{
using (UnitTestApplication.Start(TestServices.RealLayoutManager))
using (UnitTestApplication.Start(new TestServices()))
{
var target = CreateTarget(itemCount: 10);
var items = (IList<string>)target.Items;
target.ApplyTemplate();
target.Measure(Size.Empty);
target.Arrange(Rect.Empty);
@ -723,10 +723,10 @@ namespace Avalonia.Controls.UnitTests.Presenters
public void GetControlInDirection_Down_Should_Return_Existing_Container_If_Materialized()
{
var target = CreateTarget();
var scroller = (TestScroller)target.Parent;
target.ApplyTemplate();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
scroller.Width = scroller.Height = 100;
scroller.LayoutManager.ExecuteInitialLayoutPass(scroller);
var from = target.Panel.Children[5];
var result = ((ILogicalScrollable)target).GetControlInDirection(
@ -740,10 +740,10 @@ namespace Avalonia.Controls.UnitTests.Presenters
public void GetControlInDirection_Down_Should_Scroll_If_Necessary()
{
var target = CreateTarget();
var scroller = (TestScroller)target.Parent;
target.ApplyTemplate();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
scroller.Width = scroller.Height = 100;
scroller.LayoutManager.ExecuteInitialLayoutPass(scroller);
var from = target.Panel.Children[9];
var result = ((ILogicalScrollable)target).GetControlInDirection(
@ -757,44 +757,40 @@ namespace Avalonia.Controls.UnitTests.Presenters
[Fact]
public void GetControlInDirection_Down_Should_Scroll_If_Partially_Visible()
{
using (UnitTestApplication.Start(TestServices.RealLayoutManager))
{
var target = CreateTarget();
var scroller = (ScrollContentPresenter)target.Parent;
var target = CreateTarget();
var scroller = (TestScroller)target.Parent;
scroller.Measure(new Size(100, 95));
scroller.Arrange(new Rect(0, 0, 100, 95));
scroller.Width = 100;
scroller.Height = 95;
scroller.LayoutManager.ExecuteInitialLayoutPass(scroller);
var from = target.Panel.Children[8];
var result = ((ILogicalScrollable)target).GetControlInDirection(
NavigationDirection.Down,
from);
var from = target.Panel.Children[8];
var result = ((ILogicalScrollable)target).GetControlInDirection(
NavigationDirection.Down,
from);
Assert.Equal(new Vector(0, 1), ((ILogicalScrollable)target).Offset);
Assert.Same(target.Panel.Children[8], result);
}
Assert.Equal(new Vector(0, 1), ((ILogicalScrollable)target).Offset);
Assert.Same(target.Panel.Children[8], result);
}
[Fact]
public void GetControlInDirection_Up_Should_Scroll_If_Partially_Visible_Item_Is_Currently_Shown()
{
using (UnitTestApplication.Start(TestServices.RealLayoutManager))
{
var target = CreateTarget();
var scroller = (ScrollContentPresenter)target.Parent;
var target = CreateTarget();
var scroller = (TestScroller)target.Parent;
scroller.Measure(new Size(100, 95));
scroller.Arrange(new Rect(0, 0, 100, 95));
((ILogicalScrollable)target).Offset = new Vector(0, 11);
scroller.Width = 100;
scroller.Height = 95;
scroller.LayoutManager.ExecuteInitialLayoutPass(scroller);
((ILogicalScrollable)target).Offset = new Vector(0, 11);
var from = target.Panel.Children[1];
var result = ((ILogicalScrollable)target).GetControlInDirection(
NavigationDirection.Up,
from);
var from = target.Panel.Children[1];
var result = ((ILogicalScrollable)target).GetControlInDirection(
NavigationDirection.Up,
from);
Assert.Equal(new Vector(0, 10), ((ILogicalScrollable)target).Offset);
Assert.Same(target.Panel.Children[0], result);
}
Assert.Equal(new Vector(0, 10), ((ILogicalScrollable)target).Offset);
Assert.Same(target.Panel.Children[0], result);
}
[Fact]
@ -835,10 +831,10 @@ namespace Avalonia.Controls.UnitTests.Presenters
public void GetControlInDirection_Right_Should_Return_Existing_Container_If_Materialized()
{
var target = CreateTarget(orientation: Orientation.Horizontal);
var scroller = (TestScroller)target.Parent;
target.ApplyTemplate();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
scroller.Width = scroller.Height = 100;
scroller.LayoutManager.ExecuteInitialLayoutPass(scroller);
var from = target.Panel.Children[5];
var result = ((ILogicalScrollable)target).GetControlInDirection(
@ -852,10 +848,10 @@ namespace Avalonia.Controls.UnitTests.Presenters
public void GetControlInDirection_Right_Should_Scroll_If_Necessary()
{
var target = CreateTarget(orientation: Orientation.Horizontal);
var scroller = (TestScroller)target.Parent;
target.ApplyTemplate();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
scroller.Width = scroller.Height = 100;
scroller.LayoutManager.ExecuteInitialLayoutPass(scroller);
var from = target.Panel.Children[9];
var result = ((ILogicalScrollable)target).GetControlInDirection(
@ -869,32 +865,31 @@ namespace Avalonia.Controls.UnitTests.Presenters
[Fact]
public void GetControlInDirection_Right_Should_Scroll_If_Partially_Visible()
{
using (UnitTestApplication.Start(TestServices.RealLayoutManager))
{
var target = CreateTarget(orientation: Orientation.Horizontal);
var scroller = (ScrollContentPresenter)target.Parent;
var target = CreateTarget(orientation: Orientation.Horizontal);
var scroller = (TestScroller)target.Parent;
scroller.Measure(new Size(95, 100));
scroller.Arrange(new Rect(0, 0, 95, 100));
scroller.Width = 95;
scroller.Height = 100;
scroller.LayoutManager.ExecuteInitialLayoutPass(scroller);
var from = target.Panel.Children[8];
var result = ((ILogicalScrollable)target).GetControlInDirection(
NavigationDirection.Right,
from);
var from = target.Panel.Children[8];
var result = ((ILogicalScrollable)target).GetControlInDirection(
NavigationDirection.Right,
from);
Assert.Equal(new Vector(1, 0), ((ILogicalScrollable)target).Offset);
Assert.Same(target.Panel.Children[8], result);
}
Assert.Equal(new Vector(1, 0), ((ILogicalScrollable)target).Offset);
Assert.Same(target.Panel.Children[8], result);
}
[Fact]
public void GetControlInDirection_Left_Should_Scroll_If_Partially_Visible_Item_Is_Currently_Shown()
{
var target = CreateTarget(orientation: Orientation.Horizontal);
var scroller = (TestScroller)target.Parent;
target.ApplyTemplate();
target.Measure(new Size(95, 100));
target.Arrange(new Rect(0, 0, 95, 100));
scroller.Width = 95;
scroller.Height = 100;
scroller.LayoutManager.ExecuteInitialLayoutPass(scroller);
((ILogicalScrollable)target).Offset = new Vector(11, 0);
var from = target.Panel.Children[1];
@ -1010,7 +1005,6 @@ namespace Avalonia.Controls.UnitTests.Presenters
};
scroller.UpdateChild();
return result;
}
@ -1032,12 +1026,18 @@ namespace Avalonia.Controls.UnitTests.Presenters
});
}
private class TestScroller : ScrollContentPresenter, IRenderRoot
private class TestScroller : ScrollContentPresenter, IRenderRoot, ILayoutRoot
{
public IRenderer Renderer { get; }
public Size ClientSize { get; }
public double RenderScaling => 1;
public Size MaxClientSize => Size.Infinity;
public double LayoutScaling => 1;
public ILayoutManager LayoutManager { get; } = new LayoutManager();
public IRenderTarget CreateRenderTarget()
{
throw new NotImplementedException();
@ -1057,6 +1057,11 @@ namespace Avalonia.Controls.UnitTests.Presenters
{
throw new NotImplementedException();
}
protected override Size MeasureOverride(Size availableSize)
{
return base.MeasureOverride(availableSize);
}
}
private class TestItemsPresenter : ItemsPresenter

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

@ -306,7 +306,6 @@ namespace Avalonia.Controls.UnitTests.Primitives
var renderInterface = new Mock<IPlatformRenderInterface>();
AvaloniaLocator.CurrentMutable
.Bind<ILayoutManager>().ToTransient<LayoutManager>()
.Bind<IGlobalStyles>().ToFunc(() => globalStyles.Object)
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformMock())
.Bind<IStyler>().ToTransient<Styler>()

17
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -74,15 +74,16 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Layout_Pass_Should_Not_Be_Automatically_Scheduled()
{
var services = TestServices.StyledWindow.With(layoutManager: Mock.Of<ILayoutManager>());
var services = TestServices.StyledWindow;
using (UnitTestApplication.Start(services))
{
var impl = new Mock<ITopLevelImpl>();
var target = new TestTopLevel(impl.Object);
var target = new TestTopLevel(impl.Object, Mock.Of<ILayoutManager>());
// The layout pass should be scheduled by the derived class.
var layoutManagerMock = Mock.Get(LayoutManager.Instance);
var layoutManagerMock = Mock.Get(target.LayoutManager);
layoutManagerMock.Verify(x => x.ExecuteLayoutPass(), Times.Never);
}
}
@ -107,7 +108,7 @@ namespace Avalonia.Controls.UnitTests
}
};
LayoutManager.Instance.ExecuteInitialLayoutPass(target);
target.LayoutManager.ExecuteInitialLayoutPass(target);
Assert.Equal(new Rect(0, 0, 321, 432), target.Bounds);
}
@ -122,7 +123,7 @@ namespace Avalonia.Controls.UnitTests
impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
var target = new TestTopLevel(impl.Object);
LayoutManager.Instance.ExecuteLayoutPass();
target.LayoutManager.ExecuteLayoutPass();
Assert.Equal(double.NaN, target.Width);
Assert.Equal(double.NaN, target.Height);
@ -248,13 +249,17 @@ namespace Avalonia.Controls.UnitTests
private class TestTopLevel : TopLevel
{
private readonly ILayoutManager _layoutManager;
public bool IsClosed { get; private set; }
public TestTopLevel(ITopLevelImpl impl)
public TestTopLevel(ITopLevelImpl impl, ILayoutManager layoutManager = null)
: base(impl)
{
_layoutManager = layoutManager ?? new LayoutManager();
}
protected override ILayoutManager CreateLayoutManager() => _layoutManager;
protected override void HandleApplicationExiting()
{
base.HandleApplicationExiting();

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

@ -40,7 +40,7 @@ namespace Avalonia.Controls.UnitTests
IsVisible = true,
};
LayoutManager.Instance.ExecuteInitialLayoutPass(target);
target.LayoutManager.ExecuteInitialLayoutPass(target);
Mock.Get(impl).Verify(x => x.Resize(new Size(321, 432)));
}

21
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -188,6 +188,27 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Closing_Should_Only_Be_Invoked_Once()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var window = new Window();
var count = 0;
window.Closing +=
(sender, e) =>
{
count++;
};
window.Show();
window.Close();
Assert.Equal(1, count);
}
}
[Fact]
public void Showing_Should_Start_Renderer()
{

7
tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs

@ -57,11 +57,11 @@ namespace Avalonia.Layout.UnitTests
};
window.Show();
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
window.LayoutManager.ExecuteInitialLayoutPass(window);
Assert.Equal(new Size(400, 400), border.Bounds.Size);
textBlock.Width = 200;
LayoutManager.Instance.ExecuteLayoutPass();
window.LayoutManager.ExecuteLayoutPass();
Assert.Equal(new Size(200, 400), border.Bounds.Size);
}
@ -99,7 +99,7 @@ namespace Avalonia.Layout.UnitTests
};
window.Show();
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
window.LayoutManager.ExecuteInitialLayoutPass(window);
Assert.Equal(new Size(800, 600), window.Bounds.Size);
Assert.Equal(new Size(200, 200), scrollViewer.Bounds.Size);
@ -200,7 +200,6 @@ namespace Avalonia.Layout.UnitTests
.Bind<IAssetLoader>().ToConstant(new AssetLoader())
.Bind<IInputManager>().ToConstant(new Mock<IInputManager>().Object)
.Bind<IGlobalStyles>().ToConstant(globalStyles.Object)
.Bind<ILayoutManager>().ToConstant(new LayoutManager())
.Bind<IRuntimePlatform>().ToConstant(new AppBuilder().RuntimePlatform)
.Bind<IPlatformRenderInterface>().ToConstant(renderInterface.Object)
.Bind<IStyler>().ToConstant(new Styler())

325
tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs

@ -14,290 +14,231 @@ namespace Avalonia.Layout.UnitTests
[Fact]
public void Measures_And_Arranges_InvalidateMeasured_Control()
{
var target = new LayoutManager();
var control = new LayoutTestControl();
var root = new LayoutTestRoot { Child = control };
using (Start(target))
{
var control = new LayoutTestControl();
var root = new LayoutTestRoot { Child = control };
target.ExecuteInitialLayoutPass(root);
control.Measured = control.Arranged = false;
root.LayoutManager.ExecuteInitialLayoutPass(root);
control.Measured = control.Arranged = false;
control.InvalidateMeasure();
target.ExecuteLayoutPass();
control.InvalidateMeasure();
root.LayoutManager.ExecuteLayoutPass();
Assert.True(control.Measured);
Assert.True(control.Arranged);
}
Assert.True(control.Measured);
Assert.True(control.Arranged);
}
[Fact]
public void Arranges_InvalidateArranged_Control()
{
var target = new LayoutManager();
var control = new LayoutTestControl();
var root = new LayoutTestRoot { Child = control };
using (Start(target))
{
var control = new LayoutTestControl();
var root = new LayoutTestRoot { Child = control };
root.LayoutManager.ExecuteInitialLayoutPass(root);
control.Measured = control.Arranged = false;
target.ExecuteInitialLayoutPass(root);
control.Measured = control.Arranged = false;
control.InvalidateArrange();
root.LayoutManager.ExecuteLayoutPass();
control.InvalidateArrange();
target.ExecuteLayoutPass();
Assert.False(control.Measured);
Assert.True(control.Arranged);
}
Assert.False(control.Measured);
Assert.True(control.Arranged);
}
[Fact]
public void Measures_Parent_Of_Newly_Added_Control()
{
var target = new LayoutManager();
using (Start(target))
{
var control = new LayoutTestControl();
var root = new LayoutTestRoot();
var control = new LayoutTestControl();
var root = new LayoutTestRoot();
target.ExecuteInitialLayoutPass(root);
root.Child = control;
root.Measured = root.Arranged = false;
root.LayoutManager.ExecuteInitialLayoutPass(root);
root.Child = control;
root.Measured = root.Arranged = false;
target.ExecuteLayoutPass();
root.LayoutManager.ExecuteLayoutPass();
Assert.True(root.Measured);
Assert.True(root.Arranged);
Assert.True(control.Measured);
Assert.True(control.Arranged);
}
Assert.True(root.Measured);
Assert.True(root.Arranged);
Assert.True(control.Measured);
Assert.True(control.Arranged);
}
[Fact]
public void Measures_In_Correct_Order()
{
var target = new LayoutManager();
using (Start(target))
LayoutTestControl control1;
LayoutTestControl control2;
var root = new LayoutTestRoot
{
LayoutTestControl control1;
LayoutTestControl control2;
var root = new LayoutTestRoot
Child = control1 = new LayoutTestControl
{
Child = control1 = new LayoutTestControl
{
Child = control2 = new LayoutTestControl(),
}
};
Child = control2 = new LayoutTestControl(),
}
};
var order = new List<ILayoutable>();
Size MeasureOverride(ILayoutable control, Size size)
{
order.Add(control);
return new Size(10, 10);
}
var order = new List<ILayoutable>();
Size MeasureOverride(ILayoutable control, Size size)
{
order.Add(control);
return new Size(10, 10);
}
root.DoMeasureOverride = MeasureOverride;
control1.DoMeasureOverride = MeasureOverride;
control2.DoMeasureOverride = MeasureOverride;
target.ExecuteInitialLayoutPass(root);
root.DoMeasureOverride = MeasureOverride;
control1.DoMeasureOverride = MeasureOverride;
control2.DoMeasureOverride = MeasureOverride;
root.LayoutManager.ExecuteInitialLayoutPass(root);
control2.InvalidateMeasure();
control1.InvalidateMeasure();
root.InvalidateMeasure();
control2.InvalidateMeasure();
control1.InvalidateMeasure();
root.InvalidateMeasure();
order.Clear();
target.ExecuteLayoutPass();
order.Clear();
root.LayoutManager.ExecuteLayoutPass();
Assert.Equal(new ILayoutable[] { root, control1, control2 }, order);
}
Assert.Equal(new ILayoutable[] { root, control1, control2 }, order);
}
[Fact]
public void Measures_Root_And_Grandparent_In_Correct_Order()
{
var target = new LayoutManager();
using (Start(target))
LayoutTestControl control1;
LayoutTestControl control2;
var root = new LayoutTestRoot
{
LayoutTestControl control1;
LayoutTestControl control2;
var root = new LayoutTestRoot
Child = control1 = new LayoutTestControl
{
Child = control1 = new LayoutTestControl
{
Child = control2 = new LayoutTestControl(),
}
};
Child = control2 = new LayoutTestControl(),
}
};
var order = new List<ILayoutable>();
Size MeasureOverride(ILayoutable control, Size size)
{
order.Add(control);
return new Size(10, 10);
}
var order = new List<ILayoutable>();
Size MeasureOverride(ILayoutable control, Size size)
{
order.Add(control);
return new Size(10, 10);
}
root.DoMeasureOverride = MeasureOverride;
control1.DoMeasureOverride = MeasureOverride;
control2.DoMeasureOverride = MeasureOverride;
target.ExecuteInitialLayoutPass(root);
root.DoMeasureOverride = MeasureOverride;
control1.DoMeasureOverride = MeasureOverride;
control2.DoMeasureOverride = MeasureOverride;
root.LayoutManager.ExecuteInitialLayoutPass(root);
control2.InvalidateMeasure();
root.InvalidateMeasure();
control2.InvalidateMeasure();
root.InvalidateMeasure();
order.Clear();
target.ExecuteLayoutPass();
order.Clear();
root.LayoutManager.ExecuteLayoutPass();
Assert.Equal(new ILayoutable[] { root, control2 }, order);
}
Assert.Equal(new ILayoutable[] { root, control2 }, order);
}
[Fact]
public void Doesnt_Measure_Non_Invalidated_Root()
{
var target = new LayoutManager();
var control = new LayoutTestControl();
var root = new LayoutTestRoot { Child = control };
using (Start(target))
{
var control = new LayoutTestControl();
var root = new LayoutTestRoot { Child = control };
target.ExecuteInitialLayoutPass(root);
root.Measured = root.Arranged = false;
control.Measured = control.Arranged = false;
root.LayoutManager.ExecuteInitialLayoutPass(root);
root.Measured = root.Arranged = false;
control.Measured = control.Arranged = false;
control.InvalidateMeasure();
target.ExecuteLayoutPass();
control.InvalidateMeasure();
root.LayoutManager.ExecuteLayoutPass();
Assert.False(root.Measured);
Assert.False(root.Arranged);
Assert.True(control.Measured);
Assert.True(control.Arranged);
}
Assert.False(root.Measured);
Assert.False(root.Arranged);
Assert.True(control.Measured);
Assert.True(control.Arranged);
}
[Fact]
public void Doesnt_Measure_Removed_Control()
{
var target = new LayoutManager();
var control = new LayoutTestControl();
var root = new LayoutTestRoot { Child = control };
using (Start(target))
{
var control = new LayoutTestControl();
var root = new LayoutTestRoot { Child = control };
root.LayoutManager.ExecuteInitialLayoutPass(root);
control.Measured = control.Arranged = false;
target.ExecuteInitialLayoutPass(root);
control.Measured = control.Arranged = false;
control.InvalidateMeasure();
root.Child = null;
root.LayoutManager.ExecuteLayoutPass();
control.InvalidateMeasure();
root.Child = null;
target.ExecuteLayoutPass();
Assert.False(control.Measured);
Assert.False(control.Arranged);
}
Assert.False(control.Measured);
Assert.False(control.Arranged);
}
[Fact]
public void Measures_Root_With_Infinity()
{
var target = new LayoutManager();
var root = new LayoutTestRoot();
var availableSize = default(Size);
using (Start(target))
{
var root = new LayoutTestRoot();
var availableSize = default(Size);
// Should not measure with this size.
root.MaxClientSize = new Size(123, 456);
// Should not measure with this size.
root.MaxClientSize = new Size(123, 456);
root.DoMeasureOverride = (_, s) =>
{
availableSize = s;
return new Size(100, 100);
};
root.DoMeasureOverride = (_, s) =>
{
availableSize = s;
return new Size(100, 100);
};
target.ExecuteInitialLayoutPass(root);
root.LayoutManager.ExecuteInitialLayoutPass(root);
Assert.Equal(Size.Infinity, availableSize);
}
Assert.Equal(Size.Infinity, availableSize);
}
[Fact]
public void Arranges_Root_With_DesiredSize()
{
var target = new LayoutManager();
using (Start(target))
var root = new LayoutTestRoot
{
var root = new LayoutTestRoot
{
Width = 100,
Height = 100,
};
Width = 100,
Height = 100,
};
var arrangeSize = default(Size);
var arrangeSize = default(Size);
root.DoArrangeOverride = (_, s) =>
{
arrangeSize = s;
return s;
};
target.ExecuteInitialLayoutPass(root);
Assert.Equal(new Size(100, 100), arrangeSize);
root.Width = 120;
root.DoArrangeOverride = (_, s) =>
{
arrangeSize = s;
return s;
};
root.LayoutManager.ExecuteInitialLayoutPass(root);
Assert.Equal(new Size(100, 100), arrangeSize);
target.ExecuteLayoutPass();
Assert.Equal(new Size(120, 100), arrangeSize);
}
root.Width = 120;
root.LayoutManager.ExecuteLayoutPass();
Assert.Equal(new Size(120, 100), arrangeSize);
}
[Fact]
public void Invalidating_Child_Remeasures_Parent()
{
var target = new LayoutManager();
Border border;
StackPanel panel;
using (Start(target))
var root = new LayoutTestRoot
{
AvaloniaLocator.CurrentMutable.Bind<ILayoutManager>().ToConstant(target);
Border border;
StackPanel panel;
var root = new LayoutTestRoot
Child = panel = new StackPanel
{
Child = panel = new StackPanel
Children =
{
Children =
{
(border = new Border())
}
(border = new Border())
}
};
target.ExecuteInitialLayoutPass(root);
Assert.Equal(new Size(0, 0), root.DesiredSize);
}
};
border.Width = 100;
border.Height = 100;
root.LayoutManager.ExecuteInitialLayoutPass(root);
Assert.Equal(new Size(0, 0), root.DesiredSize);
target.ExecuteLayoutPass();
Assert.Equal(new Size(100, 100), panel.DesiredSize);
}
}
border.Width = 100;
border.Height = 100;
private IDisposable Start(LayoutManager layoutManager)
{
var result = AvaloniaLocator.EnterScope();
AvaloniaLocator.CurrentMutable.Bind<ILayoutManager>().ToConstant(layoutManager);
return result;
root.LayoutManager.ExecuteLayoutPass();
Assert.Equal(new Size(100, 100), panel.DesiredSize);
}
}
}

87
tests/Avalonia.Layout.UnitTests/LayoutableTests.cs

@ -102,80 +102,73 @@ namespace Avalonia.Layout.UnitTests
public void Only_Calls_LayoutManager_InvalidateMeasure_Once()
{
var target = new Mock<ILayoutManager>();
using (Start(target.Object))
var control = new Decorator();
var root = new LayoutTestRoot
{
var control = new Decorator();
var root = new LayoutTestRoot { Child = control };
Child = control,
LayoutManager = target.Object,
};
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
target.ResetCalls();
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
target.ResetCalls();
control.InvalidateMeasure();
control.InvalidateMeasure();
control.InvalidateMeasure();
control.InvalidateMeasure();
target.Verify(x => x.InvalidateMeasure(control), Times.Once());
}
target.Verify(x => x.InvalidateMeasure(control), Times.Once());
}
[Fact]
public void Only_Calls_LayoutManager_InvalidateArrange_Once()
{
var target = new Mock<ILayoutManager>();
using (Start(target.Object))
var control = new Decorator();
var root = new LayoutTestRoot
{
var control = new Decorator();
var root = new LayoutTestRoot { Child = control };
Child = control,
LayoutManager = target.Object,
};
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
target.ResetCalls();
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
target.ResetCalls();
control.InvalidateArrange();
control.InvalidateArrange();
control.InvalidateArrange();
control.InvalidateArrange();
target.Verify(x => x.InvalidateArrange(control), Times.Once());
}
target.Verify(x => x.InvalidateArrange(control), Times.Once());
}
[Fact]
public void Attaching_Control_To_Tree_Invalidates_Parent_Measure()
{
var target = new Mock<ILayoutManager>();
using (Start(target.Object))
var control = new Decorator();
var root = new LayoutTestRoot
{
var control = new Decorator();
var root = new LayoutTestRoot { Child = control };
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
Assert.True(control.IsMeasureValid);
Child = control,
LayoutManager = target.Object,
};
root.Child = null;
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
Assert.True(control.IsMeasureValid);
Assert.False(control.IsMeasureValid);
Assert.True(root.IsMeasureValid);
root.Child = null;
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
target.ResetCalls();
Assert.False(control.IsMeasureValid);
Assert.True(root.IsMeasureValid);
root.Child = control;
target.ResetCalls();
Assert.False(root.IsMeasureValid);
Assert.False(control.IsMeasureValid);
target.Verify(x => x.InvalidateMeasure(root), Times.Once());
}
}
root.Child = control;
private IDisposable Start(ILayoutManager layoutManager)
{
var result = AvaloniaLocator.EnterScope();
AvaloniaLocator.CurrentMutable.Bind<ILayoutManager>().ToConstant(layoutManager);
return result;
Assert.False(root.IsMeasureValid);
Assert.False(control.IsMeasureValid);
target.Verify(x => x.InvalidateMeasure(root), Times.Once());
}
private class TestLayoutable : Layoutable

28
tests/Avalonia.LeakTests/ControlTests.cs

@ -42,12 +42,12 @@ namespace Avalonia.LeakTests
window.Show();
// Do a layout and make sure that Canvas gets added to visual tree.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
window.LayoutManager.ExecuteInitialLayoutPass(window);
Assert.IsType<Canvas>(window.Presenter.Child);
// Clear the content and ensure the Canvas is removed.
window.Content = null;
LayoutManager.Instance.ExecuteLayoutPass();
window.LayoutManager.ExecuteLayoutPass();
Assert.Null(window.Presenter.Child);
return window;
@ -78,13 +78,13 @@ namespace Avalonia.LeakTests
window.Show();
// Do a layout and make sure that Canvas gets added to visual tree.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
window.LayoutManager.ExecuteInitialLayoutPass(window);
Assert.IsType<Canvas>(window.Find<Canvas>("foo"));
Assert.IsType<Canvas>(window.Presenter.Child);
// Clear the content and ensure the Canvas is removed.
window.Content = null;
LayoutManager.Instance.ExecuteLayoutPass();
window.LayoutManager.ExecuteLayoutPass();
Assert.Null(window.Presenter.Child);
return window;
@ -116,13 +116,13 @@ namespace Avalonia.LeakTests
// Do a layout and make sure that ScrollViewer gets added to visual tree and its
// template applied.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
window.LayoutManager.ExecuteInitialLayoutPass(window);
Assert.IsType<ScrollViewer>(window.Presenter.Child);
Assert.IsType<Canvas>(((ScrollViewer)window.Presenter.Child).Presenter.Child);
// Clear the content and ensure the ScrollViewer is removed.
window.Content = null;
LayoutManager.Instance.ExecuteLayoutPass();
window.LayoutManager.ExecuteLayoutPass();
Assert.Null(window.Presenter.Child);
return window;
@ -153,13 +153,13 @@ namespace Avalonia.LeakTests
// Do a layout and make sure that TextBox gets added to visual tree and its
// template applied.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
window.LayoutManager.ExecuteInitialLayoutPass(window);
Assert.IsType<TextBox>(window.Presenter.Child);
Assert.NotEmpty(window.Presenter.Child.GetVisualChildren());
// Clear the content and ensure the TextBox is removed.
window.Content = null;
LayoutManager.Instance.ExecuteLayoutPass();
window.LayoutManager.ExecuteLayoutPass();
Assert.Null(window.Presenter.Child);
return window;
@ -197,14 +197,14 @@ namespace Avalonia.LeakTests
// Do a layout and make sure that TextBox gets added to visual tree and its
// Text property set.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
window.LayoutManager.ExecuteInitialLayoutPass(window);
Assert.IsType<TextBox>(window.Presenter.Child);
Assert.Equal("foo", ((TextBox)window.Presenter.Child).Text);
// Clear the content and DataContext and ensure the TextBox is removed.
window.Content = null;
window.DataContext = null;
LayoutManager.Instance.ExecuteLayoutPass();
window.LayoutManager.ExecuteLayoutPass();
Assert.Null(window.Presenter.Child);
return window;
@ -235,7 +235,7 @@ namespace Avalonia.LeakTests
// Do a layout and make sure that TextBox gets added to visual tree and its
// template applied.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
window.LayoutManager.ExecuteInitialLayoutPass(window);
Assert.Same(textBox, window.Presenter.Child);
// Get the border from the TextBox template.
@ -247,7 +247,7 @@ namespace Avalonia.LeakTests
// Clear the content and ensure the TextBox is removed.
window.Content = null;
LayoutManager.Instance.ExecuteLayoutPass();
window.LayoutManager.ExecuteLayoutPass();
Assert.Null(window.Presenter.Child);
// Check that the TextBox has no subscriptions to its Classes collection.
@ -289,12 +289,12 @@ namespace Avalonia.LeakTests
window.Show();
// Do a layout and make sure that TreeViewItems get realized.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
window.LayoutManager.ExecuteInitialLayoutPass(window);
Assert.Single(target.ItemContainerGenerator.Containers);
// Clear the content and ensure the TreeView is removed.
window.Content = null;
LayoutManager.Instance.ExecuteLayoutPass();
window.LayoutManager.ExecuteLayoutPass();
Assert.Null(window.Presenter.Child);
return window;

13
tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs

@ -8,7 +8,6 @@ using Avalonia.Markup.Xaml.Converters;
using Avalonia.Styling;
using Xunit;
using System.ComponentModel;
using Portable.Xaml.ComponentModel;
using Portable.Xaml;
using Portable.Xaml.Markup;
@ -53,7 +52,17 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
Assert.Equal(AttachedOwner.AttachedProperty, result);
}
[Fact]
public void ConvertFrom_Finds_Attached_Property_With_Parentheses()
{
var target = new AvaloniaPropertyTypeConverter();
var context = CreateContext();
var result = target.ConvertFrom(context, null, "(AttachedOwner.Attached)");
Assert.Equal(AttachedOwner.AttachedProperty, result);
}
private ITypeDescriptorContext CreateContext(Style style = null)
{
var tdMock = new Mock<ITypeDescriptorContext>();

14
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs

@ -1,18 +1,12 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Moq;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Styling;
using Xunit;
using System.Reactive.Disposables;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using System.Linq;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Data
{
@ -30,7 +24,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
<Button Name='button'>
<Button.Template>
<ControlTemplate>
<TextBlock Text='{TemplateBinding}'/>
<TextBlock Tag='{TemplateBinding}'/>
</ControlTemplate>
</Button.Template>
</Button>
@ -43,7 +37,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
button.ApplyTemplate();
var textBlock = (TextBlock)button.GetVisualChildren().Single();
Assert.Equal("Avalonia.Controls.Button", textBlock.Text);
Assert.Same(button, textBlock.Tag);
}
}
}

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

@ -686,7 +686,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Template>
<ControlTemplate>
<ControlTemplate TargetType='Window'>
<ContentPresenter Name='PART_ContentPresenter'
Content='{TemplateBinding Content}'/>
</ControlTemplate>

4
tests/Avalonia.UnitTests/TestRoot.cs

@ -43,9 +43,9 @@ namespace Avalonia.UnitTests
public double LayoutScaling => 1;
public double RenderScaling => 1;
public ILayoutManager LayoutManager { get; set; } = new LayoutManager();
public ILayoutManager LayoutManager => AvaloniaLocator.Current.GetService<ILayoutManager>();
public double RenderScaling => 1;
public IRenderer Renderer { get; set; }

11
tests/Avalonia.UnitTests/TestServices.cs

@ -23,7 +23,6 @@ namespace Avalonia.UnitTests
{
public static readonly TestServices StyledWindow = new TestServices(
assetLoader: new AssetLoader(),
layoutManager: new LayoutManager(),
platform: new AppBuilder().RuntimePlatform,
renderInterface: new MockPlatformRenderInterface(),
standardCursorFactory: Mock.Of<IStandardCursorFactory>(),
@ -52,10 +51,7 @@ namespace Avalonia.UnitTests
keyboardDevice: () => new KeyboardDevice(),
keyboardNavigation: new KeyboardNavigationHandler(),
inputManager: new InputManager());
public static readonly TestServices RealLayoutManager = new TestServices(
layoutManager: new LayoutManager());
public static readonly TestServices RealStyler = new TestServices(
styler: new Styler());
@ -65,7 +61,6 @@ namespace Avalonia.UnitTests
IInputManager inputManager = null,
Func<IKeyboardDevice> keyboardDevice = null,
IKeyboardNavigationHandler keyboardNavigation = null,
ILayoutManager layoutManager = null,
Func<IMouseDevice> mouseDevice = null,
IRuntimePlatform platform = null,
IPlatformRenderInterface renderInterface = null,
@ -83,7 +78,6 @@ namespace Avalonia.UnitTests
InputManager = inputManager;
KeyboardDevice = keyboardDevice;
KeyboardNavigation = keyboardNavigation;
LayoutManager = layoutManager;
MouseDevice = mouseDevice;
Platform = platform;
RenderInterface = renderInterface;
@ -101,7 +95,6 @@ namespace Avalonia.UnitTests
public IFocusManager FocusManager { get; }
public Func<IKeyboardDevice> KeyboardDevice { get; }
public IKeyboardNavigationHandler KeyboardNavigation { get; }
public ILayoutManager LayoutManager { get; }
public Func<IMouseDevice> MouseDevice { get; }
public IRuntimePlatform Platform { get; }
public IPlatformRenderInterface RenderInterface { get; }
@ -119,7 +112,6 @@ namespace Avalonia.UnitTests
IInputManager inputManager = null,
Func<IKeyboardDevice> keyboardDevice = null,
IKeyboardNavigationHandler keyboardNavigation = null,
ILayoutManager layoutManager = null,
Func<IMouseDevice> mouseDevice = null,
IRuntimePlatform platform = null,
IPlatformRenderInterface renderInterface = null,
@ -138,7 +130,6 @@ namespace Avalonia.UnitTests
inputManager: inputManager ?? InputManager,
keyboardDevice: keyboardDevice ?? KeyboardDevice,
keyboardNavigation: keyboardNavigation ?? KeyboardNavigation,
layoutManager: layoutManager ?? LayoutManager,
mouseDevice: mouseDevice ?? MouseDevice,
platform: platform ?? Platform,
renderInterface: renderInterface ?? RenderInterface,

4
tests/Avalonia.UnitTests/TestTemplatedRoot.cs

@ -39,9 +39,9 @@ namespace Avalonia.UnitTests
public double LayoutScaling => 1;
public double RenderScaling => 1;
public ILayoutManager LayoutManager { get; set; } = new LayoutManager();
public ILayoutManager LayoutManager => AvaloniaLocator.Current.GetService<ILayoutManager>();
public double RenderScaling => 1;
public IRenderTarget RenderTarget => null;

1
tests/Avalonia.UnitTests/UnitTestApplication.cs

@ -50,7 +50,6 @@ namespace Avalonia.UnitTests
.Bind<IInputManager>().ToConstant(Services.InputManager)
.Bind<IKeyboardDevice>().ToConstant(Services.KeyboardDevice?.Invoke())
.Bind<IKeyboardNavigationHandler>().ToConstant(Services.KeyboardNavigation)
.Bind<ILayoutManager>().ToConstant(Services.LayoutManager)
.Bind<IMouseDevice>().ToConstant(Services.MouseDevice?.Invoke())
.Bind<IRuntimePlatform>().ToConstant(Services.Platform)
.Bind<IPlatformRenderInterface>().ToConstant(Services.RenderInterface)

29
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@ -522,7 +522,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Should_Update_ClipBounds_For_Negative_Margin()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator decorator;
Border border;
@ -542,7 +542,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
};
var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
var layout = tree.LayoutManager;
layout.ExecuteInitialLayoutPass(tree);
var scene = new Scene(tree);
@ -566,7 +566,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Should_Update_Descendent_Tranform_When_Margin_Changed()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator decorator;
Border border;
@ -585,7 +585,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
};
var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
var layout = tree.LayoutManager;
layout.ExecuteInitialLayoutPass(tree);
var scene = new Scene(tree);
@ -613,7 +613,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void DirtyRects_Should_Contain_Old_And_New_Bounds_When_Margin_Changed()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator decorator;
Border border;
@ -633,7 +633,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
};
var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
var layout = tree.LayoutManager;
layout.ExecuteInitialLayoutPass(tree);
var scene = new Scene(tree);
@ -660,7 +660,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Resizing_Scene_Should_Add_DirtyRects()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator decorator;
Border border;
@ -707,7 +707,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Setting_Opacity_Should_Add_Descendent_Bounds_To_DirtyRects()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator decorator;
Border border;
@ -744,7 +744,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Should_Set_GeometryClip()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var clip = StreamGeometry.Parse("M100,0 L0,100 100,100");
Decorator decorator;
@ -768,7 +768,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Disposing_Scene_Releases_DrawOperation_References()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var bitmap = RefCountable.Create(Mock.Of<IBitmapImpl>());
Image img;
@ -800,7 +800,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Replacing_Control_Releases_DrawOperation_Reference()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var bitmap = RefCountable.Create(Mock.Of<IBitmapImpl>());
Image img;
@ -830,12 +830,5 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
}
}
private IDisposable TestApplication()
{
return UnitTestApplication.Start(
TestServices.MockPlatformRenderInterface.With(
layoutManager: new LayoutManager()));
}
}
}

20
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs

@ -17,7 +17,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Control_With_Animated_Opacity_And_Children_Should_Start_New_Layer()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator decorator;
Border border;
@ -39,7 +39,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
};
var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
var layout = tree.LayoutManager;
layout.ExecuteInitialLayoutPass(tree);
var animation = new BehaviorSubject<double>(0.5);
@ -85,7 +85,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Control_With_Animated_Opacity_And_No_Children_Should_Not_Start_New_Layer()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator decorator;
Border border;
@ -104,7 +104,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
};
var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
var layout = tree.LayoutManager;
layout.ExecuteInitialLayoutPass(tree);
var animation = new BehaviorSubject<double>(0.5);
@ -121,7 +121,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Removing_Control_With_Animated_Opacity_Should_Remove_Layers()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator decorator;
Border border;
@ -146,7 +146,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
};
var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
var layout = tree.LayoutManager;
layout.ExecuteInitialLayoutPass(tree);
var animation = new BehaviorSubject<double>(0.5);
@ -171,7 +171,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void Hiding_Transparent_Control_Should_Remove_Layers()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
Decorator decorator;
Border border;
@ -196,7 +196,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
};
var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
var layout = tree.LayoutManager;
layout.ExecuteInitialLayoutPass(tree);
var animation = new BehaviorSubject<double>(0.5);
@ -221,7 +221,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
[Fact]
public void GeometryClip_Should_Affect_Child_Layers()
{
using (TestApplication())
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var clip = StreamGeometry.Parse("M100,0 L0,100 100,100");
Decorator decorator;
@ -240,7 +240,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
}
};
var layout = AvaloniaLocator.Current.GetService<ILayoutManager>();
var layout = tree.LayoutManager;
layout.ExecuteInitialLayoutPass(tree);
var animation = new BehaviorSubject<double>(0.5);

Loading…
Cancel
Save