diff --git a/Maui.ServerDrivenUI/Abstractions/IServerDrivenVisualElement.cs b/Maui.ServerDrivenUI/Abstractions/IServerDrivenVisualElement.cs
index 1a4978f..ef77b49 100644
--- a/Maui.ServerDrivenUI/Abstractions/IServerDrivenVisualElement.cs
+++ b/Maui.ServerDrivenUI/Abstractions/IServerDrivenVisualElement.cs
@@ -2,21 +2,17 @@
public interface IServerDrivenVisualElement
{
- public string? ServerKey
- {
- get; set;
- }
+ string? ServerKey { get; set; }
- public UIElementState State
- {
- get; set;
- }
- public Action OnLoaded
- {
- get;
- set;
- }
+ UIElementState State { get; set; }
+
+ Action? OnLoaded { get; set; }
+
+ DataTemplate LoadingTemplate { get; set; }
+
+ DataTemplate ErrorTemplate { get; set; }
void OnStateChanged(UIElementState newState);
+
void OnError(Exception ex);
}
diff --git a/Maui.ServerDrivenUI/Views/ServerDrivenContentPage.cs b/Maui.ServerDrivenUI/Views/ServerDrivenContentPage.cs
index f576cb0..577476f 100644
--- a/Maui.ServerDrivenUI/Views/ServerDrivenContentPage.cs
+++ b/Maui.ServerDrivenUI/Views/ServerDrivenContentPage.cs
@@ -18,6 +18,18 @@ public abstract class ServerDrivenContentPage : ContentPage, IServerDrivenVisual
typeof(ServerDrivenContentPage),
UIElementState.None,
propertyChanged: ServerDrivenVisualElement.OnStatePropertyChanged);
+
+ public static readonly BindableProperty LoadingTemplateProperty = BindableProperty.Create(
+ nameof(LoadingTemplate),
+ typeof(DataTemplate),
+ typeof(ServerDrivenView),
+ null);
+
+ public static readonly BindableProperty ErrorTemplateProperty = BindableProperty.Create(
+ nameof(ErrorTemplate),
+ typeof(DataTemplate),
+ typeof(ServerDrivenView),
+ null);
#endregion
@@ -35,12 +47,24 @@ public UIElementState State
set => SetValue(StateProperty, value);
}
- public Action OnLoaded
+ public Action? OnLoaded
{
get;
set;
}
+ public DataTemplate LoadingTemplate
+ {
+ get => (DataTemplate)GetValue(LoadingTemplateProperty);
+ set => SetValue(LoadingTemplateProperty, value);
+ }
+
+ public DataTemplate ErrorTemplate
+ {
+ get => (DataTemplate)GetValue(ErrorTemplateProperty);
+ set => SetValue(ErrorTemplateProperty, value);
+ }
+
#endregion
#region Constructors
diff --git a/Maui.ServerDrivenUI/Views/ServerDrivenView.cs b/Maui.ServerDrivenUI/Views/ServerDrivenView.cs
index 68a2ebc..8e8145a 100644
--- a/Maui.ServerDrivenUI/Views/ServerDrivenView.cs
+++ b/Maui.ServerDrivenUI/Views/ServerDrivenView.cs
@@ -19,6 +19,18 @@ public class ServerDrivenView : ContentView, IServerDrivenVisualElement
UIElementState.None,
propertyChanged: ServerDrivenVisualElement.OnStatePropertyChanged);
+ public static readonly BindableProperty LoadingTemplateProperty = BindableProperty.Create(
+ nameof(LoadingTemplate),
+ typeof(DataTemplate),
+ typeof(ServerDrivenView),
+ null);
+
+ public static readonly BindableProperty ErrorTemplateProperty = BindableProperty.Create(
+ nameof(ErrorTemplate),
+ typeof(DataTemplate),
+ typeof(ServerDrivenView),
+ null);
+
#endregion
#region Properties
@@ -35,6 +47,18 @@ public UIElementState State
set => SetValue(StateProperty, value);
}
+ public DataTemplate LoadingTemplate
+ {
+ get => (DataTemplate)GetValue(LoadingTemplateProperty);
+ set => SetValue(LoadingTemplateProperty, value);
+ }
+
+ public DataTemplate ErrorTemplate
+ {
+ get => (DataTemplate)GetValue(ErrorTemplateProperty);
+ set => SetValue(ErrorTemplateProperty, value);
+ }
+
public Action? OnLoaded
{
get;
diff --git a/Maui.ServerDrivenUI/Views/ServerDrivenVisualElement.cs b/Maui.ServerDrivenUI/Views/ServerDrivenVisualElement.cs
index e038e81..62652e0 100644
--- a/Maui.ServerDrivenUI/Views/ServerDrivenVisualElement.cs
+++ b/Maui.ServerDrivenUI/Views/ServerDrivenVisualElement.cs
@@ -9,8 +9,10 @@ internal class ServerDrivenVisualElement
internal static async Task InitializeComponentAsync(IServerDrivenVisualElement element, int attempt = 0)
{
+ var errorMessage = string.Empty;
try
{
+ ShowLoadingView(element);
MainThread.BeginInvokeOnMainThread(() => element.State = UIElementState.Loading);
var serverDrivenUiService = ServiceProviderHelper
@@ -60,6 +62,8 @@ internal static async Task InitializeComponentAsync(IServerDrivenVisualElement e
}
else
{
+ errorMessage = SERVICE_NOT_FOUND;
+
MainThread.BeginInvokeOnMainThread(() => {
element.State = UIElementState.Error;
element.OnError(new DependencyRegistrationException(SERVICE_NOT_FOUND));
@@ -68,11 +72,17 @@ internal static async Task InitializeComponentAsync(IServerDrivenVisualElement e
}
catch (Exception ex)
{
+ errorMessage = ex.Message;
MainThread.BeginInvokeOnMainThread(() => {
element.State = UIElementState.Error;
element.OnError(ex);
});
}
+
+ if (!string.IsNullOrWhiteSpace(errorMessage))
+ {
+ ShowErrorView(element, errorMessage);
+ }
}
private static bool IsXamlLoaded(IServerDrivenVisualElement element, int attempt)
@@ -96,6 +106,50 @@ private static bool IsXamlLoaded(IServerDrivenVisualElement element, int attempt
return true;
}
+ private static void ShowLoadingView(IServerDrivenVisualElement element)
+ {
+ View loadingView = (element.LoadingTemplate?.CreateContent() as View)
+ ?? CreateDefaultLoadingTemplate();
+
+ SetContent(element, loadingView);
+ }
+
+ private static void ShowErrorView(IServerDrivenVisualElement element, string errorMessage)
+ {
+ try
+ {
+ View errorView = (element.ErrorTemplate?.CreateContent() as View)
+ ?? CreateDefaultErrorTemplate(errorMessage);
+
+ SetContent(element, errorView);
+ }
+ catch (Exception ex)
+ {
+ MainThread.BeginInvokeOnMainThread(() => {
+ element.State = UIElementState.Error;
+ element.OnError(ex);
+ });
+ }
+ }
+
+ private static void SetContent(IServerDrivenVisualElement element, View template)
+ {
+ if (element is ContentView contentView && template != null)
+ {
+ MainThread.BeginInvokeOnMainThread(() => {
+ template.BindingContext = contentView.BindingContext;
+ contentView.Content = template;
+ });
+ }
+ else if (element is ContentPage contentPage && template != null)
+ {
+ MainThread.BeginInvokeOnMainThread(() => {
+ template.BindingContext = contentPage.BindingContext;
+ contentPage.Content = template;
+ });
+ }
+ }
+
internal static void OnStatePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is not IServerDrivenVisualElement view
@@ -106,4 +160,34 @@ internal static void OnStatePropertyChanged(BindableObject bindable, object oldV
if (newState is UIElementState.Loaded)
view.OnLoaded?.Invoke();
}
+
+ internal static View CreateDefaultLoadingTemplate() =>
+ new Frame() {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ BackgroundColor = Colors.Black,
+ BorderColor = Colors.Transparent,
+ Content = new ActivityIndicator() {
+ IsRunning = true,
+ IsEnabled = true,
+ Color = Colors.White,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ }
+ };
+
+ internal static View CreateDefaultErrorTemplate(string errorMessage) =>
+ new Frame() {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ BackgroundColor = Colors.Red,
+ BorderColor = Colors.Transparent,
+ Content = new Label {
+ Text = errorMessage,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center
+ }
+ };
}
diff --git a/README.md b/README.md
index c181f73..b1d9f50 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,10 @@ public static class MauiProgram
}
```
-- You can now use the ServerDrivenUI Elements
+- You can now use the ServerDrivenUI Elements, defining the key that will be used to get the UI from the API
+- You can also define a LoadingTemplate and an ErrorTemplate to be shown while the UI is being fetched from the API
+
+```csharp
```xaml
@@ -53,7 +56,23 @@ public static class MauiProgram
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -67,4 +86,4 @@ We are currently doing a [workaround](https://github.com/felipebaltazar/Maui.Ser
## Repo Activity
-![Alt](https://repobeats.axiom.co/api/embed/e3457a9dc9131c33ca38ceb2203bfffa67864080.svg "Repobeats analytics image")
+![Alt](https://repobeats.axiom.co/api/embed/e3457a9dc9131c33ca38ceb2203bfffa67864080.svg "Repo activity analytics image")
diff --git a/samples/Maui.ServerDrivenUI.Sample/MainPage.xaml b/samples/Maui.ServerDrivenUI.Sample/MainPage.xaml
index 1340a34..02cdc37 100644
--- a/samples/Maui.ServerDrivenUI.Sample/MainPage.xaml
+++ b/samples/Maui.ServerDrivenUI.Sample/MainPage.xaml
@@ -4,6 +4,22 @@
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Maui.ServerDrivenUI.Sample/ViewModelBase.cs b/samples/Maui.ServerDrivenUI.Sample/ViewModelBase.cs
index 0c0c921..4e5629a 100644
--- a/samples/Maui.ServerDrivenUI.Sample/ViewModelBase.cs
+++ b/samples/Maui.ServerDrivenUI.Sample/ViewModelBase.cs
@@ -11,7 +11,6 @@ protected void OnPropertyChanged(string propertyName) =>
protected void Set(ref T field, T value, string propertyName)
{
-
if (!EqualityComparer.Default.Equals(field, value))
{
field = value;