Skip to content

Commit

Permalink
Merge pull request #14000 from unoplatform/dev/jela/hr-template
Browse files Browse the repository at this point in the history
Adjust DataTemplate reload
  • Loading branch information
jeromelaban authored Oct 18, 2023
2 parents 72dc180 + ef957d7 commit 0b444c5
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ public void InitializeComponent()
"myTemplate"
] =
new global::Uno.UI.Xaml.WeakResourceInitializer(this, __ResourceOwner_1 =>
new global::Windows.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => new _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_TestReproMyResourceDictionarySC0().Build(__owner)
new global::Windows.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => new _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_TestReproMyResourceDictionarySC0_().Build(__owner)
) )
;
}

private class _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_TestReproMyResourceDictionarySC0
private class _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_TestReproMyResourceDictionarySC0_
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
private const string __baseUri_prefix_MyResourceDictionary_92716e07ff456818f6d4125e055d4d57 = "ms-appx:///TestProject/";
Expand Down Expand Up @@ -176,7 +176,7 @@ private ResourceDictionarySingleton__MyResourceDictionary_92716e07ff456818f6d412

// Method for resource myTemplate
private object Get_1(object __ResourceOwner_1) =>
new global::Windows.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => new __Resources._MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC1().Build(__owner)
new global::Windows.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => new __Resources._MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC1_().Build(__owner)
) ;

private global::Windows.UI.Xaml.ResourceDictionary _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_ResourceDictionary;
Expand All @@ -195,7 +195,7 @@ private object Get_1(object __ResourceOwner_1) =>
"myTemplate"
] =
new global::Uno.UI.Xaml.WeakResourceInitializer(this, __ResourceOwner_1 =>
new global::Windows.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => new __Resources._MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC2().Build(__owner)
new global::Windows.UI.Xaml.DataTemplate(__ResourceOwner_1 , __owner => new __Resources._MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC2_().Build(__owner)
) )
,
}
Expand All @@ -215,7 +215,7 @@ private object Get_1(object __ResourceOwner_1) =>
}
namespace MyProject.__Resources
{
class _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC1
class _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC1_
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
private const string __baseUri_prefix_MyResourceDictionary_92716e07ff456818f6d4125e055d4d57 = "ms-appx:///TestProject/";
Expand Down Expand Up @@ -303,7 +303,7 @@ private static bool TryGetInstance_xBind_2(global::TestRepro.MyModel ___tctx, ou
return true;
}
}
class _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC2
class _MyResourceDictionary_92716e07ff456818f6d4125e055d4d57_MyResourceDictionaryRDSC2_
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
private const string __baseUri_prefix_MyResourceDictionary_92716e07ff456818f6d4125e055d4d57 = "ms-appx:///TestProject/";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private void InitializeComponent()
IsParsing = true,
Name = "topLevel",
Tag = @"42",
ContentTemplate = new global::Windows.UI.Xaml.DataTemplate(this , __owner => new _Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca_UnoUITestsWindows_UI_Xaml_DataBindingTestsControlsBinding_ElementName_In_TemplateSC0().Build(__owner)
ContentTemplate = new global::Windows.UI.Xaml.DataTemplate(this , __owner => new _Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca_UnoUITestsWindows_UI_Xaml_DataBindingTestsControlsBinding_ElementName_In_TemplateSC0_().Build(__owner)
) ,
// Source 0\Binding_ElementName_In_Template.xaml (Line 11:4)
}
Expand Down Expand Up @@ -115,7 +115,7 @@ private void InitializeComponent()
_topLevelSubject.ElementInstance = value;
}
}
private class _Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca_UnoUITestsWindows_UI_Xaml_DataBindingTestsControlsBinding_ElementName_In_TemplateSC0
private class _Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca_UnoUITestsWindows_UI_Xaml_DataBindingTestsControlsBinding_ElementName_In_TemplateSC0_
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
private const string __baseUri_prefix_Binding_ElementName_In_Template_66bf0a54f1801c397a6fa4930a237eca = "ms-appx:///TestProject/";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,27 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.IO;
using System.Linq;
using Uno.UI.SourceGenerators.Helpers;
using Uno.UI.SourceGenerators.XamlGenerator.XamlRedirection;

namespace Uno.UI.SourceGenerators.XamlGenerator
{
internal class XamlFileDefinition : IEquatable<XamlFileDefinition>
{
public XamlFileDefinition(string file, string targetFilePath)
public XamlFileDefinition(string file, string targetFilePath, ImmutableArray<byte> checksum)
{
Namespaces = new List<NamespaceDeclaration>();
Objects = new List<XamlObjectDefinition>();
FilePath = file;
TargetFilePath = targetFilePath;

UniqueID = SanitizedFileName + "_" + HashBuilder.Build(FilePath);

Checksum = string.Concat(checksum.Select(c => c.ToString("x2", CultureInfo.InvariantCulture)));
}

private string SanitizedFileName => Path
Expand All @@ -30,6 +35,8 @@ public XamlFileDefinition(string file, string targetFilePath)

public string FilePath { get; }

public string Checksum { get; }

public string? SourceLink { get; internal set; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6469,7 +6469,14 @@ private void BuildChildThroughSubclass(IIndentedStringBuilder writer, XamlMember

var namespacePrefix = _scopeStack.Count == 1 && _scopeStack.Last().Name.EndsWith("RD", StringComparison.Ordinal) ? "__Resources." : "";

var subclassName = $"_{_fileUniqueId}_{subClassPrefix}SC{(_subclassIndex++).ToString(CultureInfo.InvariantCulture)}";
// This is needed to avoid having outer classes being marked
// as updated types, but not having new types created as a result
// of an inner change, which may prevent XAML HR to apply changes.
var hashValue = _isHotReloadEnabled && !subClassPrefix.Contains(_fileDefinition.Checksum)
? _fileDefinition.Checksum
: "";

var subclassName = $"_{_fileUniqueId}_{subClassPrefix}SC{(_subclassIndex++).ToString(CultureInfo.InvariantCulture)}_{hashValue}";

RegisterChildSubclass(subclassName, contentOwner, returnType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Uno.UI.SourceGenerators.XamlGenerator.Utils;
using Uno.Roslyn;
using Windows.Foundation.Metadata;
using System.Collections.Immutable;

namespace Uno.UI.SourceGenerators.XamlGenerator
{
Expand Down Expand Up @@ -118,7 +119,7 @@ private static void ScavengeCache()
{
cancellationToken.ThrowIfCancellationRequested();

var xamlFileDefinition = Visit(reader, file.Path, targetFilePath, cancellationToken);
var xamlFileDefinition = Visit(reader, file, targetFilePath, cancellationToken);
if (!reader.DisableCaching)
{
_cachedFiles[cachedFileKey] = new CachedFile(DateTimeOffset.Now, xamlFileDefinition);
Expand Down Expand Up @@ -228,11 +229,11 @@ private XmlReader RewriteForXBind(SourceText sourceText)
}
}

private XamlFileDefinition Visit(XamlXmlReader reader, string file, string targetFilePath, CancellationToken cancellationToken)
private XamlFileDefinition Visit(XamlXmlReader reader, AdditionalText source, string targetFilePath, CancellationToken cancellationToken)
{
WriteState(reader);

var xamlFile = new XamlFileDefinition(file, targetFilePath);
var xamlFile = new XamlFileDefinition(source.Path, targetFilePath, source.GetText(cancellationToken)?.GetChecksum() ?? ImmutableArray<byte>.Empty);

do
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.7.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.7.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.7.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.7.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.8.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.8.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.8.0-2.final" />
<!-- TODO: Use version 8 when compiling against .NET 8? -->
<PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
<PackageReference Include="Microsoft.Build" Version="16.10.0" ExcludeAssets="runtime" />
Expand All @@ -38,10 +38,10 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net7.0'">
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.7.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.7.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.7.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.7.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.8.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.8.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0-2.final" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.8.0-2.final" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
<PackageReference Include="Microsoft.Build" Version="16.10.0" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Framework" Version="16.10.0" ExcludeAssets="runtime" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ private static async Task ReloadWithUpdatedTypes(Type[] updatedTypes)
async (fe, key) =>
{
// Get the original type of the element, in case it's been replaced
var originalType = fe.GetType().GetOriginalType() ?? fe.GetType();
var liveType = fe.GetType();
var originalType = liveType.GetOriginalType() ?? fe.GetType();

// Get the handler for the type specified
// Since we're only interested in handlers for specific element types
Expand Down Expand Up @@ -244,15 +245,25 @@ private static async Task ReloadWithUpdatedTypes(Type[] updatedTypes)
}
}

return (handler is not null || mappedType is not null) ? (fe, handler, mappedType) : default;
if (updatedTypes.Contains(liveType))
{
// This may happen if one of the nested types has been hot reloaded, but not the type itself.
// For instance, a DataTemplate in a resource dictionary may mark the type as updated in `updatedTypes`
// but it will not be considered as a new type even if "CreateNewOnMetadataUpdate" was set.

return (fe, null, liveType);
}
else
{
return (handler is not null || mappedType is not null) ? (fe, handler, mappedType) : default;
}
},
parentKey: default);

// Forced iteration to capture all state before doing ui update
var instancesToUpdate = await treeIterator.ToArrayAsync();


// Iterate through the visual tree and either invole ElementUpdate,
// Iterate through the visual tree and either invoke ElementUpdate,
// or replace the element with a new one
foreach (var (element, elementHandler, elementMappedType) in instancesToUpdate)
{
Expand All @@ -262,6 +273,11 @@ private static async Task ReloadWithUpdatedTypes(Type[] updatedTypes)

if (elementMappedType is not null)
{
if (_log.IsEnabled(LogLevel.Trace))
{
_log.Error($"Updating element [{element}] to [{elementMappedType}]");
}

ReplaceViewInstance(element, elementMappedType, elementHandler);
}
}
Expand All @@ -281,7 +297,6 @@ private static async Task ReloadWithUpdatedTypes(Type[] updatedTypes)
}
throw;
}

}

private static void ReplaceViewInstance(UIElement instance, Type replacementType, ElementUpdateAgent.ElementUpdateHandlerActions? handler = default, Type[]? updatedTypes = default)
Expand All @@ -290,7 +305,7 @@ private static void ReplaceViewInstance(UIElement instance, Type replacementType
{
if (_log.IsEnabled(LogLevel.Trace))
{
_log.Trace($"Creating instance of type {instance.GetType()}");
_log.Trace($"Creating instance of type {replacementType}");
}

var newInstance = Activator.CreateInstance(replacementType);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

using System.Reflection.Metadata;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.Extensions;
using Uno.UI.Helpers;
using Uno.UI.RuntimeTests.Tests.HotReload.Frame.HRApp.Tests;
using Uno.UI.RuntimeTests.Tests.HotReload.Frame.Pages;

namespace Uno.UI.RuntimeTests.Tests.HotReload.Frame.HRApp.Tests;

[TestClass]
[RunsOnUIThread]
public class Given_DataTemplate : BaseTestClass
{
/// <summary>
/// Change the Text of a TextBlock inside a UserControl, where the UserControl
/// is nested inside a Viewbox
/// </summary>
[TestMethod]
public async Task When_Change_DataTemplate()
{
var ct = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;

// We're not storing the instance explicitly, as the HR engine replaces
// the top level content of the window. We keep poking at the UnitTestsUIContentHelper.Content
// as it gets updated with reloaded content.
UnitTestsUIContentHelper.Content = new HR_Frame_Pages_DataTemplate();

var originalText = "** Original Text **";
var updatedText = "** Updated Text **";

// Check the initial text of the TextBlock
await UnitTestsUIContentHelper.Content.ValidateTextOnChildTextBlock(originalText, 0);

// Check the updated text of the TextBlock
await HotReloadHelper.UpdateServerFileAndRevert<HR_Frame_Pages_DataTemplate>(
originalText,
updatedText,
() => UnitTestsUIContentHelper.Content.ValidateTextOnChildTextBlock(updatedText, 0),
ct);

// Validate that content been returned to the original text
await UnitTestsUIContentHelper.Content.ValidateTextOnChildTextBlock(originalText, 0);

await Task.Yield();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Page x:Class="Uno.UI.RuntimeTests.Tests.HotReload.Frame.Pages.HR_Frame_Pages_DataTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Uno.UI.RuntimeTests.Tests.HotReload.Frame.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="myTemplate">
<TextBlock Text="** Original Text **" />
</DataTemplate>
</Page.Resources>
<StackPanel>
<ContentControl ContentTemplate="{StaticResource myTemplate}" Content="21">
</ContentControl>
</StackPanel>
</Page>

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Uno.UI.RuntimeTests.Tests.HotReload.Frame.Pages;

public sealed partial class HR_Frame_Pages_DataTemplate : Page
{
public HR_Frame_Pages_DataTemplate()
{
this.InitializeComponent();
}
}
8 changes: 8 additions & 0 deletions src/Uno.UI.RuntimeTests/Uno.UI.RuntimeTests.Skia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
<DefineConstants>$(DefineConstants);IS_RUNTIME_UI_TESTS</DefineConstants>
<NoWarn>$(NoWarn);CS1998</NoWarn>
</PropertyGroup>

<!--
Uncomment this block to enable generated source debugging
<PropertyGroup>
<UnoUISourceGeneratorDebuggerBreak>True</UnoUISourceGeneratorDebuggerBreak>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
-->

<ItemGroup>
<PackageReference Include="FluentAssertions" />
Expand Down

0 comments on commit 0b444c5

Please sign in to comment.