Windows

Overview

The Layouts API is accessible through io.Layouts.

Layout Operations

Current Global Layout

Available since io.Connect Desktop 9.3

To get the currently restored Global Layout, use the GetCurrentLayout() method:

ILayout currentLayout = await io.Layouts.GetCurrentLayout();

Listing All Layouts

To get a collection of all currently available Layouts synchronously, use the List() method:

ILayout[] layouts = io.Layouts.List();

To get a collection of all currently available Layouts asynchronously, use the ListAsync() method:

ILayout[] layouts = await io.Layouts.ListAsync();

Specific Layout

To get a specific Layout, use the AwaitLayout() method and pass a predicate function to find the desired Layout:

ILayout myLayout = await io.Layouts.AwaitLayout(layout => findMyLayout(layout));

Save

To save a Layout, use the Save() method and pass a SaveOptions object with a required Name property. If a Layout with that name already exists, it will be replaced. The method resolves with the saved Layout:

SaveOptions options = new SaveOptions { Name = "My Layout" };

ILayout savedLayout = await io.Layouts.Save(options);

Available since io.Connect Desktop 9.1

When saving a Layout, you can specify whether to set it as the currently active Global Layout by using the SetAsCurrent property of the SaveLayoutOptions object:

SaveOptions options = new SaveOptions
{
    Name = "My Layout",
    SaveLayoutOptions = new SaveLayoutOptions { SetAsCurrent = true }
};

ILayout savedLayout = await io.Layouts.Save(options);

Restore

To restore a Layout, use the Restore() method and pass the Layout object as a first argument. You can also pass a RestoreOptions object as a second argument in which to specify options for the restored Layout:

RestoreOptions options = new RestoreOptions { RestoreMode = CurrentInstances.CloseAndReuse };

ILayout restoredLayout = await io.Layouts.Restore(myLayout, options);

Rename

To rename a Layout, use the Rename() method. You must pass the Layout object to rename as a first argument and the new name for the Layout as a second argument:

await io.Layouts.Rename(myLayout, "My New Layout");

Remove

To remove a layout, use the Remove() method and pass the Layout object to be removed:

ILayout removedLayout = await io.Layouts.Remove(myLayout);

Export & Import

You can export all currently available Layout objects with the ExportLayouts() method. Exported Layouts can be stored to a database and then be used as restore points, or can be sent to another user and imported on their machine:

Tuple<ILayout, Value>[] exportedLayouts = await io.Layouts.ExportLayouts();

To import exported Layouts, use the ImportLayouts() method. Specify the ImportMode as a first argument and pass the collection of Layout objects to import as a second argument:

await io.Layouts.ImportLayouts(ImportMode.Merge, myLayouts);

The ImportMode controls the import behavior. If set to ImportMode.Replace, all existing Layouts will be removed and will be replaced by the imported ones. If set to ImportMode.Merge, the imported Layouts will be added to the existing ones, replacing any of them with conflicting names.

Layout Events

The Layouts API allows your app to react to Layout events - adding, removing, updating or renaming a Layout:

// Notifies when a new Layout is added.
io.Layouts.LayoutAdded += LayoutsManagerOnLayoutEvent;

// Notifies when a Layout is removed.
io.Layouts.LayoutRemoved += LayoutsManagerOnLayoutEvent;

// Notifies when a Layout is changed.
io.Layouts.LayoutChanged += LayoutsManagerOnLayoutEvent;

// Notifies when a Layout is renamed.
io.Layouts.LayoutRenamed += LayoutsManagerOnLayoutEvent;

private void LayoutsManagerOnLayoutEvent(object sender, LayoutEventArgs e)
{
    switch (e.LayoutEvent)
    {
        case LayoutEvent.OnLayoutAdded:
            HandleLayoutEvent(() => OnLayoutAdded(e.Layout));
            break;
        case LayoutEvent.OnLayoutChanged:
            HandleLayoutEvent(() => OnLayoutChanged(e.Layout));
            break;
        case LayoutEvent.OnLayoutRemoved:
            HandleLayoutEvent(() => OnLayoutRemoved(e.Layout));
            break;
        case LayoutEvent.OnLayoutRenamed:
            HandleLayoutEvent(() => OnLayoutRenamed(e.Layout, e.PrevLayout));
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
}

Saving Context

When a Layout is saved, apps can store context data in it. When the Layout is restored, the context data is also restored and returned to the apps. Context data can be saved in all Layout types.

⚠️ Note that saving large volumes of custom data as window context (e.g., thousands of lines of table data) can lead to significant delays when saving a Layout. A Layout usually contains several (in some cases - many) apps and/or Workspaces (which can also contain many apps) and if one or more of the apps saves large amounts of context data each time a Layout is saved, this will significantly slow down the saving process. The methods for saving custom context work best with smaller amounts of data. If your app needs to save large amounts of data, you have to think about how to design this process better - for instance, you may store IDs, indices, etc., as context data, save the actual data to a database and when you restore the Layout, fetch the data using the data IDs saved as window context.

To save custom data, apps can subscribe for Layout save requests. The callback passed as an argument will be invoked when a Layout save is requested.

You can define your own typed configuration for your app:

public class AppState
{
    public string SelectedClient { get; set; }
    public bool DarkThemeOn { get; set; }
}

When initializing the library, you can specify the save/restore endpoint as a typed/untyped callback:

var options = new InitializeOptions();

options.SetSaveRestoreStateEndpoint(
    saveState => new AppState
        {
            DarkThemeOn = false,
            SelectedClient = "clientID"
        }.AsCompletedTask(),
    restoreState =>
        {
            AppState stateToRestore = restoreState;
            // Handle the restore state.
        }
);

Glue42 io = await Glue42.InitializeGlue(options);

For convenience, the save state endpoint requires a Task, so you can save your app state asynchronously.

Alternatively, you can restore your app state by extracting it from the io.Connect library instance after it has been initialized:

var options = new InitializeOptions();

// Specify only a save state callback.
options.SetSaveRestoreStateEndpoint(
    saveState => new AppState
        {
            DarkThemeOn = false,
            SelectedClient = "some client"
        }.AsCompletedTask()
);

Glue42 io = await Glue42.InitializeGlue(options);

AppState restoreState = io.GetRestoreState<AppState>();
// Handle the restore state.

If you want to return any object and work in an untyped manner, use object instead of specifying a generic type, and use the OnSaveState property of the library initialization object:

var options = new InitializeOptions
{
    OnSaveState = saveState => new Dictionary<string, object>().AsCompletedTask<object>()
};

Glue42 io = await Glue42.InitializeGlue(options);

var restoreState = io.GetRestoreState<Dictionary<string, object>>();

Default Global Layout

io.Connect Desktop allows you to specify a default Global Layout that will be automatically loaded on start. You can get, set and clear the default Global Layout programmatically.

To get the current default Global Layout, use the GetDefaultLayout() method:

// May return `null` if no default Global Layout has been set.
ILayout defaultLayout = await io.Layouts.GetDefaultLayout();

To set a default Global Layout, use the SelectDefaultLayout() method and pass a Layout object as an argument:

ILayout defaultLayout = await io.Layouts.SelectDefaultLayout(myLayout);

To clear the default Global Layout, use the DeselectDefaultLayout() method:

await io.Layouts.DeselectDefaultLayout();