Browse Source

Merge pull request #7008 from workgroupengineering/features/Issue_6642

feat(ContentPresenter): Content of ContentPresenter should become DataContext of the subtree whenever ContentTemplate is not null
pull/7853/head
Max Katz 5 years ago
committed by GitHub
parent
commit
49d4f0c026
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 53
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  2. 23
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs

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

@ -107,9 +107,6 @@ namespace Avalonia.Controls.Presenters
AffectsRender<ContentPresenter>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); AffectsRender<ContentPresenter>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty);
AffectsArrange<ContentPresenter>(HorizontalContentAlignmentProperty, VerticalContentAlignmentProperty); AffectsArrange<ContentPresenter>(HorizontalContentAlignmentProperty, VerticalContentAlignmentProperty);
AffectsMeasure<ContentPresenter>(BorderThicknessProperty, PaddingProperty); AffectsMeasure<ContentPresenter>(BorderThicknessProperty, PaddingProperty);
ContentProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.ContentChanged(e));
ContentTemplateProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.ContentChanged(e));
TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.TemplatedParentChanged(e));
} }
public ContentPresenter() public ContentPresenter()
@ -240,6 +237,21 @@ namespace Avalonia.Controls.Presenters
} }
} }
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
switch (change.Property.Name)
{
case nameof(Content):
case nameof(ContentTemplate):
ContentChanged(change);
break;
case nameof(TemplatedParent):
TemplatedParentChanged(change);
break;
}
}
/// <summary> /// <summary>
/// Updates the <see cref="Child"/> control based on the control's <see cref="Content"/>. /// Updates the <see cref="Child"/> control based on the control's <see cref="Content"/>.
/// </summary> /// </summary>
@ -254,8 +266,14 @@ namespace Avalonia.Controls.Presenters
public void UpdateChild() public void UpdateChild()
{ {
var content = Content; var content = Content;
UpdateChild(content);
}
private void UpdateChild(object? content)
{
var contentTemplate = ContentTemplate;
var oldChild = Child; var oldChild = Child;
var newChild = CreateChild(); var newChild = CreateChild(content, oldChild, contentTemplate);
var logicalChildren = Host?.LogicalChildren ?? LogicalChildren; var logicalChildren = Host?.LogicalChildren ?? LogicalChildren;
// Remove the old child if we're not recycling it. // Remove the old child if we're not recycling it.
@ -271,7 +289,7 @@ namespace Avalonia.Controls.Presenters
} }
// Set the DataContext if the data isn't a control. // Set the DataContext if the data isn't a control.
if (!(content is IControl)) if (contentTemplate is { } || !(content is IControl))
{ {
DataContext = content; DataContext = content;
} }
@ -299,6 +317,7 @@ namespace Avalonia.Controls.Presenters
} }
_createdChild = true; _createdChild = true;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -325,18 +344,23 @@ namespace Avalonia.Controls.Presenters
{ {
var content = Content; var content = Content;
var oldChild = Child; var oldChild = Child;
return CreateChild(content, oldChild, ContentTemplate);
}
private IControl? CreateChild(object? content, IControl? oldChild, IDataTemplate? template)
{
var newChild = content as IControl; var newChild = content as IControl;
// We want to allow creating Child from the Template, if Content is null. // We want to allow creating Child from the Template, if Content is null.
// But it's important to not use DataTemplates, otherwise we will break content presenters in many places, // But it's important to not use DataTemplates, otherwise we will break content presenters in many places,
// otherwise it will blow up every ContentPresenter without Content set. // otherwise it will blow up every ContentPresenter without Content set.
if (newChild == null if ((newChild == null
&& (content != null || ContentTemplate != null)) && (content != null || template != null)) || (newChild is { } && template is { }))
{ {
var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? var dataTemplate = this.FindDataTemplate(content, template) ??
( (
RecognizesAccessKey RecognizesAccessKey
? FuncDataTemplate.Access ? FuncDataTemplate.Access
: FuncDataTemplate.Default : FuncDataTemplate.Default
); );
@ -446,7 +470,14 @@ namespace Avalonia.Controls.Presenters
if (((ILogical)this).IsAttachedToLogicalTree) if (((ILogical)this).IsAttachedToLogicalTree)
{ {
UpdateChild(); if (e.Property.Name == nameof(Content))
{
UpdateChild(e.NewValue);
}
else
{
UpdateChild();
}
} }
else if (Child != null) else if (Child != null)
{ {

23
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_InTemplate.cs

@ -353,6 +353,29 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Null(target.Host); Assert.Null(target.Host);
} }
[Fact]
public void Content_Should_Become_DataContext_When_ControlTemplate_Is_Not_Null()
{
var (target, _) = CreateTarget();
var textBlock = new TextBlock
{
[!TextBlock.TextProperty] = new Binding("Name"),
};
var canvas = new Canvas()
{
Name = "Canvas"
};
target.ContentTemplate = new FuncDataTemplate<Canvas>((_, __) => textBlock);
target.Content = canvas;
Assert.NotNull(target.DataContext);
Assert.Equal(canvas, target.DataContext);
Assert.Equal("Canvas", textBlock.Text);
}
(ContentPresenter presenter, ContentControl templatedParent) CreateTarget() (ContentPresenter presenter, ContentControl templatedParent) CreateTarget()
{ {
var templatedParent = new ContentControl var templatedParent = new ContentControl

Loading…
Cancel
Save