From 5cdeb3b2a8c87fe6cf278279c2dce5529fafcc8e Mon Sep 17 00:00:00 2001 From: Oleksandr Liakhevych Date: Fri, 12 May 2023 01:11:55 +0300 Subject: [PATCH] ComponentGenerator - support custom collection types --- .../MDComboBox.generated.cs | 233 ++++++++++++++++++ .../MDComboBoxItem.generated.cs | 33 +++ .../MDMenuItem.generated.cs | 165 +++++++++++++ .../MDNavigationBar.generated.cs | 16 +- .../Extensions/NavigationExtensions.cs | 2 + .../Properties/Elements.cs | 3 + .../GeneratedPropertyInfo.RenderFragment.cs | 47 +++- .../Elements/ShellContent.generated.cs | 25 ++ 8 files changed, 506 insertions(+), 18 deletions(-) create mode 100644 samples/ThirdPartyControlsSample/Elements/Material.Components/MDComboBox.generated.cs create mode 100644 samples/ThirdPartyControlsSample/Elements/Material.Components/MDComboBoxItem.generated.cs create mode 100644 samples/ThirdPartyControlsSample/Elements/Material.Components/MDMenuItem.generated.cs diff --git a/samples/ThirdPartyControlsSample/Elements/Material.Components/MDComboBox.generated.cs b/samples/ThirdPartyControlsSample/Elements/Material.Components/MDComboBox.generated.cs new file mode 100644 index 00000000..e5891c54 --- /dev/null +++ b/samples/ThirdPartyControlsSample/Elements/Material.Components/MDComboBox.generated.cs @@ -0,0 +1,233 @@ +// +// This code was generated by a BlazorBindings.Maui component generator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// + +using BlazorBindings.Core; +using BlazorBindings.Maui.Elements; +using MC = Microsoft.Maui.Controls; +using MCM = Material.Components.Maui; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; +using Microsoft.Maui.Graphics; +using System.Collections; +using System.Threading.Tasks; + +#pragma warning disable CA2252 + +namespace BlazorBindings.Maui.Elements.Material.Components +{ + public partial class MDComboBox : SKTouchCanvasView + { + static MDComboBox() + { + RegisterAdditionalHandlers(); + } + + [Parameter] public Color ActiveIndicatorColor { get; set; } + [Parameter] public int? ActiveIndicatorHeight { get; set; } + [Parameter] public float? ActiveIndicatorOpacity { get; set; } + [Parameter] public Color BackgroundColour { get; set; } + [Parameter] public string FontFamily { get; set; } + [Parameter] public bool? FontItalic { get; set; } + [Parameter] public float? FontSize { get; set; } + [Parameter] public int? FontWeight { get; set; } + [Parameter] public Color ForegroundColor { get; set; } + [Parameter] public bool? IsOutline { get; set; } + [Parameter] public IList ItemsSource { get; set; } + [Parameter] public string LabelText { get; set; } + [Parameter] public Color LabelTextColor { get; set; } + [Parameter] public float? LabelTextOpacity { get; set; } + [Parameter] public Color OutlineColor { get; set; } + [Parameter] public int? OutlineWidth { get; set; } + [Parameter] public int? SelectedIndex { get; set; } + [Parameter] public MCM.Tokens.Shape? Shape { get; set; } + [Parameter] public Color StateLayerColor { get; set; } + [Parameter] public string Text { get; set; } + [Parameter] public RenderFragment ChildContent { get; set; } + [Parameter] public EventCallback SelectedIndexChanged { get; set; } + + public new MCM.ComboBox NativeControl => (MCM.ComboBox)((BindableObject)this).NativeControl; + + protected override MCM.ComboBox CreateNativeElement() => new(); + + protected override void HandleParameter(string name, object value) + { + switch (name) + { + case nameof(ActiveIndicatorColor): + if (!Equals(ActiveIndicatorColor, value)) + { + ActiveIndicatorColor = (Color)value; + NativeControl.ActiveIndicatorColor = ActiveIndicatorColor; + } + break; + case nameof(ActiveIndicatorHeight): + if (!Equals(ActiveIndicatorHeight, value)) + { + ActiveIndicatorHeight = (int?)value; + NativeControl.ActiveIndicatorHeight = ActiveIndicatorHeight ?? (int)MCM.ComboBox.ActiveIndicatorHeightProperty.DefaultValue; + } + break; + case nameof(ActiveIndicatorOpacity): + if (!Equals(ActiveIndicatorOpacity, value)) + { + ActiveIndicatorOpacity = (float?)value; + NativeControl.ActiveIndicatorOpacity = ActiveIndicatorOpacity ?? (float)MCM.ComboBox.ActiveIndicatorOpacityProperty.DefaultValue; + } + break; + case nameof(BackgroundColour): + if (!Equals(BackgroundColour, value)) + { + BackgroundColour = (Color)value; + NativeControl.BackgroundColour = BackgroundColour; + } + break; + case nameof(FontFamily): + if (!Equals(FontFamily, value)) + { + FontFamily = (string)value; + NativeControl.FontFamily = FontFamily; + } + break; + case nameof(FontItalic): + if (!Equals(FontItalic, value)) + { + FontItalic = (bool?)value; + NativeControl.FontItalic = FontItalic ?? (bool)MCM.ComboBox.FontItalicProperty.DefaultValue; + } + break; + case nameof(FontSize): + if (!Equals(FontSize, value)) + { + FontSize = (float?)value; + NativeControl.FontSize = FontSize ?? (float)MCM.ComboBox.FontSizeProperty.DefaultValue; + } + break; + case nameof(FontWeight): + if (!Equals(FontWeight, value)) + { + FontWeight = (int?)value; + NativeControl.FontWeight = FontWeight ?? (int)MCM.ComboBox.FontWeightProperty.DefaultValue; + } + break; + case nameof(ForegroundColor): + if (!Equals(ForegroundColor, value)) + { + ForegroundColor = (Color)value; + NativeControl.ForegroundColor = ForegroundColor; + } + break; + case nameof(IsOutline): + if (!Equals(IsOutline, value)) + { + IsOutline = (bool?)value; + NativeControl.IsOutline = IsOutline ?? (bool)MCM.ComboBox.IsOutlineProperty.DefaultValue; + } + break; + case nameof(ItemsSource): + if (!Equals(ItemsSource, value)) + { + ItemsSource = (IList)value; + NativeControl.ItemsSource = ItemsSource; + } + break; + case nameof(LabelText): + if (!Equals(LabelText, value)) + { + LabelText = (string)value; + NativeControl.LabelText = LabelText; + } + break; + case nameof(LabelTextColor): + if (!Equals(LabelTextColor, value)) + { + LabelTextColor = (Color)value; + NativeControl.LabelTextColor = LabelTextColor; + } + break; + case nameof(LabelTextOpacity): + if (!Equals(LabelTextOpacity, value)) + { + LabelTextOpacity = (float?)value; + NativeControl.LabelTextOpacity = LabelTextOpacity ?? (float)MCM.ComboBox.LabelTextOpacityProperty.DefaultValue; + } + break; + case nameof(OutlineColor): + if (!Equals(OutlineColor, value)) + { + OutlineColor = (Color)value; + NativeControl.OutlineColor = OutlineColor; + } + break; + case nameof(OutlineWidth): + if (!Equals(OutlineWidth, value)) + { + OutlineWidth = (int?)value; + NativeControl.OutlineWidth = OutlineWidth ?? (int)MCM.ComboBox.OutlineWidthProperty.DefaultValue; + } + break; + case nameof(SelectedIndex): + if (!Equals(SelectedIndex, value)) + { + SelectedIndex = (int?)value; + NativeControl.SelectedIndex = SelectedIndex ?? (int)MCM.ComboBox.SelectedIndexProperty.DefaultValue; + } + break; + case nameof(Shape): + if (!Equals(Shape, value)) + { + Shape = (MCM.Tokens.Shape?)value; + NativeControl.Shape = Shape ?? (MCM.Tokens.Shape)MCM.ComboBox.ShapeProperty.DefaultValue; + } + break; + case nameof(StateLayerColor): + if (!Equals(StateLayerColor, value)) + { + StateLayerColor = (Color)value; + NativeControl.StateLayerColor = StateLayerColor; + } + break; + case nameof(Text): + if (!Equals(Text, value)) + { + Text = (string)value; + NativeControl.Text = Text; + } + break; + case nameof(ChildContent): + ChildContent = (RenderFragment)value; + break; + case nameof(SelectedIndexChanged): + if (!Equals(SelectedIndexChanged, value)) + { + void NativeControlSelectedIndexChanged(object sender, MCM.Core.SelectedIndexChangedEventArgs e) + { + var value = NativeControl.SelectedIndex; + SelectedIndex = value; + InvokeEventCallback(SelectedIndexChanged, value); + } + + SelectedIndexChanged = (EventCallback)value; + NativeControl.SelectedIndexChanged -= NativeControlSelectedIndexChanged; + NativeControl.SelectedIndexChanged += NativeControlSelectedIndexChanged; + } + break; + + default: + base.HandleParameter(name, value); + break; + } + } + + protected override void RenderAdditionalElementContent(RenderTreeBuilder builder, ref int sequence) + { + base.RenderAdditionalElementContent(builder, ref sequence); + RenderTreeBuilderHelper.AddListContentProperty(builder, sequence++, ChildContent, x => x.Items); + } + + static partial void RegisterAdditionalHandlers(); + } +} diff --git a/samples/ThirdPartyControlsSample/Elements/Material.Components/MDComboBoxItem.generated.cs b/samples/ThirdPartyControlsSample/Elements/Material.Components/MDComboBoxItem.generated.cs new file mode 100644 index 00000000..1c14ced8 --- /dev/null +++ b/samples/ThirdPartyControlsSample/Elements/Material.Components/MDComboBoxItem.generated.cs @@ -0,0 +1,33 @@ +// +// This code was generated by a BlazorBindings.Maui component generator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// + +using BlazorBindings.Core; +using BlazorBindings.Maui.Elements; +using MC = Microsoft.Maui.Controls; +using MCM = Material.Components.Maui; +using Microsoft.AspNetCore.Components; +using System.Threading.Tasks; + +#pragma warning disable CA2252 + +namespace BlazorBindings.Maui.Elements.Material.Components +{ + public partial class MDComboBoxItem : MDMenuItem + { + static MDComboBoxItem() + { + RegisterAdditionalHandlers(); + } + + public new MCM.ComboBoxItem NativeControl => (MCM.ComboBoxItem)((BindableObject)this).NativeControl; + + protected override MCM.ComboBoxItem CreateNativeElement() => new(); + + + static partial void RegisterAdditionalHandlers(); + } +} diff --git a/samples/ThirdPartyControlsSample/Elements/Material.Components/MDMenuItem.generated.cs b/samples/ThirdPartyControlsSample/Elements/Material.Components/MDMenuItem.generated.cs new file mode 100644 index 00000000..c7864c88 --- /dev/null +++ b/samples/ThirdPartyControlsSample/Elements/Material.Components/MDMenuItem.generated.cs @@ -0,0 +1,165 @@ +// +// This code was generated by a BlazorBindings.Maui component generator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// + +using BlazorBindings.Core; +using BlazorBindings.Maui.Elements; +using MC = Microsoft.Maui.Controls; +using MCM = Material.Components.Maui; +using Microsoft.AspNetCore.Components; +using Microsoft.Maui.Graphics; +using System.Threading.Tasks; + +#pragma warning disable CA2252 + +namespace BlazorBindings.Maui.Elements.Material.Components +{ + public partial class MDMenuItem : SKTouchCanvasView + { + static MDMenuItem() + { + RegisterAdditionalHandlers(); + } + + [Parameter] public Color BackgroundColour { get; set; } + [Parameter] public string FontFamily { get; set; } + [Parameter] public bool? FontItalic { get; set; } + [Parameter] public float? FontSize { get; set; } + [Parameter] public int? FontWeight { get; set; } + [Parameter] public Color ForegroundColor { get; set; } + [Parameter] public string IconData { get; set; } + [Parameter] public global::IconPacks.Material.IconKind? IconKind { get; set; } + [Parameter] public global::SkiaSharp.SKPicture IconSource { get; set; } + [Parameter] public Color RippleColor { get; set; } + [Parameter] public Color StateLayerColor { get; set; } + [Parameter] public string Text { get; set; } + [Parameter] public string TrailIconData { get; set; } + [Parameter] public global::IconPacks.Material.IconKind? TrailIconKind { get; set; } + [Parameter] public global::SkiaSharp.SKPicture TrailIconSource { get; set; } + + public new MCM.MenuItem NativeControl => (MCM.MenuItem)((BindableObject)this).NativeControl; + + protected override MCM.MenuItem CreateNativeElement() => new(); + + protected override void HandleParameter(string name, object value) + { + switch (name) + { + case nameof(BackgroundColour): + if (!Equals(BackgroundColour, value)) + { + BackgroundColour = (Color)value; + NativeControl.BackgroundColour = BackgroundColour; + } + break; + case nameof(FontFamily): + if (!Equals(FontFamily, value)) + { + FontFamily = (string)value; + NativeControl.FontFamily = FontFamily; + } + break; + case nameof(FontItalic): + if (!Equals(FontItalic, value)) + { + FontItalic = (bool?)value; + NativeControl.FontItalic = FontItalic ?? (bool)MCM.MenuItem.FontItalicProperty.DefaultValue; + } + break; + case nameof(FontSize): + if (!Equals(FontSize, value)) + { + FontSize = (float?)value; + NativeControl.FontSize = FontSize ?? (float)MCM.MenuItem.FontSizeProperty.DefaultValue; + } + break; + case nameof(FontWeight): + if (!Equals(FontWeight, value)) + { + FontWeight = (int?)value; + NativeControl.FontWeight = FontWeight ?? (int)MCM.MenuItem.FontWeightProperty.DefaultValue; + } + break; + case nameof(ForegroundColor): + if (!Equals(ForegroundColor, value)) + { + ForegroundColor = (Color)value; + NativeControl.ForegroundColor = ForegroundColor; + } + break; + case nameof(IconData): + if (!Equals(IconData, value)) + { + IconData = (string)value; + NativeControl.IconData = IconData; + } + break; + case nameof(IconKind): + if (!Equals(IconKind, value)) + { + IconKind = (global::IconPacks.Material.IconKind?)value; + NativeControl.IconKind = IconKind ?? (global::IconPacks.Material.IconKind)MCM.MenuItem.IconKindProperty.DefaultValue; + } + break; + case nameof(IconSource): + if (!Equals(IconSource, value)) + { + IconSource = (global::SkiaSharp.SKPicture)value; + NativeControl.IconSource = IconSource; + } + break; + case nameof(RippleColor): + if (!Equals(RippleColor, value)) + { + RippleColor = (Color)value; + NativeControl.RippleColor = RippleColor; + } + break; + case nameof(StateLayerColor): + if (!Equals(StateLayerColor, value)) + { + StateLayerColor = (Color)value; + NativeControl.StateLayerColor = StateLayerColor; + } + break; + case nameof(Text): + if (!Equals(Text, value)) + { + Text = (string)value; + NativeControl.Text = Text; + } + break; + case nameof(TrailIconData): + if (!Equals(TrailIconData, value)) + { + TrailIconData = (string)value; + NativeControl.TrailIconData = TrailIconData; + } + break; + case nameof(TrailIconKind): + if (!Equals(TrailIconKind, value)) + { + TrailIconKind = (global::IconPacks.Material.IconKind?)value; + NativeControl.TrailIconKind = TrailIconKind ?? default; + } + break; + case nameof(TrailIconSource): + if (!Equals(TrailIconSource, value)) + { + TrailIconSource = (global::SkiaSharp.SKPicture)value; + NativeControl.TrailIconSource = TrailIconSource; + } + break; + + default: + base.HandleParameter(name, value); + break; + } + } + + static partial void RegisterAdditionalHandlers(); + } +} diff --git a/samples/ThirdPartyControlsSample/Elements/Material.Components/MDNavigationBar.generated.cs b/samples/ThirdPartyControlsSample/Elements/Material.Components/MDNavigationBar.generated.cs index 629ebc5d..cb65ddf0 100644 --- a/samples/ThirdPartyControlsSample/Elements/Material.Components/MDNavigationBar.generated.cs +++ b/samples/ThirdPartyControlsSample/Elements/Material.Components/MDNavigationBar.generated.cs @@ -25,9 +25,8 @@ static MDNavigationBar() } [Parameter] public bool? HasLabel { get; set; } - [Parameter] public MCM.Core.ItemCollection Items { get; set; } [Parameter] public int? SelectedIndex { get; set; } - [Parameter] public RenderFragment SelectedItem { get; set; } + [Parameter] public RenderFragment ChildContent { get; set; } public new MCM.NavigationBar NativeControl => (MCM.NavigationBar)((BindableObject)this).NativeControl; @@ -44,13 +43,6 @@ protected override void HandleParameter(string name, object value) NativeControl.HasLabel = HasLabel ?? (bool)MCM.NavigationBar.HasLabelProperty.DefaultValue; } break; - case nameof(Items): - if (!Equals(Items, value)) - { - Items = (MCM.Core.ItemCollection)value; - NativeControl.Items = Items; - } - break; case nameof(SelectedIndex): if (!Equals(SelectedIndex, value)) { @@ -58,8 +50,8 @@ protected override void HandleParameter(string name, object value) NativeControl.SelectedIndex = SelectedIndex ?? (int)MCM.NavigationBar.SelectedIndexProperty.DefaultValue; } break; - case nameof(SelectedItem): - SelectedItem = (RenderFragment)value; + case nameof(ChildContent): + ChildContent = (RenderFragment)value; break; default: @@ -71,7 +63,7 @@ protected override void HandleParameter(string name, object value) protected override void RenderAdditionalElementContent(RenderTreeBuilder builder, ref int sequence) { base.RenderAdditionalElementContent(builder, ref sequence); - RenderTreeBuilderHelper.AddContentProperty(builder, sequence++, SelectedItem, (x, value) => x.SelectedItem = (MCM.NavigationBarItem)value); + RenderTreeBuilderHelper.AddListContentProperty(builder, sequence++, ChildContent, x => x.Items); } static partial void RegisterAdditionalHandlers(); diff --git a/samples/ThirdPartyControlsSample/Extensions/NavigationExtensions.cs b/samples/ThirdPartyControlsSample/Extensions/NavigationExtensions.cs index 134b89fa..cbecc419 100644 --- a/samples/ThirdPartyControlsSample/Extensions/NavigationExtensions.cs +++ b/samples/ThirdPartyControlsSample/Extensions/NavigationExtensions.cs @@ -7,7 +7,9 @@ public static class NavigationExtensions { public static async Task ShowPopupAsync(this Navigation navigationService) { +#pragma warning disable CA2252 // This API requires opting into preview features var popup = await navigationService.BuildElement(typeof(T), null); +#pragma warning restore CA2252 // This API requires opting into preview features return await Application.Current.MainPage.ShowPopupAsync(popup); } } diff --git a/samples/ThirdPartyControlsSample/Properties/Elements.cs b/samples/ThirdPartyControlsSample/Properties/Elements.cs index b52349d1..48ca766a 100644 --- a/samples/ThirdPartyControlsSample/Properties/Elements.cs +++ b/samples/ThirdPartyControlsSample/Properties/Elements.cs @@ -37,6 +37,9 @@ // Material.Components.Maui [assembly: GenerateComponent(typeof(Material.Components.Maui.ProgressIndicator), Aliases = new[] { "ProgressIndicator:MDProgressIndicator" })] [assembly: GenerateComponent(typeof(Material.Components.Maui.NavigationBar), Aliases = new[] { "NavigationBar:MDNavigationBar" })] +[assembly: GenerateComponent(typeof(Material.Components.Maui.ComboBox), Aliases = new[] { "ComboBox:MDComboBox" })] +[assembly: GenerateComponent(typeof(Material.Components.Maui.ComboBoxItem), Aliases = new[] { "ComboBoxItem:MDComboBoxItem" })] +[assembly: GenerateComponent(typeof(Material.Components.Maui.MenuItem), Aliases = new[] { "MenuItem:MDMenuItem" })] [assembly: GenerateComponent(typeof(Material.Components.Maui.SKTouchCanvasView))] [assembly: GenerateComponent(typeof(SKCanvasView))] diff --git a/src/BlazorBindings.Maui.ComponentGenerator/GeneratedPropertyInfo.RenderFragment.cs b/src/BlazorBindings.Maui.ComponentGenerator/GeneratedPropertyInfo.RenderFragment.cs index 548c20e0..2e9bd078 100644 --- a/src/BlazorBindings.Maui.ComponentGenerator/GeneratedPropertyInfo.RenderFragment.cs +++ b/src/BlazorBindings.Maui.ComponentGenerator/GeneratedPropertyInfo.RenderFragment.cs @@ -24,6 +24,7 @@ private static readonly (string TypeName, bool AllowDescendantTypes)[] ContentTy public bool IsRenderFragmentProperty => Kind == GeneratedPropertyKind.RenderFragment; public bool IsControlTemplate => _propertyInfo.Type.GetFullName() == "Microsoft.Maui.Controls.ControlTemplate"; public bool IsDataTemplate => _propertyInfo.Type.GetFullName() == "Microsoft.Maui.Controls.DataTemplate"; + public bool ForceContent => ContainingType.Settings.ContentProperties.Contains(_propertyInfo.Name); public string GetHandleContentProperty() { @@ -53,10 +54,10 @@ public string RenderContentProperty() var itemTypeName = GenericTypeArgument is null ? "T" : GetTypeNameAndAddNamespace(GenericTypeArgument); return $"\r\n RenderTreeBuilderHelper.AddDataTemplateProperty<{MauiContainingTypeName}, {itemTypeName}>(builder, sequence++, {ComponentPropertyName}, (x, template) => x.{_propertyInfo.Name} = template);"; } - else if (type.IsGenericType && type.ConstructedFrom.SpecialType == SpecialType.System_Collections_Generic_IList_T) + else if (!ForceContent && IsIList(type, out var itemType)) { // RenderTreeBuilderHelper.AddListContentProperty(builder, sequence++, ChildContent, x => x.Children); - var itemTypeName = GetTypeNameAndAddNamespace(type.TypeArguments[0]); + var itemTypeName = GetTypeNameAndAddNamespace(itemType); return $"\r\n RenderTreeBuilderHelper.AddListContentProperty<{MauiContainingTypeName}, {itemTypeName}>(builder, sequence++, {ComponentPropertyName}, x => x.{_propertyInfo.Name});"; } else @@ -73,12 +74,26 @@ internal static GeneratedPropertyInfo[] GetContentProperties(GeneratedTypeInfo c var propInfos = GetMembers(componentInfo.TypeSymbol, containingType.Settings.Include) .Where(e => !componentInfo.Exclude.Contains(e.Name)) .Where(IsPublicProperty) + .Where(prop => !IsReferenceProperty(containingType, prop)) .Where(prop => IsRenderFragmentPropertySymbol(containingType, prop)) .OrderBy(prop => prop.Name, StringComparer.OrdinalIgnoreCase); return propInfos.Select(prop => new GeneratedPropertyInfo(containingType, prop, GeneratedPropertyKind.RenderFragment)).ToArray(); } + private static bool IsReferenceProperty(GeneratedTypeInfo containingType, IPropertySymbol prop) + { + if (containingType.Settings.ContentProperties.Contains(prop.Name)) + return false; + + // RenderFragment property makes sense when we're creating a new element. + // However, some properties expect not a new element, but a reference to an existing one. + // E.g. VisibleViews, SelectedItem, CurrentItem, etc. + // As for now we exclude such properties. + var referenceNames = new[] { "Visible", "Selected", "Current" }; + return referenceNames.Any(n => prop.Name.Contains(n)); + } + private static bool IsRenderFragmentPropertySymbol(GeneratedTypeInfo containingType, IPropertySymbol prop) { if (containingType.Settings.ContentProperties.Contains(prop.Name)) @@ -88,10 +103,7 @@ private static bool IsRenderFragmentPropertySymbol(GeneratedTypeInfo containingT if (IsContent(type) && HasPublicSetter(prop)) return true; - if (type is INamedTypeSymbol namedType - && namedType.IsGenericType - && namedType.ConstructedFrom.SpecialType == SpecialType.System_Collections_Generic_IList_T - && IsContent(namedType.TypeArguments[0])) + if (IsIList(type, out var itemType) && IsContent(itemType)) { return true; } @@ -107,5 +119,28 @@ bool IsContent(ITypeSymbol type) => ContentTypes.Any(t => || t.AllowDescendantTypes && conversion is { IsReference: true, IsImplicit: true }; }); } + + private static bool IsIList(ITypeSymbol type, out ITypeSymbol itemType) + { + var isList = TypeEqualsIList(type, out var outItemType) || type.AllInterfaces.Any(i => IsIList(i, out outItemType)); + itemType = outItemType; + return isList; + + static bool TypeEqualsIList(ITypeSymbol type, out ITypeSymbol itemType) + { + if (type is INamedTypeSymbol namedType + && namedType.IsGenericType + && namedType.ConstructedFrom.SpecialType == SpecialType.System_Collections_Generic_IList_T) + { + itemType = namedType.TypeArguments[0]; + return true; + } + else + { + itemType = null; + return false; + } + } + } } } diff --git a/src/BlazorBindings.Maui/Elements/ShellContent.generated.cs b/src/BlazorBindings.Maui/Elements/ShellContent.generated.cs index f4238609..a7b6a69f 100644 --- a/src/BlazorBindings.Maui/Elements/ShellContent.generated.cs +++ b/src/BlazorBindings.Maui/Elements/ShellContent.generated.cs @@ -8,6 +8,7 @@ using BlazorBindings.Core; using MC = Microsoft.Maui.Controls; using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; using System.Threading.Tasks; #pragma warning disable CA2252 @@ -24,10 +25,34 @@ static ShellContent() RegisterAdditionalHandlers(); } + /// + /// Add instances to flyout. + /// + [Parameter] public RenderFragment MenuItems { get; set; } + public new MC.ShellContent NativeControl => (MC.ShellContent)((BindableObject)this).NativeControl; protected override MC.ShellContent CreateNativeElement() => new(); + protected override void HandleParameter(string name, object value) + { + switch (name) + { + case nameof(MenuItems): + MenuItems = (RenderFragment)value; + break; + + default: + base.HandleParameter(name, value); + break; + } + } + + protected override void RenderAdditionalElementContent(RenderTreeBuilder builder, ref int sequence) + { + base.RenderAdditionalElementContent(builder, ref sequence); + RenderTreeBuilderHelper.AddListContentProperty(builder, sequence++, MenuItems, x => x.MenuItems); + } static partial void RegisterAdditionalHandlers(); }