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
configIcyConfigurationThe 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
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
Background
Gets or sets the background brush of the canvas.
public IBrush? Background { get; set; }
Property Value
ContentBounds
Gets the bounds of the content area of the canvas, in surface units (see SurfaceSize).
public Rectangle ContentBounds { get; }
Property Value
EffectiveScale
Gets the scale factor between surface units and physical pixels, as computed by the last RefreshScale() call.
public float EffectiveScale { get; }
Property Value
Remarks
EffectiveScale = base × UserScale, where the base factor comes from
ScaleMode (or Mode when unset):
- None:
1. - Dpi: DisplayScale.
- ReferenceResolution: the viewport-to-ReferenceSize ratio picked by ReferenceFit.
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
IsInputEnabled
Gets or sets a value indicating whether input is enabled for the canvas.
public bool IsInputEnabled { get; set; }
Property Value
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
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
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
Offset
Gets or sets the offset of the canvas.
public Vector2 Offset { get; set; }
Property Value
Opacity
Gets or sets the opacity of the canvas.
public float Opacity { get; set; }
Property Value
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
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
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
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
Scale
Gets or sets the scale factor of the canvas.
public Vector2 Scale { get; set; }
Property Value
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
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
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
Methods
Add(UIElement)
Adds a UI element to the canvas.
public void Add(UIElement element)
Parameters
elementUIElementThe UI element to add.
AddOverlay(UIElement)
public void AddOverlay(UIElement element)
Parameters
elementUIElementThe element to add.
AddOverlay(UIElement, UIElement?)
public void AddOverlay(UIElement element, UIElement? owner)
Parameters
elementUIElementThe element to add, such as a popup's root.
ownerUIElementThe element the overlay belongs to, such as the
ComboBoxwhose 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
elementUIElementThe overlay to raise.
Returns
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
scopeUIElementThe IsFocusScope element to close, e.g. a
Windowbeing dismissed viaWindow.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
elementUIElementThe element to find the enclosing focus scope of.
Returns
Focus(UIElement?)
Moves input focus to the specified element.
public void Focus(UIElement? element)
Parameters
elementUIElementThe 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
Returns
- UIElement
The owner passed to AddOverlay(UIElement, UIElement?), or null when there was none or
overlayisn'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
screenPointPointA point in screen/window space (the same space pointer/touch positions arrive in).
Returns
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
screenPointPointA point in screen/window space (the same space pointer/touch positions arrive in).
includeOverlayFunc<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
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
pathstringThe markup file's path, relative to the configuration's default asset context (the application folder unless changed). Copy the file to the output folder.
dataContextobjectThe 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
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
directionVector2Any direction, UI space (+Y down). It's snapped to its dominant axis.
Returns
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
ePropertyChangingEventArgsThe 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
elementUIElementThe UI element to remove.
Returns
- bool
trueif the element was successfully removed; otherwise,false.
RemoveOverlay(UIElement)
public bool RemoveOverlay(UIElement element)
Parameters
elementUIElementThe element to remove.
Returns
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.