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