Component Reference

tip

Did you know Geocortex Workflow allows you to create custom forms you can present to an end user?

Implementing a custom component in Geocortex Mobile gives you the highest degree of flexibility with what you can do. Components can display custom, dynamic UI, register operation implementations, store persistent data, and more. They are also one of the most complex ways of customizing your mobile app, so it might be worthwhile to review simpler options like Commands and Operations or Workflow first.

[assembly: Component(typeof(CustomComponent), "custom-component", XmlNamespace = XmlNamespaces.App1Namespace)]
namespace App1.Components
{
class CustomComponent : ComponentBase
{
protected override VisualElement Create(XNode node)
{
return new Label(){ Text = "My Custom Component" };
}
}
}

Component Registration#

Geocortex Mobile uses Autofac to register, locate, and inject components, services and other classes.

Each component class is registered with the Component assembly attribute

note

It's convention for the name of the component in the layout to be lower case and kebab case, i.e. custom-component.

[assembly: Component(typeof(CustomComponent), "custom-component", XmlNamespace = XmlNamespaces.App1Namespace)]
namespace App1.Components
{
class CustomComponent : ComponentBase
{
...
}
}

Components and Layout#

Once a component has been registered, it can be used in a layout by referring it by name and namespace.

<?xml version="1.0" encoding="utf-8" ?>
<layout
xmlns="https://geocortex.com/layout/v1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://geocortex.com/layout/v1 ../../ViewerSpec/layout/layout-mobile.xsd"
xmlns:custom="https://your.org/layout/app1">
<custom:custom-component/>
</layout>

Component Anatomy#

Geocortex Mobile is built on top of the Xamarin Platform. In general, XAML Views are used to define reactive component UI. Component Views in Geocortex Mobile are built with the Model-View-ViewModel (MVVM) pattern. The model for components is usually either app config or a service hosting shared application data.

Component Class#

Each instance of a component class directly correlates to a component in the layout. A component class extends ComponentBase and renders visual elements. As seen in the example below, components can provide their UI inline, but it is recommended that components render an associated XAML view instead.

[assembly: Component(typeof(CustomComponent), "custom-component", XmlNamespace = XmlNamespaces.App1Namespace)]
namespace App1.Components
{
class CustomComponent : ComponentBase
{
protected override VisualElement Create(XNode node)
{
return new Label(){ Text = "My Custom Component" };
}
}
}

Component Dependencies#

Components are instantiated by dependency injection when the layout is rendered. This means that a component can also list dependencies that have been registered with autofac, such as the ILayoutModel<MapView> in its constructor, and have them be passed at runtime. This also means that if a class associated with a component has application dependencies, such as a reference to the MapOperations for map interactions, then the component will either need to provide that dependency somehow.

The preferred way to provide sub-dependencies to classes associated with components is to instantiate those associated classes with dependency injection also. Check out managing sub-dependencies for an example.

XAML View#

XAML views and their respective "code-behind" are responsible for the presentation concerns of a component. Check out a complete example in the SDK samples.

XAML views can use any XAML Controls along with built-in Geocortex Mobile XAML Elements and styling.

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="Geocortex.Mobile.Samples.Samples.Custom.XamlComponent.XamlComponentView">
<ContentView.Content>
<StackLayout Margin="5">
<Label Text="Hello XAML Component!" />
<Label Text="This component view is created using a XAML view." />
</StackLayout>
</ContentView.Content>
</ContentView>

View Model#

Component view models are responsible for populating properties with data for the view to bind to. The data for the view model is usually provided by the component class, which can consume app config or other data from the application through dependency injection. View models can use commands and operations to interact with application services and other components.

Check out this example of a component with a view model.

Configuration Models#

Every component is bound to specific item type, and each item type is bound to a class registered as an AppItem with autofac. This class instantiates itself with values from the app config in its constructor, acting as a configuration model. The component class then consumes this configuration model, and then passes the appropriate values to the view and view model.

[assembly: AppItem(MyAppItem.ConfigItemtype, typeof(MyAppItem))]
namespace VertiGIS.Mobile.Samples.Samples.Custom.ComponentConfiguration
{
public class MyAppItem : VisualAppItem
{
public const string ConfigItemtype = "my-app-item";
public string ConfigText { get; private set; }
public MyAppItem()
: this(Guid.NewGuid().ToString())
{
}
public MyAppItem(string id)
: this(new Properties { ["id"] = id })
{
}
public MyAppItem(Properties properties) :
base(properties, ConfigItemtype)
{
if (properties.TryGetValue("text", out var text))
{
ConfigText = text as string ?? "Default text.";
}
}
}
}

Component Defaults#

Most components support the config attribute in the layout, which links a component to configuration in the app config JSON. However, many configuration models have default values they can supply for initialization instead of relying on a value to exist in the config. Any AppItem class can provide default values through the CreateDefault method. This method should return an instance of the AppItem which represents the default values.

[assembly: AppItem(MyAppItem.ConfigItemtype, typeof(MyAppItem))]
namespace VertiGIS.Mobile.Samples.Samples.Custom.ComponentConfiguration
{
public class MyAppItem : VisualAppItem
{
public const string ConfigItemtype = "my-app-item";
...
public override object CreateDefault()
{
return new MyAppItem();
}
}
}

This pattern is what allows you to place a map with no config in the layout and have it render a default map.

<?xml version="1.0" encoding="utf-8" ?>
<layout
xmlns="https://geocortex.com/layout/v1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://geocortex.com/layout/v1 ../../ViewerSpec/layout/layout-mobile.xsd">
<map/> <!-- The map component is populated with values from the MapExtension's `CreateDefault` function. -->
</layout>

Initialization and Teardown#

Components have an initialization method, which can be used to perform asynchronous startup logic, and a teardown method, which can be used to free resources when a component is removed from the layout.

important

Always call base.Dispose(disposing) when overriding the Dispose method. ComponentBase already implements IDisposable and IDisposableTracker, so only override the Dispose method if you have created new managed resources which need to be cleaned up.

To learn more about memory management in Geocortex Mobile, check out this article, and the relevant SDK sample.

[assembly: Component(typeof(CustomComponent), "component", XmlNamespace = XmlNamespaces.SamplesNamespace)]
namespace VertiGIS.Mobile.Samples.Samples.Custom.Component
{
internal class CustomComponent : ComponentBase
{
private bool disposed = false;
public CustomComponent()
{
}
...
protected override async Task DoInitializeAsync()
{
// Initialization code.
// ...
}
protected override void Dispose(bool disposing)
{
if (disposed)
{
return;
}
if (disposing)
{
// Clean up managed resources.
// ...
}
// Clean up unmanaged resources.
// ...
base.Dispose(disposing);
disposed = true;
}
}
}

Component Lifecycle#

When a Geocortex Mobile Application boots up, the set of components which are in the layout are created. Next, any components that are initially active, depending on the layout, will be activated and initialized. Components like the Panel will only activate their first child.

Activation and Deactivation#

Custom code can listen and react to a components activation or deactivation by subscribing to the ui.activated or ui.deactivated event. The ui.* events contain various events relating to the component lifecycle.

tip

The UIOperations class contains implements the commands ui.activate and ui.deactivate which can be used to examine and manipulate the activation state of components.

class CustomComponent : ComponentBase
{
public CustomComponent(UIEvents uiEvents)
{
uiEvents.ComponentActivated.Subscribe(_onComponentActivated, this);
uiEvents.ComponentDeactivated.Subscribe(_onComponentDeactivated, this);
private async void _onComponentActivated(string id)
{
// ... react to component activation.
}
private async void _onComponentDeactivated(string id)
{
// ... react to component deactivation.
}
...
}

Relevant SDK Samples#

Check out the relevant Geocortex Mobile SDK Samples:

Next Steps#

Learn how to use Commands and Operations with Components

Learn how to run and implement commands and operations with custom components

Learn about Component Interactions

Learn about how components and services can interact with each other

Create a Component with a Complex UI

Follow along with a more in depth component example

Create a Component that Consumes App Config

Learn more about writing components that consume configuration values.