MAUI Desktop Windows

With roots in Xamarin Forms, MAUI is primarily geared toward mobile OS environments. But with its ability to run on both Windows and Mac desktops it can be a useful option for desktop .NET apps to go cross-platform. Some desktop specific features can be painful to use, or even entirely unavailable, but things are improving with each release.

Restoring window sizing and placement across app relaunches is one feature that makes a big usability difference for desktop apps, but doesn’t come for free. MAUI does now have easy access to the things needed to implement this feature yourself though. The two important parts are the ability to get and set window size and position, and reliable saving and reloading of values across app sessions. There is a third part which can improve the feature further that is unfortunately not quite sufficient for this application: getting system screen sizes.

Window Settings

Settings for the window can be initially accessed by overriding the CreateWindow method on your App class. The Window object created there has X, Y, Width and Height properties.

protected override Window CreateWindow(IActivationState? activationState)
{
    var window = base.CreateWindow(activationState);
    
    // set whatever initial values needed
    window.Width = 800;

    return window;
}

When windows are moved or resized, the new values can be accessed from the Window.SizeChanged event.

window.SizeChanged += (_, _) =>
{
    // read window values
};

Saving and Restoring Values

For storing settings the Preferences class provides easy access to key-value data using the Get and Set methods.

Preferences.Set("WindowLocationX", window.X);

Combining the saved preferences with the Window object can be wrapped up into a helper class. Here the X, Y, Width, and Height are saved and reloaded.

public class SavedWindowState
{
    public double? X { get; set; }
    public double? Y { get; set; }
    public double? Width { get; set; }
    public double? Height { get; set; }

    public SavedWindowState(Window window)
    {
        X = window.X;
        Y = window.Y;
        Width = window.Width;
        Height = window.Height;
    }

    public SavedWindowState()
    {
        X = GetSavedValue("WindowLocationX");
        Y = GetSavedValue("WindowLocationY");
        Width = GetSavedValue("WindowSizeWidth");
        Height = GetSavedValue("WindowSizeHeight");
    }

    public void Save()
    {
        SaveValue("WindowLocationX", X);
        SaveValue("WindowLocationY", Y);
        SaveValue("WindowSizeWidth", Width);
        SaveValue("WindowSizeHeight", Height);
    }

    private double? GetSavedValue(string valueKey)
    {
        var xValue = Preferences.Get(valueKey, double.NaN);
        if (!double.IsNaN(xValue))
        {
            return xValue;
        }

        return null;
    }

    private void SaveValue(string valueKey, double? value)
    {
        if (value != null)
            Preferences.Set(valueKey, value.Value);
        else
            Preferences.Remove(valueKey);
    }
}

Using the new class in CreateWindow requires both loading the saved values at startup and then saving new values whenever the window changes.

protected override Window CreateWindow(IActivationState? activationState)
{
    var window = base.CreateWindow(activationState);
    window.Title = "My App Window";
    var windowState = new SavedWindowState();
    
    if (windowState.X != null) window.X = windowState.X.Value;
    if (windowState.Y != null) window.Y = windowState.Y.Value;
    if (windowState.Width != null) window.Width = windowState.Width.Value;
    if (windowState.Height != null) window.Height = windowState.Height.Value;

    window.SizeChanged += (_, _) => { new SavedWindowState(window).Save(); };

    return window;
}

Limitations

Under normal circumstances this should be fine, but there’s a significant use case not accounted for. If the app is closed and then the monitor setup changes, the placement may be partially or completely off screen the next time it starts. In a traditional desktop Windows app (like WPF) it would make sense to do an additional check for the current screen dimensions and limit the size and position to stay within the available space. Unfortunately MAUI doesn’t yet provide sufficient information about screen sizes. The DeviceDisplay.MainDisplayInfo is available but doesn’t cover multi-monitor scenarios, which is one of the main use cases for fixing window placement.

Code Versions

Example code is using .NET 7.0