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 4 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);
AffectsArrange<ContentPresenter>(HorizontalContentAlignmentProperty, VerticalContentAlignmentProperty);
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()
@ -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>
/// Updates the <see cref="Child"/> control based on the control's <see cref="Content"/>.
/// </summary>
@ -254,8 +266,14 @@ namespace Avalonia.Controls.Presenters
public void UpdateChild()
{
var content = Content;
UpdateChild(content);
}
private void UpdateChild(object? content)
{
var contentTemplate = ContentTemplate;
var oldChild = Child;
var newChild = CreateChild();
var newChild = CreateChild(content, oldChild, contentTemplate);
var logicalChildren = Host?.LogicalChildren ?? LogicalChildren;
// 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.
if (!(content is IControl))
if (contentTemplate is { } || !(content is IControl))
{
DataContext = content;
}
@ -299,6 +317,7 @@ namespace Avalonia.Controls.Presenters
}
_createdChild = true;
}
/// <inheritdoc/>
@ -325,18 +344,23 @@ namespace Avalonia.Controls.Presenters
{
var content = Content;
var oldChild = Child;
return CreateChild(content, oldChild, ContentTemplate);
}
private IControl? CreateChild(object? content, IControl? oldChild, IDataTemplate? template)
{
var newChild = content as IControl;
// 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,
// otherwise it will blow up every ContentPresenter without Content set.
if (newChild == null
&& (content != null || ContentTemplate != null))
if ((newChild == null
&& (content != null || template != null)) || (newChild is { } && template is { }))
{
var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ??
var dataTemplate = this.FindDataTemplate(content, template) ??
(
RecognizesAccessKey
? FuncDataTemplate.Access
RecognizesAccessKey
? FuncDataTemplate.Access
: FuncDataTemplate.Default
);
@ -446,7 +470,14 @@ namespace Avalonia.Controls.Presenters
if (((ILogical)this).IsAttachedToLogicalTree)
{
UpdateChild();
if (e.Property.Name == nameof(Content))
{
UpdateChild(e.NewValue);
}
else
{
UpdateChild();
}
}
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);
}
[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()
{
var templatedParent = new ContentControl

Loading…
Cancel
Save