Table of Contents

Class Canvas

Namespace
Icy.UI
Assembly
IcyUI.dll

Represents the root container for UI controls and manages the UI tree, input handling, and rendering.

public class Canvas : ObservableDispatcherObject, INotifyPropertyChanged, INotifyPropertyChanging, IContainerLayout
Inheritance
Canvas
Implements
Inherited Members
Extension Methods

Remarks

The Canvas class serves as the main entry point for the UI system, managing the visual tree, handling input events, and coordinating rendering operations. It maintains a collection of root-level controls and provides services for focus management, input routing, and visual updates.

Constructors

Canvas(IcyConfiguration)

Initializes a new instance of the Canvas class with the specified configuration.

public Canvas(IcyConfiguration config)

Parameters

config IcyConfiguration

The configuration settings for the UI library.

Properties

ActiveDebugTools

Gets the names of the registered debug tools (see Diagnostics) currently active for this canvas - empty by default, so debug visualization costs nothing until something is added here.

public ISet<string> ActiveDebugTools { get; }

Property Value

ISet<string>

Remarks

A name here that matches a registered IDebugOverlay applies to every element unless overridden by its own Debug.Visualization; a name that matches a registered IDebugHudPanel shows that panel in the surface-space HUD. The two catalogs are looked up independently, so one active set can freely mix overlay and panel names.

AllowPerElementDebugOverrides

Gets or sets a value indicating whether a Debug.Visualization override on an individual element is honored even while ActiveDebugTools is empty - letting one element be "drilled into" without turning anything on canvas-wide. false by default, so the fully-disabled path costs one collection-count check and one boolean check per element, and never touches per-element attached-property storage.

public bool AllowPerElementDebugOverrides { get; set; }

Property Value

bool

Background

Gets or sets the background brush of the canvas.

public IBrush? Background { get; set; }

Property Value

IBrush

ContentBounds

Gets the bounds of the content area of the canvas, in surface units (see SurfaceSize).

public Rectangle ContentBounds { get; }

Property Value

Rectangle

EffectiveScale

Gets the scale factor between surface units and physical pixels, as computed by the last RefreshScale() call.

public float EffectiveScale { get; }

Property Value

float

Remarks

EffectiveScale = base × UserScale, where the base factor comes from ScaleMode (or Mode when unset):

Recomputed at the start of every Render(). Raises PropertyChanged when it changes.

FocusedElement

Gets the element currently holding input focus, or null if none does.

public UIElement? FocusedElement { get; }

Property Value

UIElement

IsInputEnabled

Gets or sets a value indicating whether input is enabled for the canvas.

public bool IsInputEnabled { get; set; }

Property Value

bool

IsKeyboardNavigationEnabled

Gets or sets a value indicating whether this canvas acts on keyboard and gamepad navigation: moving focus with FocusNext/FocusPrevious and the arrows, and closing focus scopes with CloseModal.

public bool IsKeyboardNavigationEnabled { get; set; }

Property Value

bool

Remarks

true by default. Tools that take over the keyboard (the design-time editor in Edit mode) and game states such as cutscenes set it to false.

Controls that handle navigation themselves while focused aren't affected: an arrow still reaches the focused control first (a text box moves its caret, a ComboBox its highlight), and only an arrow it doesn't claim would have moved the focus. Clear FocusedElement to silence them.

IsMouseOverGUI

Gets a value indicating whether the mouse is over anything this canvas draws: a root element or an overlay that hit-tests at the mouse position.

public bool IsMouseOverGUI { get; }

Property Value

bool

Remarks

Updated once per Render() while IsInputEnabled is true. A game checks it to decide whether its own scene should ignore the mouse this frame. A full-surface overlay, such as the design-time editor's capture layer, makes it true everywhere.

IsVisible

Gets or sets a value indicating whether the canvas is visible.

public bool IsVisible { get; set; }

Property Value

bool

Offset

Gets or sets the offset of the canvas.

public Vector2 Offset { get; set; }

Property Value

Vector2

Opacity

Gets or sets the opacity of the canvas.

public float Opacity { get; set; }

Property Value

float

Overlays

Gets the ordered list of elements drawn last, every frame, directly in surface space (see SurfaceSize) - unclipped, ignoring this canvas's own pan/rotate/scale transform (see Offset/Rotation/Scale) but scaled by EffectiveScale. The last entry draws on top.

public IReadOnlyList<UIElement> Overlays { get; }

Property Value

IReadOnlyList<UIElement>

Remarks

Deliberately minimal - no z-ordering beyond insertion order, no adorner/anchoring system. An entry positions itself via its own Margin/HorizontalAlignment/ VerticalAlignment against the full SurfaceSize, arranged the same way Icy.Diagnostics.DebugHudHost's own surface-space HUD already is. Overlay elements now participate in HitTest(Point) (checked first, so topmost/last-added win) but not in focus traversal or Add(UIElement)'s root-element list - an overlay (a Preview, a future Dialog's backdrop, a future ComboBox's dropdown) sits visually above everything and is hittable for pointer/touch input, but doesn't participate in keyboard focus or tab order. Use AddOverlay(UIElement)/RemoveOverlay(UIElement) to change it, not direct list mutation - those also wire Canvas.

ReferenceFit

Gets or sets the reference fit policy of this canvas, overriding ReferenceFit. null (the default) inherits the configuration value.

public ReferenceFit? ReferenceFit { get; set; }

Property Value

ReferenceFit?

ReferenceSize

Gets or sets the reference resolution of this canvas, overriding ReferenceSize. null (the default) inherits the configuration value.

public Size? ReferenceSize { get; set; }

Property Value

Size?

Resources

Gets the canvas-wide resource dictionary - the root of the resource chain every attached UIElement can reach, one level above its own outermost ancestor's Resources.

public ResourceDictionary Resources { get; }

Property Value

ResourceDictionary

Remarks

Only consulted for implicit (keyless, per-type) Style resolution, as the final fallback once an element's own ancestor-Parent chain is exhausted - {StaticResource} resolves entirely at markup-load time against the document's own root, so it never reaches this far. Populated with Theme automatically at construction when set (see UseDefaultTheme(IcyConfiguration)); an application can add further shared resources here directly.

Rotation

Gets or sets the rotation angle of the canvas in degrees.

public float Rotation { get; set; }

Property Value

float

Scale

Gets or sets the scale factor of the canvas.

public Vector2 Scale { get; set; }

Property Value

Vector2

ScaleMode

Gets or sets the scale mode of this canvas, overriding Mode. null (the default) inherits the configuration value.

public UIScaleMode? ScaleMode { get; set; }

Property Value

UIScaleMode?

SurfaceSize

Gets the size of the logical drawing surface: the physical viewport divided by EffectiveScale, rounded down to whole units.

public Size SurfaceSize { get; }

Property Value

Size

Remarks

Root elements, overlays and the debug HUD are all laid out against this size.

TransformOrigin

Gets or sets the origin point for transformations applied to the canvas.

public Vector2 TransformOrigin { get; set; }

Property Value

Vector2

Methods

Add(UIElement)

Adds a UI element to the canvas.

public void Add(UIElement element)

Parameters

element UIElement

The UI element to add.

AddOverlay(UIElement)

Adds an element to Overlays and wires its Canvas.

public void AddOverlay(UIElement element)

Parameters

element UIElement

The element to add.

AddOverlay(UIElement, UIElement?)

Adds an element to Overlays on behalf of owner, and wires its Canvas.

public void AddOverlay(UIElement element, UIElement? owner)

Parameters

element UIElement

The element to add, such as a popup's root.

owner UIElement

The element the overlay belongs to, such as the ComboBox whose popup it is, or null for none. See GetOverlayOwner(UIElement).

Remarks

An overlay has no visual parent, so the owner is the only way to tell which part of the UI opened it. Tools that layer themselves over the UI use it: a design-time editor keeps the page's popups beneath itself but its own panels' popups above.

BringOverlayToFront(UIElement)

Moves an element of Overlays to the end of the list, above every other overlay.

public bool BringOverlayToFront(UIElement element)

Parameters

element UIElement

The overlay to raise.

Returns

bool

true if the element is an overlay of this canvas; otherwise, false.

Remarks

Unlike RemoveOverlay(UIElement) followed by AddOverlay(UIElement, UIElement?), the element stays attached and keeps its owner, so its subtree isn't detached and reattached: open popups stay open and the focus stays where it is.

CloseFocusScope(UIElement)

Restores whatever was focused before scope was entered (see Focus(UIElement?)), as if the user had pressed cancel/back while focus was inside it.

public void CloseFocusScope(UIElement scope)

Parameters

scope UIElement

The IsFocusScope element to close, e.g. a Window being dismissed via Window.Close(). A no-op if nothing was ever focused before entering this scope.

Remarks

This is the same restore logic CloseModal triggers (e.g. pressing Escape/a cancel button) - exposed so API-driven scope closes (like a Window's Close() method) restore focus consistently too, not just input-driven ones.

FindEnclosingFocusScope(UIElement?)

Finds the nearest enclosing IsFocusScope ancestor of the specified element (walking up through Parent), or null if none of its ancestors (or itself) are a focus scope.

public static UIElement? FindEnclosingFocusScope(UIElement? element)

Parameters

element UIElement

The element to find the enclosing focus scope of.

Returns

UIElement

The nearest enclosing focus scope, or null if there isn't one.

Focus(UIElement?)

Moves input focus to the specified element.

public void Focus(UIElement? element)

Parameters

element UIElement

The element to focus, or null to clear focus. Ignored (no-op) if the element isn't IsFocusable or isn't IsVisible.

GetOverlayOwner(UIElement)

Gets the element an overlay was added on behalf of.

public UIElement? GetOverlayOwner(UIElement overlay)

Parameters

overlay UIElement

An element of Overlays.

Returns

UIElement

The owner passed to AddOverlay(UIElement, UIElement?), or null when there was none or overlay isn't an overlay of this canvas.

HitTest(Point)

Determines which element - if any - is under the specified point. Checks Overlays first (topmost/last-added wins), falling back to Icy.UI.Canvas.rootElements - see Overlays's own remarks for why overlay content participates here (unlike focus traversal, which it still doesn't).

public UIElement? HitTest(Point screenPoint)

Parameters

screenPoint Point

A point in screen/window space (the same space pointer/touch positions arrive in).

Returns

UIElement

The topmost hit-testable element under the point, or null if none is.

HitTest(Point, Func<UIElement, bool>?)

Determines which element is under the specified point, testing only the overlays includeOverlay accepts, then the root elements.

public UIElement? HitTest(Point screenPoint, Func<UIElement, bool>? includeOverlay)

Parameters

screenPoint Point

A point in screen/window space (the same space pointer/touch positions arrive in).

includeOverlay Func<UIElement, bool>

Decides, per entry of Overlays, whether it takes part; null tests every overlay. Tools use it to look past their own overlays while still seeing the page's popups.

Returns

UIElement

The topmost hit-testable element under the point, or null if none is.

InvalidateTransform()

Invalidates the current transformation, marking it as needing an update.

public void InvalidateTransform()

LoadMarkup(string, object?)

Loads a markup file, sets its data context, and adds it to the canvas.

public UIElement LoadMarkup(string path, object? dataContext = null)

Parameters

path string

The markup file's path, relative to the configuration's default asset context (the application folder unless changed). Copy the file to the output folder.

dataContext object

The object the markup's {Binding} expressions read from, or null to leave DataContext unset.

Returns

UIElement

The loaded root element, already added to this canvas.

Exceptions

FileNotFoundException

The file doesn't exist.

InvalidOperationException

Markup support isn't enabled. The engine integrations enable it by default; otherwise call AddMarkupSupport(IConfigurationBuilder).

MarkupException

The markup is invalid.

MoveFocus(bool)

Moves focus to the next or previous focusable element, wrapping around, and respecting the focused element's enclosing IsFocusScope boundary (if any) - focus won't move outside it.

public void MoveFocus(bool forward)

Parameters

forward bool

true to move to the next element in traversal order; false for the previous one.

Remarks

The newly focused element is brought into view (see BringIntoView()).

MoveFocus(Vector2)

Moves focus to the nearest focusable element in direction, the way arrow keys, the D-pad and the left stick do when the focused element doesn't use the press.

public bool MoveFocus(Vector2 direction)

Parameters

direction Vector2

Any direction, UI space (+Y down). It's snapped to its dominant axis.

Returns

bool

true when focus moved.

Remarks

  • Candidates are the focusable, visible, enabled elements of the focused element's focus scope (see IsFocusScope), or of the whole canvas without one.
  • Elements clipped away are skipped, except content scrolled out of a ScrollViewer that is itself on screen.
  • Candidates straight ahead (overlapping on the cross axis) win over diagonal ones; then the nearest wins.
  • Pressing the opposite direction right after a move returns to where it started.
  • With nothing focused, the first element in Tab order is focused. There is no wrap-around.

The newly focused element is brought into view (see BringIntoView()).

OnPropertyChanging(PropertyChangingEventArgs)

Raises the PropertyChanging event.

protected override void OnPropertyChanging(PropertyChangingEventArgs e)

Parameters

e PropertyChangingEventArgs

The input PropertyChangingEventArgs instance.

OnResize()

Invalidates the arrangement of every root element and overlay, so the next Render() lays them out against the current SurfaceSize.

public void OnResize()

Remarks

Render() calls this itself whenever the render context's ViewportSize changes, so a window resize needs no extra wiring. Call it directly only to force a re-layout without a size change.

RefreshScale()

Recomputes EffectiveScale from the current configuration, display scale, viewport and overrides, and re-arranges all content if it changed.

public void RefreshScale()

Remarks

Called automatically at the start of every Render() and when an override changes. The canvas deliberately doesn't subscribe to configuration or render-context events: it has no disposal point, and a subscription would keep every discarded canvas alive.

Remove(UIElement)

Removes a UI element from the canvas.

public bool Remove(UIElement element)

Parameters

element UIElement

The UI element to remove.

Returns

bool

true if the element was successfully removed; otherwise, false.

RemoveOverlay(UIElement)

Removes an element from Overlays and clears its Canvas.

public bool RemoveOverlay(UIElement element)

Parameters

element UIElement

The element to remove.

Returns

bool

true if the element was found and removed; otherwise, false.

Render()

Renders the canvas and its child elements.

public void Render()

Remarks

This method updates the input state, layout, and visual representation of the canvas. It should be called every frame to ensure the UI is rendered correctly.