diff --git a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeBuilder.cs b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeBuilder.cs
index 2aa8808f5a..f71cc4c92a 100644
--- a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeBuilder.cs
+++ b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeBuilder.cs
@@ -65,6 +65,25 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
public void AddContent(int sequence, string textContent)
=> Append(RenderTreeFrame.Text(sequence, textContent ?? string.Empty));
+ ///
+ /// Appends frames representing an arbitrary fragment of content.
+ ///
+ /// An integer that represents the position of the instruction in the source code.
+ /// Content to append.
+ public void AddContent(int sequence, RenderFragment fragment)
+ {
+ if (fragment != null)
+ {
+ // We surround the fragment with a region delimiter to indicate that the
+ // sequence numbers inside the fragment are unrelated to the sequence numbers
+ // outside it. If we didn't do this, the diffing logic might produce inefficient
+ // diffs depending on how the sequence numbers compared.
+ OpenRegion(sequence);
+ fragment(this);
+ CloseRegion();
+ }
+ }
+
///
/// Appends a frame representing text content.
///
diff --git a/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeBuilderTest.cs b/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeBuilderTest.cs
index 76ef5d71e9..3bba6922a7 100644
--- a/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeBuilderTest.cs
+++ b/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeBuilderTest.cs
@@ -342,6 +342,33 @@ namespace Microsoft.AspNetCore.Blazor.Test
frame => AssertFrame.Text(frame, "Goodbye", 6));
}
+ [Fact]
+ public void CanAddFragments()
+ {
+ // Arrange
+ var builder = new RenderTreeBuilder(new TestRenderer());
+ RenderFragment fragment = fragmentBuilder =>
+ {
+ fragmentBuilder.AddContent(0, "Hello from the fragment");
+ fragmentBuilder.OpenElement(1, "Fragment element");
+ fragmentBuilder.AddContent(2, "Some text");
+ fragmentBuilder.CloseElement();
+ };
+
+ // Act
+ builder.OpenElement(10, "parent");
+ builder.AddContent(11, fragment);
+ builder.CloseElement();
+
+ // Assert
+ Assert.Collection(builder.GetFrames(),
+ frame => AssertFrame.Element(frame, "parent", 5, 10),
+ frame => AssertFrame.Region(frame, 4, 11),
+ frame => AssertFrame.Text(frame, "Hello from the fragment", 0),
+ frame => AssertFrame.Element(frame, "Fragment element", 2, 1),
+ frame => AssertFrame.Text(frame, "Some text", 2));
+ }
+
[Fact]
public void CanClear()
{
diff --git a/test/testapps/BasicTestApp/RenderBlockComponent.cs b/test/testapps/BasicTestApp/RenderBlockComponent.cs
index aaf454e288..7088aad36d 100644
--- a/test/testapps/BasicTestApp/RenderBlockComponent.cs
+++ b/test/testapps/BasicTestApp/RenderBlockComponent.cs
@@ -48,9 +48,7 @@ namespace BasicTestApp
if (_showRegion)
{
- builder.OpenRegion(3);
- _exampleContent(builder);
- builder.CloseRegion();
+ builder.AddContent(3, _exampleContent);
}
builder.OpenElement(4, "button");