WindowManagement
Enumerate every attached screen and place windows or fullscreen content on a chosen one - the presenter's second-screen slide view, in C#.
@inject Bit.Butil.WindowManagement windowManagementMDN reference
GetScreenDetails prompts for the window-management permission and so
must run inside a user gesture; the resolved details are cached for the page, so later calls do
not re-prompt.
Left/Top in a shared multi-screen space, which is what makes placing a
window on the right monitor possible at all. IsExtended needs no permission, so it
is the right gate for showing a "move to second screen" affordance in the first place.
IsSupported reports whether getScreenDetails exists; IsExtended reports whether more than one screen is attached, without any permission - the same reading Screen.IsExtended gives, offered here so a component already injecting this service needs no second one; QueryPermission reads the window-management state without prompting, and answers in PermissionState like every other permission read in the package.
@inject Bit.Butil.WindowManagement windowManagement
var supported = await windowManagement.IsSupported();
var extended = await windowManagement.IsExtended();
PermissionState state = await windowManagement.QueryPermission();Returns every attached screen and which one this window is on. Null when the API is missing or the prompt was dismissed. Before permission is granted a browser reports only the current screen, so the list length is itself a hint about the permission state.
var details = await windowManagement.GetScreenDetails();
// Null when the API is missing or the prompt was dismissed - there is nothing to enumerate.
if (details is null) return;
foreach (var screen in details.Screens)
{
// screen.Label, screen.Left, screen.Top, screen.AvailWidth, screen.IsPrimary, screen.IsCurrent
}Opens a window positioned on the chosen screen. It is still a popup, so it needs a user gesture and the popup blocker still applies - false means it was blocked, or that the screen index does not exist.
await windowManagement.OpenOnScreen("https://bitplatform.dev", screenIndex: 1, fullSize: true);Takes an element fullscreen on the chosen screen rather than on the one the window is currently on. Needs a user gesture, like any fullscreen request.
<div @ref="_stage">...</div>
await windowManagement.RequestFullscreenOnScreen(_stage, screenIndex: 1);Fires when a monitor is attached or removed, and when this window is dragged onto a different screen. The handler gets the whole snapshot, because either event can invalidate every index the caller is holding.
await using var subscription = await windowManagement.SubscribeChange(details =>
{
// details!.Screens, details.CurrentScreenIndex
});API reference
ValueTask<bool> IsSupported()ValueTask<bool> IsExtended()ValueTask<PermissionState> QueryPermission()ValueTask<ScreenDetails?> GetScreenDetails()ValueTask<bool> OpenOnScreen(string url, int screenIndex, bool fullSize = false, string? features = null)ValueTask<bool> RequestFullscreenOnScreen(ElementReference element, int screenIndex)ValueTask<ButilSubscription> SubscribeChange(Action<ScreenDetails?> handler)ValueTask RemoveChange(Guid id)ValueTask RemoveAllChanges()ValueTask DisposeAsync()void InvokeScreensChange(Guid id, ScreenDetails? details)class ScreenDetails { bool IsExtended; int CurrentScreenIndex; ScreenDetailInfo[] Screens; }class ScreenDetailInfo { string Label; int Left; int Top; int Width; int Height; int AvailLeft; int AvailTop; int AvailWidth; int AvailHeight; int ColorDepth; int PixelDepth; double DevicePixelRatio; bool IsPrimary; bool IsInternal; string? OrientationType; int OrientationAngle; bool IsCurrent; }