UI application development
APIC OGI uses AvaloniaUI platform for it cross platform user interface applications, the UI applications communicate with the Core-Engine application via events and the LRUs share memory infrastructure.
.csproj, a standard Avalonia Program.cs/App.axaml
entry point, and a MainWindow.axaml/.axaml.cs pair) that connects
to a running CoreEngine externally - it is not compiled into CoreEngine itself. Start from
an existing OGI project as a template rather than generating this scaffolding from
scratch. There is no ICoreEngineApi interface and no C# class ever derives
from CModelBase - Models are exclusively C++ and are a completely separate
context from the GUI application (see the CoreEngine framework development guide).
axaml File anatomy
Example of AvaloniaUI control snippet
<ProgressBar x:Name="ControlName"
Minimum="{sim:SimBinding UI.OGI.ProgressMin}"
Maximum="{sim:SimBinding UI.OGI.ProgressMax}"
Value="{sim:SimBinding UI.OGI.ProgressPos}"
Height="10" Margin="0 10 0 0">
<ProgressBar.Tag>
[Tag Data goes here (See below)]
</ProgressBar.Tag>
</ProgressBar>
Key Features
-
Control Declaration
<ProgressBar x:Name="ControlName"Every control must have anx:NameThis gives the control an identifier so:
- It becomes accessible in code-behind
- The UI parser includes the control
- Other parts of the UI can reference it
Without x:Name, the parser may skip it in your custom processing. -
Properties Bound with SimBinding
Minimum="{sim:SimBinding UI.OGI.ProgressMin}" Maximum="{sim:SimBinding UI.OGI.ProgressMax}" Value="{sim:SimBinding UI.OGI.ProgressPos}"This works similarly to standard bindings but gets the value from your Core-Engine App.
The binding updates automatically whenever the sim element value changes.
Syntex:
[Control property name] = "{sim:SimBinding [LRU.Block.Element]}"Note: The axaml file header must include the following declaration
xmlns:sim="clr-namespace:DynamicSimBinding;assembly=OGIDI" -
The Tag Property:
<ProgressBar.Tag> Tag Data goes here (See below) </ProgressBar.Tag>The Tag value stores metadata for the control binding with the Core-Engine App
See Control Tag below for detailed explanation.
x:Name assigned. Without it, the UI parser will ignore and omit that control.
Example of AvaloniaUI control TAG
TAG data:
[notify{mouse};]
type{button | toggle | tab | check | imagebutton | stateImage | image};
[group{all buttons name participate in the group including this one};] //Only for type=check
states{
1=.\Bitmaps\btn-new_enable.bmp, 1(hot)=.\Bitmaps\btn-new_hot.bmp,
2=.\Bitmaps\btn-new_selected.bmp, 2(hot)=.\Bitmaps\btn-new_selectedhot.bmp,
3=.\Bitmaps\btn-new_disable.bmp, 3(hot)=.\Bitmaps\btn-new_disableHot.bmp,
4="Text", 4(hot)="TextHot",
5=#controlName, 4(hot)=#controlName,
enable=1, click=2, notclick=1[checkbox only], disable=3, MouseLeftButtonDown=2}; //named state
[defaultState={any state above or empty to hide the control};] //the default state and visibility on startup
[
statesMachine{MouseLeftButtonDown:1=2,2=3,3=1|
MouseRightButtonDown:1=2,2=3,3=1 |
MouseEnter: ... |
MouseLeave: ... |
MouseLeftButtonUp: ... |
MouseRightButtonUp: ...
};
]
TAG Data Specification
Defines how the control changes state and handle events.
1. Notifications
[notify{mouse};]
Enables mouse-based notifications for this control.
Notifications are send to the Core-Engine App.
2. Control Type
type{button | toggle | tab | check | imagebutton | stateImage | image | progressbar};
Defines how the control behaves visually and functionally.
3. Group (checkbox only)
[group{all buttons name participate in the group including this one};]
Groups check-type controls together.
4. States
states{
1 = .\Bitmaps\btn-new_enable.bmp,
1(hot) = .\Bitmaps\btn-new_hot.bmp,
2 = .\Bitmaps\btn-new_selected.bmp,
2(hot) = .\Bitmaps\btn-new_selectedhot.bmp,
3 = .\Bitmaps\btn-new_disable.bmp,
3(hot) = .\Bitmaps\btn-new_disableHot.bmp,
4 = "Text",
4(hot) = "TextHot",
5 = #controlName,
5(hot) = #controlName,
enable = 1,
click = 2,
notclick = 1,
disable = 3,
MouseLeftButtonDown = 2
};
Defines images, text, or references to use for each visual state.
5. Default State
[defaultState={stateName or empty}];
Sets the initial state or hides the control if empty.
6. State Machine
statesMachine{
MouseLeftButtonDown: 1=2, 2=3, 3=1 |
MouseRightButtonDown: 1=2, 2=3, 3=1 |
MouseEnter: ... |
MouseLeave: ... |
MouseLeftButtonUp: ... |
MouseRightButtonUp: ...
};
States defined by content assigned to numeric value which represents the state ID
States content can be image or text.
C++ Code behind anatomy
Auto-generated files vs. what you write
Running the OGI application against your AXAML regenerates exactly 2 files - do not hand-edit these, your changes are overwritten on the next run:
-
OGIControls.h
One integer ID and one dotted path constant per named control (e.g.
SENDBUTTON/SENDBUTTON_CTRLforx:Name="sendButton"), plus a name lookup table. -
OGIAppSetup.cpp
A creation array mapping each control ID to its concrete C++ wrapper type (e.g. a
ButtonbecomesCOGIMultiStateButton), and the startup code that registers them and initializes the OGI subsystem.
You write one more pair of files yourself, not regenerated, named after your project (e.g. OGIROMAH.h / OGIROMAH.cpp for a project called ROMAH):
COGICmdTarget, not the Model base class
(CModelBase) used elsewhere in CoreEngine development - OGI event handling is
its own, separate context.
-
OGI{ProjectName}.h
Declares your handler class and its message map:
class CROMAH : public COGICmdTarget { public: CROMAH() : COGICmdTarget() {} ~CROMAH() {} DECLARE_OGI_MESSAGE_MAP(); void OnButtonClick(); }; -
OGI{ProjectName}.cpp
Provide the interface for the OGI events.
the events handlers declare as follow:
////////////////////////// //OGI Events handler BEGIN_OGI_MESSAGE_MAP(CROMAH, COGICmdTarget) ON_OGICOMMAND(SENDBUTTON, &CROMAH::OnButtonClick) END_OGI_MESSAGE_MAP() IMPLEMENT_OGI_MESSAGE_MAP(CROMAH);And the event handler method declare as follow:void CROMAH::OnButtonClick() { COGIMultiStateButton* pObj = (COGIMultiStateButton*)CUIControlsFactory::FindControl(SENDBUTTON); pObj->SetState(3); }Note: You must callOGIInit()once, from one of the Models'OnInit()method - it constructs your handler class and calls itsInit().