Skip to content

Commit

Permalink
Initial Shell support
Browse files Browse the repository at this point in the history
  • Loading branch information
Eilon committed Oct 25, 2019
1 parent 7e2e02d commit 14641a1
Show file tree
Hide file tree
Showing 21 changed files with 677 additions and 3 deletions.
5 changes: 2 additions & 3 deletions samples/BlazorNativeTodo/TodoApp.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<TabbedPage>
<Shell>
<ContentPage Title="Todo">
<StackLayout>
<Label FontSize="20" TextColor="Color.Crimson" Text="Todo Items" HorizontalTextAlignment="TextAlignment.Center" />
Expand All @@ -11,5 +11,4 @@
<ContentPage Title="About">
<About />
</ContentPage>
</TabbedPage>

</Shell>
60 changes: 60 additions & 0 deletions src/Microsoft.Blazor.Native/BlazorNativeElementManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Emblazon;
using System.Diagnostics;
using Xamarin.Forms;
using System;

namespace Microsoft.Blazor.Native
{
Expand Down Expand Up @@ -52,6 +53,60 @@ protected override bool IsParented(IXamarinFormsElementHandler handler)

switch (parent)
{
case Shell parentAsShell:
switch (child)
{
case TemplatedPage childAsTemplatedPage:
parentAsShell.Items.Add(childAsTemplatedPage); // Implicit conversion
break;
case ShellContent childAsShellContent:
parentAsShell.Items.Add(childAsShellContent); // Implicit conversion
break;
case ShellSection childAsShellSection:
parentAsShell.Items.Add(childAsShellSection); // Implicit conversion
break;
case ShellItem childAsShellItem:
parentAsShell.Items.Add(childAsShellItem);
break;
default:
throw new ArgumentException($"Cannot add child of type '{child?.GetType().FullName}' to parent of type '{parent?.GetType().FullName}'.");
}
break;
case ShellItem parentAsShellItem:
switch (child)
{
case TemplatedPage childAsTemplatedPage:
parentAsShellItem.Items.Add(childAsTemplatedPage); // Implicit conversion
break;
case ShellContent childAsShellContent:
parentAsShellItem.Items.Add(childAsShellContent); // Implicit conversion
break;
case ShellSection childAsShellSection:
parentAsShellItem.Items.Add(childAsShellSection);
break;
default:
throw new ArgumentException($"Cannot add child of type '{child?.GetType().FullName}' to parent of type '{parent?.GetType().FullName}'.");
}
break;
case ShellSection parentAsShellSection:
switch (child)
{
case TemplatedPage childAsTemplatedPage:
parentAsShellSection.Items.Add(childAsTemplatedPage); // Implicit conversion
break;
case ShellContent childAsShellContent:
parentAsShellSection.Items.Add(childAsShellContent);
break;
default:
throw new ArgumentException($"Cannot add child of type '{child?.GetType().FullName}' to parent of type '{parent?.GetType().FullName}'.");
}
break;
case ShellContent parentAsShellContent:
{
var childAsTemplatedPage = child as TemplatedPage;
parentAsShellContent.Content = childAsTemplatedPage;
}
break;
case Layout<View> parentAsLayout:
{
var childAsView = child as View;
Expand Down Expand Up @@ -184,6 +239,11 @@ protected override bool IsParented(IXamarinFormsElementHandler handler)
// TODO: What is the set of types that support child elements? Do they all need to be special-cased here? (Maybe...)
var nativeComponent = handler.ElementControl;

if (nativeComponent.Parent is null)
{
return 0;
}

switch (nativeComponent.Parent)
{
case Layout<View> parentAsLayout:
Expand Down
64 changes: 64 additions & 0 deletions src/Microsoft.Blazor.Native/Elements/BaseShellItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Emblazon;
using Microsoft.AspNetCore.Components;
using Microsoft.Blazor.Native.Elements.Handlers;
using XF = Xamarin.Forms;

namespace Microsoft.Blazor.Native.Elements
{
public class BaseShellItem : NavigableElement
{
static BaseShellItem()
{
ElementHandlerRegistry.RegisterElementHandler<BaseShellItem>(
renderer => new BaseShellItemHandler(renderer, new XF.BaseShellItem()));
}

[Parameter] public XF.ImageSource FlyoutIcon { get; set; }
[Parameter] public XF.ImageSource Icon { get; set; }
[Parameter] public bool? IsEnabled { get; set; }
[Parameter] public bool? IsTabStop { get; set; }
[Parameter] public string Route { get; set; }
[Parameter] public string Title { get; set; }
[Parameter] public int? TabIndex { get; set; }

[Parameter] public EventCallback OnAppearing { get; set; }
[Parameter] public EventCallback OnDisappearing { get; set; }

protected override void RenderAttributes(AttributesBuilder builder)
{
base.RenderAttributes(builder);

if (FlyoutIcon != null)
{
builder.AddAttribute(nameof(FlyoutIcon), AttributeHelper.ImageSourceToString(FlyoutIcon));
}
if (Icon != null)
{
builder.AddAttribute(nameof(Icon), AttributeHelper.ImageSourceToString(Icon));
}
if (IsEnabled != null)
{
builder.AddAttribute(nameof(IsEnabled), IsEnabled.Value);
}
if (IsTabStop != null)
{
builder.AddAttribute(nameof(IsTabStop), IsTabStop.Value);
}
if (Route != null)
{
builder.AddAttribute(nameof(Route), Route);
}
if (Title != null)
{
builder.AddAttribute(nameof(Title), Title);
}
if (TabIndex != null)
{
builder.AddAttribute(nameof(TabIndex), TabIndex.Value);
}

builder.AddAttribute("onappearing", OnAppearing);
builder.AddAttribute("ondisappearing", OnDisappearing);
}
}
}
15 changes: 15 additions & 0 deletions src/Microsoft.Blazor.Native/Elements/FlyoutItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Emblazon;
using Microsoft.Blazor.Native.Elements.Handlers;
using XF = Xamarin.Forms;

namespace Microsoft.Blazor.Native.Elements
{
public class FlyoutItem : ShellItem
{
static FlyoutItem()
{
ElementHandlerRegistry.RegisterElementHandler<FlyoutItem>(
renderer => new FlyoutItemHandler(renderer, new XF.FlyoutItem()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using Emblazon;
using System;
using XF = Xamarin.Forms;

namespace Microsoft.Blazor.Native.Elements.Handlers
{
public class BaseShellItemHandler : NavigableElementHandler
{
public BaseShellItemHandler(EmblazonRenderer renderer, XF.BaseShellItem baseShellItemControl) : base(renderer, baseShellItemControl)
{
BaseShellItemControl = baseShellItemControl ?? throw new ArgumentNullException(nameof(baseShellItemControl));

BaseShellItemControl.Appearing += (s, e) =>
{
if (AppearingEventHandlerId != default)
{
renderer.Dispatcher.InvokeAsync(() => renderer.DispatchEventAsync(AppearingEventHandlerId, null, e));
}
};
BaseShellItemControl.Disappearing += (s, e) =>
{
if (DisappearingEventHandlerId != default)
{
renderer.Dispatcher.InvokeAsync(() => renderer.DispatchEventAsync(DisappearingEventHandlerId, null, e));
}
};
}

public XF.BaseShellItem BaseShellItemControl { get; }
public ulong AppearingEventHandlerId { get; set; }
public ulong DisappearingEventHandlerId { get; set; }

public override void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
{
switch (attributeName)
{
case nameof(XF.BaseShellItem.FlyoutIcon):
BaseShellItemControl.FlyoutIcon = attributeValue == null ? null : AttributeHelper.StringToImageSource((string)attributeValue);
break;
case nameof(XF.BaseShellItem.Icon):
BaseShellItemControl.Icon = attributeValue == null ? null : AttributeHelper.StringToImageSource((string)attributeValue);
break;
case nameof(XF.BaseShellItem.IsEnabled):
BaseShellItemControl.IsEnabled = AttributeHelper.GetBool(attributeValue);
break;
case nameof(XF.BaseShellItem.IsTabStop):
BaseShellItemControl.IsTabStop = AttributeHelper.GetBool(attributeValue);
break;
case nameof(XF.BaseShellItem.Route):
BaseShellItemControl.Route = (string)attributeValue;
break;
case nameof(XF.BaseShellItem.Title):
BaseShellItemControl.Title = (string)attributeValue;
break;
case nameof(XF.BaseShellItem.TabIndex):
BaseShellItemControl.TabIndex = AttributeHelper.GetInt(attributeValue);
break;

case "onappearing":
Renderer.RegisterEvent(attributeEventHandlerId, () => AppearingEventHandlerId = 0);
AppearingEventHandlerId = attributeEventHandlerId;
break;
case "ondisappearing":
Renderer.RegisterEvent(attributeEventHandlerId, () => DisappearingEventHandlerId = 0);
DisappearingEventHandlerId = attributeEventHandlerId;
break;
default:
base.ApplyAttribute(attributeEventHandlerId, attributeName, attributeValue, attributeEventUpdatesAttributeName);
break;
}
}
}
}
16 changes: 16 additions & 0 deletions src/Microsoft.Blazor.Native/Elements/Handlers/FlyoutItemHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Emblazon;
using System;
using XF = Xamarin.Forms;

namespace Microsoft.Blazor.Native.Elements.Handlers
{
public class FlyoutItemHandler : ShellItemHandler
{
public FlyoutItemHandler(EmblazonRenderer renderer, XF.FlyoutItem flyoutItemControl) : base(renderer, flyoutItemControl)
{
FlyoutItemControl = flyoutItemControl ?? throw new ArgumentNullException(nameof(flyoutItemControl));
}

public XF.FlyoutItem FlyoutItemControl { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Emblazon;
using System;
using XF = Xamarin.Forms;

namespace Microsoft.Blazor.Native.Elements.Handlers
{
public class ShellContentHandler : BaseShellItemHandler
{
public ShellContentHandler(EmblazonRenderer renderer, XF.ShellContent shellContentControl) : base(renderer, shellContentControl)
{
ShellContentControl = shellContentControl ?? throw new ArgumentNullException(nameof(shellContentControl));
}

public XF.ShellContent ShellContentControl { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using XF = Xamarin.Forms;

namespace Microsoft.Blazor.Native.Elements.Handlers
{
internal sealed class ShellContentMarkerItem : XF.ShellContent
{
public ShellContentMarkerItem()
{
// Set dummy content to ensure the item is valid
Content = new XF.ContentPage();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Emblazon;
using System;
using XF = Xamarin.Forms;

namespace Microsoft.Blazor.Native.Elements.Handlers
{
public class ShellGroupItemHandler : BaseShellItemHandler
{
public ShellGroupItemHandler(EmblazonRenderer renderer, XF.ShellGroupItem shellGroupItemControl) : base(renderer, shellGroupItemControl)
{
ShellGroupItemControl = shellGroupItemControl ?? throw new ArgumentNullException(nameof(shellGroupItemControl));
}

public XF.ShellGroupItem ShellGroupItemControl { get; }

public override void ApplyAttribute(ulong attributeEventHandlerId, string attributeName, object attributeValue, string attributeEventUpdatesAttributeName)
{
switch (attributeName)
{
case nameof(XF.ShellGroupItem.FlyoutDisplayOptions):
ShellGroupItemControl.FlyoutDisplayOptions = (XF.FlyoutDisplayOptions)AttributeHelper.GetInt(attributeValue);
break;
default:
base.ApplyAttribute(attributeEventHandlerId, attributeName, attributeValue, attributeEventUpdatesAttributeName);
break;
}
}
}
}

0 comments on commit 14641a1

Please sign in to comment.