Browse Source

Fixed DevTools.

- Set `InheritanceParent` before Parent so that control doesn't
initially inherit the wrong DataContext
- Don't set `InheritanceParent` when setting `Parent` if
`InheritanceParent` already set
- Added `IDataTemplate.SupportsRecycling`
pull/558/head
Steven Kirk 10 years ago
parent
commit
d717b46112
  1. 6
      src/Avalonia.Controls/Control.cs
  2. 8
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  3. 21
      src/Avalonia.Controls/Templates/FuncDataTemplate.cs
  4. 13
      src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs
  5. 6
      src/Avalonia.Controls/Templates/IDataTemplate.cs
  6. 2
      src/Avalonia.Diagnostics/ViewLocator.cs
  7. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  8. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  9. 33
      tests/Avalonia.Controls.UnitTests/ControlTests.cs
  10. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests.cs
  11. 2
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

6
src/Avalonia.Controls/Control.cs

@ -435,7 +435,11 @@ namespace Avalonia.Controls
OnDetachedFromLogicalTreeCore(e);
}
InheritanceParent = parent as AvaloniaObject;
if (InheritanceParent == null || parent == null)
{
InheritanceParent = parent as AvaloniaObject;
}
_parent = (IControl)parent;
if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true)

8
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -231,7 +231,10 @@ namespace Avalonia.Controls.Presenters
if (content != null)
{
if (old != null && _dataTemplate?.Match(content) == true)
if (old != null &&
_dataTemplate != null &&
_dataTemplate.SupportsRecycling &&
_dataTemplate.Match(content))
{
result = old;
}
@ -267,6 +270,8 @@ namespace Avalonia.Controls.Presenters
if (result != null)
{
((ISetInheritanceParent)result).SetParent(this);
Child = result;
if (result.Parent == null)
@ -274,7 +279,6 @@ namespace Avalonia.Controls.Presenters
((ISetLogicalParent)result).SetParent((ILogical)this.TemplatedParent ?? this);
}
((ISetInheritanceParent)result).SetParent(this);
VisualChildren.Add(result);
}
else

21
src/Avalonia.Controls/Templates/FuncDataTemplate.cs

@ -31,7 +31,8 @@ namespace Avalonia.Controls.Templates
{
return null;
}
});
},
true);
/// <summary>
/// The implementation of the <see cref="Match"/> method.
@ -45,8 +46,12 @@ namespace Avalonia.Controls.Templates
/// <param name="build">
/// A function which when passed an object of <paramref name="type"/> returns a control.
/// </param>
public FuncDataTemplate(Type type, Func<object, IControl> build)
: this(o => IsInstance(o, type), build)
/// <param name="supportsRecycling">Whether the control can be recycled.</param>
public FuncDataTemplate(
Type type,
Func<object, IControl> build,
bool supportsRecycling = false)
: this(o => IsInstance(o, type), build, supportsRecycling)
{
}
@ -59,14 +64,22 @@ namespace Avalonia.Controls.Templates
/// <param name="build">
/// A function which returns a control for matching data.
/// </param>
public FuncDataTemplate(Func<object, bool> match, Func<object, IControl> build)
/// <param name="supportsRecycling">Whether the control can be recycled.</param>
public FuncDataTemplate(
Func<object, bool> match,
Func<object, IControl> build,
bool supportsRecycling = false)
: base(build)
{
Contract.Requires<ArgumentNullException>(match != null);
_match = match;
SupportsRecycling = supportsRecycling;
}
/// <inheritdoc/>
public bool SupportsRecycling { get; }
/// <summary>
/// Checks to see if this data template matches the specified data.
/// </summary>

13
src/Avalonia.Controls/Templates/FuncDataTemplate`1.cs

@ -17,8 +17,9 @@ namespace Avalonia.Controls.Templates
/// <param name="build">
/// A function which when passed an object of <typeparamref name="T"/> returns a control.
/// </param>
public FuncDataTemplate(Func<T, IControl> build)
: base(typeof(T), CastBuild(build))
/// <param name="supportsRecycling">Whether the control can be recycled.</param>
public FuncDataTemplate(Func<T, IControl> build, bool supportsRecycling = false)
: base(typeof(T), CastBuild(build), supportsRecycling)
{
}
@ -31,8 +32,12 @@ namespace Avalonia.Controls.Templates
/// <param name="build">
/// A function which when passed an object of <typeparamref name="T"/> returns a control.
/// </param>
public FuncDataTemplate(Func<T, bool> match, Func<T, IControl> build)
: base(CastMatch(match), CastBuild(build))
/// <param name="supportsRecycling">Whether the control can be recycled.</param>
public FuncDataTemplate(
Func<T, bool> match,
Func<T, IControl> build,
bool supportsRecycling = false)
: base(CastMatch(match), CastBuild(build), supportsRecycling)
{
}

6
src/Avalonia.Controls/Templates/IDataTemplate.cs

@ -8,6 +8,12 @@ namespace Avalonia.Controls.Templates
/// </summary>
public interface IDataTemplate : ITemplate<object, IControl>
{
/// <summary>
/// Gets a value indicating whether the data template supports recycling of the generated
/// control.
/// </summary>
bool SupportsRecycling { get; }
/// <summary>
/// Checks to see if this data template matches the specified data.
/// </summary>

2
src/Avalonia.Diagnostics/ViewLocator.cs

@ -9,6 +9,8 @@ namespace Avalonia.Diagnostics
{
public class ViewLocator<TViewModel> : IDataTemplate
{
public bool SupportsRecycling => false;
public IControl Build(object data)
{
var name = data.GetType().FullName.Replace("ViewModel", "View");

2
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

@ -16,6 +16,8 @@ namespace Avalonia.Markup.Xaml.Templates
[Content]
public TemplateContent Content { get; set; }
public bool SupportsRecycling => true;
public bool Match(object data)
{
if (DataType == null)

2
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs

@ -24,6 +24,8 @@ namespace Avalonia.Markup.Xaml.Templates
[AssignBinding]
public Binding ItemsSource { get; set; }
public bool SupportsRecycling => true;
public bool Match(object data)
{
if (DataType == null)

33
tests/Avalonia.Controls.UnitTests/ControlTests.cs

@ -22,22 +22,51 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void LogicalParent_Should_Be_Set_To_Parent()
public void Setting_Parent_Should_Also_Set_InheritanceParent()
{
var parent = new Decorator();
var target = new TestControl();
parent.Child = target;
Assert.Equal(parent, target.Parent);
Assert.Equal(parent, target.InheritanceParent);
}
[Fact]
public void LogicalParent_Should_Be_Cleared_When_Removed_From_Parent()
public void Setting_Parent_Should_Not_Set_InheritanceParent_If_Already_Set()
{
var parent = new Decorator();
var inheritanceParent = new Decorator();
var target = new TestControl();
((ISetInheritanceParent)target).SetParent(inheritanceParent);
parent.Child = target;
Assert.Equal(parent, target.Parent);
Assert.Equal(inheritanceParent, target.InheritanceParent);
}
[Fact]
public void InheritanceParent_Should_Be_Cleared_When_Removed_From_Parent()
{
var parent = new Decorator();
var target = new TestControl();
parent.Child = target;
parent.Child = null;
Assert.Null(target.InheritanceParent);
}
[Fact]
public void InheritanceParent_Should_Be_Cleared_When_Removed_From_Parent_When_Has_Different_InheritanceParent()
{
var parent = new Decorator();
var inheritanceParent = new Decorator();
var target = new TestControl();
((ISetInheritanceParent)target).SetParent(inheritanceParent);
parent.Child = target;
parent.Child = null;

2
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests.cs

@ -160,7 +160,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
{
DataTemplates = new DataTemplates
{
new FuncDataTemplate<string>(_ => new Border()),
new FuncDataTemplate<string>(_ => new Border(), true),
},
Content = "foo",
};

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

@ -462,6 +462,8 @@ namespace Avalonia.Controls.UnitTests
return new TextBlock { Text = node.Value };
}
public bool SupportsRecycling => false;
public InstancedBinding ItemsSelector(object item)
{
var obs = new ExpressionObserver(item, nameof(Node.Children));

Loading…
Cancel
Save