Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enable AOT compatibility #157

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/BlazorBindings.Core/BlazorBindings.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Description>Common logic for using Blazor to target native renderers. For mobile app projects for Android and iOS use the BlazorBindings.Maui package.</Description>
<PackageTags>blazor;blazorbindings</PackageTags>
<ImplicitUsings>enable</ImplicitUsings>
<IsTrimmable>true</IsTrimmable>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/BlazorBindings.Maui/BlazorBindings.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Title>Maui binding for Blazor</Title>
<Description>Maui Blazor Bindings enables using Blazor syntax in .razor files to build native apps for iOS and Android.</Description>
<PackageTags>blazor;blazorbindings</PackageTags>
<IsTrimmable>true</IsTrimmable>
<IsAotCompatible>true</IsAotCompatible>

<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">10.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private int AddTemplateGroup()
void INonPhysicalChild.SetParent(object parentElement)
{
var parent = (TControl)parentElement;
var dataTemplate = new DataTemplateSelector(TemplateSelector, AddTemplateGroup, AddTemplateRootToGroup);
var dataTemplate = new DataTemplateSelector<TItem>(TemplateSelector, AddTemplateGroup, AddTemplateRootToGroup);
SetDataTemplateSelectorAction(parent, dataTemplate);
}

Expand All @@ -66,57 +66,58 @@ void INonPhysicalChild.RemoveFromParent(object parentElement) { }
void IContainerElementHandler.AddChild(object child, int physicalSiblingIndex) { }
void IContainerElementHandler.RemoveChild(object child, int physicalSiblingIndex) { }

class DataTemplateSelector : MC.DataTemplateSelector
}

file class DataTemplateSelector<TItem> : MC.DataTemplateSelector
{
private readonly RenderFragment<TItem> _renderFragment;
private readonly Func<int> _addGroupFunc;
private readonly Func<int, MC.View> _addRootToGroupFunc;
private readonly Dictionary<Type, MC.DataTemplate> _dataTemplates = new();

public DataTemplateSelector(RenderFragment<TItem> renderFragment, Func<int> addGroupFunc, Func<int, MC.View> addRootToGroupFunc)
{
private readonly RenderFragment<TItem> _renderFragment;
private readonly Func<int> _addGroupFunc;
private readonly Func<int, MC.View> _addRootToGroupFunc;
private readonly Dictionary<Type, MC.DataTemplate> _dataTemplates = new();
_renderFragment = renderFragment;
_addGroupFunc = addGroupFunc;
_addRootToGroupFunc = addRootToGroupFunc;
}

public DataTemplateSelector(RenderFragment<TItem> renderFragment, Func<int> addGroupFunc, Func<int, MC.View> addRootToGroupFunc)
protected override MC.DataTemplate OnSelectTemplate(object item, MC.BindableObject container)
{
var componentType = GetComponentType(_renderFragment((TItem)item));

if (!_dataTemplates.TryGetValue(componentType, out var dataTemplate))
{
_renderFragment = renderFragment;
_addGroupFunc = addGroupFunc;
_addRootToGroupFunc = addRootToGroupFunc;
var groupIndex = _addGroupFunc();
dataTemplate = new MC.DataTemplate(() => _addRootToGroupFunc(groupIndex));
_dataTemplates[componentType] = dataTemplate;
}

protected override MC.DataTemplate OnSelectTemplate(object item, MC.BindableObject container)
return dataTemplate;
}

static readonly RenderTreeBuilder InspectRenderTreeBuilder = new();
private static Type GetComponentType(RenderFragment renderFragment)
{
// Consider reworking if https://github.com/dotnet/aspnetcore/issues/17200 is implemented.
try
{
var componentType = GetComponentType(_renderFragment((TItem)item));
renderFragment(InspectRenderTreeBuilder);
var frames = InspectRenderTreeBuilder.GetFrames();

if (!_dataTemplates.TryGetValue(componentType, out var dataTemplate))
for (var i = 0; i < frames.Count; i++)
{
var groupIndex = _addGroupFunc();
dataTemplate = new MC.DataTemplate(() => _addRootToGroupFunc(groupIndex));
_dataTemplates[componentType] = dataTemplate;
if (frames.Array[i].FrameType == RenderTreeFrameType.Component)
{
return frames.Array[i].ComponentType;
}
}

return dataTemplate;
throw new InvalidOperationException("RenderFragment does not contain any components.");
}

static readonly RenderTreeBuilder InspectRenderTreeBuilder = new();
private static Type GetComponentType(RenderFragment renderFragment)
finally
{
// Consider reworking if https://github.com/dotnet/aspnetcore/issues/17200 is implemented.
try
{
renderFragment(InspectRenderTreeBuilder);
var frames = InspectRenderTreeBuilder.GetFrames();

for (var i = 0; i < frames.Count; i++)
{
if (frames.Array[i].FrameType == RenderTreeFrameType.Component)
{
return frames.Array[i].ComponentType;
}
}

throw new InvalidOperationException("RenderFragment does not contain any components.");
}
finally
{
InspectRenderTreeBuilder.Clear();
}
InspectRenderTreeBuilder.Clear();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,35 +46,35 @@ public MC.BindableObject AddTemplateRoot(TItem initialItem)
void INonPhysicalChild.SetParent(object parentElement)
{
var parent = (TControl)parentElement;
var dataTemplate = new DataTemplateSelector(AddTemplateRoot);
var dataTemplate = new DataTemplateSelector<TItem>(AddTemplateRoot);
SetDataTemplateAction(parent, dataTemplate);
}

void INonPhysicalChild.RemoveFromParent(object parentElement) { }
object IElementHandler.TargetElement => null;
void IContainerElementHandler.AddChild(object child, int physicalSiblingIndex) { }
void IContainerElementHandler.RemoveChild(object child, int physicalSiblingIndex) { }
}

// In order to be able to render the item synchronously, we need to have an item upfront, before the render.
// Unfortunately, regular DataTemplate does not have an access to the item, it is set set BindingContext afterwards.
// In order to workaround this issue, we use a DataTemplateSelector, which returns the same DataTemplate. But we're able
// to store the item from OnSelectTemplate method, which is used to render the item in DataTemplate.
class DataTemplateSelector : MC.DataTemplateSelector
{
private readonly MC.DataTemplate _dataTemplate;
private readonly Func<TItem, MC.BindableObject> _loadTemplate;
private TItem _initialItem;
// In order to be able to render the item synchronously, we need to have an item upfront, before the render.
// Unfortunately, regular DataTemplate does not have an access to the item, it is set set BindingContext afterwards.
// In order to workaround this issue, we use a DataTemplateSelector, which returns the same DataTemplate. But we're able
// to store the item from OnSelectTemplate method, which is used to render the item in DataTemplate.
file class DataTemplateSelector<TItem> : MC.DataTemplateSelector
{
private readonly MC.DataTemplate _dataTemplate;
private readonly Func<TItem, MC.BindableObject> _loadTemplate;
private TItem _initialItem;

public DataTemplateSelector(Func<TItem, MC.BindableObject> loadTemplate)
{
_loadTemplate = loadTemplate;
_dataTemplate = new MC.DataTemplate(() => _loadTemplate(_initialItem));
}
public DataTemplateSelector(Func<TItem, MC.BindableObject> loadTemplate)
{
_loadTemplate = loadTemplate;
_dataTemplate = new MC.DataTemplate(() => _loadTemplate(_initialItem));
}

protected override MC.DataTemplate OnSelectTemplate(object item, MC.BindableObject container)
{
_initialItem = (TItem)item;
return _dataTemplate;
}
protected override MC.DataTemplate OnSelectTemplate(object item, MC.BindableObject container)
{
_initialItem = (TItem)item;
return _dataTemplate;
}
}
}
Loading