How to Build an APIC User Interface for Designers

Build OGI screens in AXAML with an extremely low need to coordinate with the C++ developer.

This guide is for graphical/UI designers building OGI screens - the operator-facing windows and controls that talk to a running CoreEngine. If you're looking for the C++ side of OGI (writing the event handler that reacts to a click), see How to Build the OGI Operator Interface instead - this page is about your side of the work and, most importantly, how little the two sides need to talk to each other to get there.

1. Your Job

You design the AXAML screen: layout, controls, visual states, and how each control's value or state is driven by simulation data. Three things matter for every control you add:

See also: How to Build the OGI Operator Interface for the full AXAML syntax, the Tag format, and worked examples for buttons, progress bars, and text boxes.

2. The Handoff: Extremely Low Interaction Needed

There is an extremely low need for back-and-forth between you and the C++ developer. You don't need to tell them what you named a control, send them a list of IDs, or agree on a naming scheme in a meeting. Once your AXAML is done and you run the OGI application, it automatically generates two files that hand your control names to the C++ side mechanically - your AXAML file is the single source of truth, and the generation step is the entire handoff.

3. The Bridge Between the UI App and the CoreEngine App

Running the OGI application against your AXAML produces exactly two files that bridge your UI app to the CoreEngine app for the C++ developer:

FileWhat it gives the C++ developer
OGIControls.hOne integer ID and one dotted path constant per control you named (e.g. SENDBUTTON / SENDBUTTON_CTRL for a control you called x:Name="sendButton"), plus a name lookup table. This is how the C++ developer refers to your control - by the name you gave it in AXAML.
OGIAppSetup.cppMaps each control to its concrete C++ wrapper type based on what kind of control you used (a Button becomes COGIMultiStateButton, for example) and registers them with the OGI subsystem.
Both files are regenerated every time the OGI application runs against your AXAML - nobody hand-edits them. Rename a control in AXAML, rerun the generator, and the C++ side sees the new name the next time it builds - no message needed.

4. Worked Example

A control declared as <Button x:Name="sendButton"> in the AXAML shows up in the generated header as #define SENDBUTTON 10 / #define SENDBUTTON_CTRL "OGIlWindow.OGIRoot.sendButton", and in the generated setup file as a COGIMultiStateButton creation entry - all produced from the AXAML alone.

5. Full AXAML Reference

For the complete Tag syntax (notifications, control types, states, state machines) and more control examples, continue with How to Build the OGI Operator Interface and the OGI reference doc.

6. Reusable Widgets Available to You

The SimWidgets library ships a set of ready-made AvaloniaUI controls you can drop straight into your AXAML instead of building common patterns from scratch. Reference them with xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets".

Every widget below is embedded the same way - declare it with an x:Name:

<controls:WidgetName x:Name="MyCtrl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

What differs is the C# needed after InitializeComponent(). Real examples for each, taken from actual usage in the codebase:

DIViewerCtrl

The data-injection grid: LRU / Block / Type / Element / Value columns per row, with inject (Raw/Eng) toggles and an edit mode. This is the same widget the standalone DIPanel application is built around - if you want DIPanel-like data injection inside your own UI app, this is the control to embed.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:DIViewerCtrl x:Name="DIViewerCtrl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</UserControl>
DIViewerCtrl.SetElementMenu(
    (element, title) => CreateGraph(new List<CElementsView> { element }, title),  // Graph
    (element) => CreateImage(new List<CElementsView> { element })               // Image
);
DIViewerCtrl.Run();
DIViewerCtrl.InsertDIElementsList(newDIElements);
DIViewerCtrl.ShowFindDlg();
DIViewerCtrl.ClearDIPanel();

RawViewerCtrl

Hex/decimal raw-bytes viewer for a block, with a bit-width selector, Find, and "View in Image".

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:RawViewerCtrl x:Name="RawViewerCtrl"/>
</UserControl>
RawViewerCtrl.Init(viewerLRUs);
RawViewerCtrl.UpdateRawViewer(buffer, rawStepSize);
RawViewerCtrl.SetRawStepSize(rawStepSize);
RawViewerCtrl.Clear();

ImageCtrl

Renders a block as an image - pick RAW or Image format and a pixel/bit-depth type, with Save.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:ImageCtrl x:Name="ImageCtrl"/>
</UserControl>
ImageCtrl.SetShememPtrs(
    element.engShmemElementPos,
    element.element.bytes.Value * arraySize,
    element.name);

GraphView

A live-plotting canvas with reset, auto-scale, and buffer-size/sampling-interval controls.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:GraphView x:Name="GraphViewCtrl"/>
</UserControl>
GraphViewCtrl.Init();
GraphViewCtrl.SetShememPtrs(DIElements, "X", "Time", DIViewerCtrl.LRUs.simRefreshRateMS);
GraphViewCtrl.Run();
// ...
GraphViewCtrl.Close();

RecordingViewerCtrl

Full recording playback UI - LRU list, time graph, scrub slider, export/import - built by combining DIViewerCtrl and RawViewerCtrl.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:RecordingViewerCtrl x:Name="RecordingViewerCtrl"/>
</UserControl>
RecordingViewerCtrl.Init("MyProjectName", true);
bool ok = RecordingViewerCtrl.Load(recordingFolder);

BlockInfoWindow

A block inspector popup: enum names/values and stream/block properties, with a "Send Once" trigger button. Unlike the others, this one is constructed directly rather than declared in AXAML:

var blockInfoWindow = new BlockInfoWindow(appStreams);

ReportCtrl

A SimReport log viewer with Info/Warnings/Errors/States/Tests filter checkboxes, Find, and Save. Forward resize events so it can re-lay its columns:

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:ReportCtrl x:Name="ReportCtrl"/>
</UserControl>
ReportCtrl.Window_Resized(new Size(finalSize.Width, finalSize.Height));

ReportProfileCtrl

A tabular profiler report viewer with Reset.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:ReportProfileCtrl x:Name="ReportProfileCtrl"/>
</UserControl>
ReportProfileCtrl.Init(null);
ReportProfileCtrl.Clear();
ReportProfileCtrl.Window_Resized(new Size(width, height));
ReportProfileCtrl.Close();

ReportStateCtrl

A tabbed state-machine status viewer.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:ReportStateCtrl x:Name="ReportStateCtrl"/>
</UserControl>
ReportStateCtrl.Init("APIC center", "");
ReportStateCtrl.Window_Resized(new Size(width, height));

ReportStatusCtrl

A two-pane Models/Drivers status grid (the Core States view).

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:ReportStatusCtrl x:Name="ReportStatusCtrl"/>
</UserControl>
ReportStatusCtrl.Init();
ReportStatusCtrl.Window_Resized(new Size(width, height));

CoreEngineFlowState

A compact engine control panel - Reset / Init / Play-Pause / Step buttons plus a running/stopped/idle indicator. Its buttons already wire their own click handlers internally (OnReset, OnInit, OnPlayPause, OnStep) - no extra C# is needed beyond declaring it.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:CoreEngineFlowState x:Name="CoreEngineFlowStateCtrl"/>
</UserControl>

SimEngPicker

A dropdown for selecting (and killing) a running CoreEngine instance - the "SimEng picker" referenced elsewhere in these docs. Read the current selection from its property:

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:SimEngPicker x:Name="SimEngPicker"/>
</UserControl>
var selectedSimEngID = SimEngPicker.selectedSimEngID;

TestsPlanCtrl

A test-plan viewer for building/viewing Tests CSV flows, embedding a DIViewerCtrl.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <StackPanel>
        <StackPanel x:Name="TestingFlowStateContainer"/>
        <controls:TestsPlanCtrl x:Name="TestsPlanCtrl"/>
    </StackPanel>
</UserControl>
TestsPlanCtrl.Init(TestingFlowStateContainer);
TestsPlanCtrl.OnSimEngChanged(selectedSimEngID);
TestsPlanCtrl.Close();

PickSingleElmsCtrl

A text box with an autocomplete flyout for picking a single ICD element by its LRU.Block.Element name.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:PickSingleElmsCtrl x:Name="PickElement"/>
</UserControl>
PickElement.Init(lrus, SimWidgets.PickSingleElmsCtrl.EPickState.eElement,
    (string elementName, LRUS.Element element) => { /* handle the pick */ });

MessageBox

A modal dialog window with message text and a dynamic button row, for OK/Cancel-style prompts. Shown programmatically rather than declared as a child control:

var msgBox = SimWidgets.MessageBoxWindow.Create();
var result = await SimWidgets.MessageBoxWindow.Show(
    parentWindow, "Failed to Start Application",
    "Could not load the LRU JSON file.",
    SimWidgets.MessageBoxWindow.MessageBoxButtons.Ok, msgBox);

JsonEditorCtrl

A tree-view JSON hierarchy editor with add/remove nodes and a dynamic properties panel. Self-contained - no external setup call was found beyond declaring it.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:JsonEditorCtrl x:Name="JsonEditorCtrl"/>
</UserControl>

NCalcCtrl

A formula/expression editor. Type an expression directly - rnd(0:1), sin(t)*a, cos(x)+offset - no external setup call was found beyond declaring it.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:NCalcCtrl x:Name="FormulaEditor"/>
</UserControl>

IpAddressBox

A segmented four-part IP address text input. Self-contained - no external setup call was found beyond declaring it.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:IpAddressBox x:Name="RemoteIpCtrl"/>
</UserControl>

ThemeSetupCtrl

An app theme (light/dark) selector settings panel. It binds directly to a theme list via its own DataContext - no external setup call was found beyond declaring it.

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:controls="clr-namespace:SimWidgets;assembly=SimWidgets"
             x:Class="YourNamespace.YourView">
    <controls:ThemeSetupCtrl x:Name="ThemeSetupCtrl"/>
</UserControl>

Splash

The application splash/loading screen. Unlike the others, this one is a Window, not a control you embed in AXAML - construct and show it directly:

new SimWidgets.Splash().Show();
For all five widgets on this page: if you need to read or set a value programmatically and no method is shown above, check the widget's own code-behind for the specific property or method name rather than assuming one of the earlier patterns applies - these did not have a verified external call site in the reference codebase at the time this was written.