# .NET

Source: https://docs.interop.io/desktop/capabilities/windows/layouts/net/index.html

## Overview

The Layouts API is accessible via the `io.Layouts` object.

## Layout Operations

### Current Global Layout

Available since io.Connect Desktop 9.3

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

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

### Listing All Layouts

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

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

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

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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.

## Events

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

```csharp
// 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 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:

```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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](https://docs.interop.io/desktop/capabilities/windows/layouts/overview/index.md#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:

```csharp
// 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:

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

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

```csharp
await io.Layouts.DeselectDefaultLayout();
```
