Recommended EA Structure
ModernUI EAs should read like application code, not library plumbing.
Use one main panel class that owns the UI, state, lifecycle, creation methods, helper methods, user actions, and event routing.
File order
Keep files in this order:
- File header and
#propertyvalues #includestatements- Enums and constants
- Main EA/panel class declaration
- Global instance of the main class
- Constructor/destructor
- EA lifecycle methods:
OnInitEvent,OnDeinitEvent,OnTimerEvent,OnChartEvent - UI creation methods
- Helper/calculation/update methods
- User action methods
OnMuiEvent- Native MQL5 entry points
Class shape
class CTradePanel : public MuiEventSink
{
private:
MuiRoot m_ui;
MuiWindow *m_wWindow;
MuiButtonAction *m_bBuy;
MuiSpinEditHost *m_seRisk;
double m_riskPct;
public:
ENUM_INIT_RETCODE OnInitEvent(void);
void OnDeinitEvent(const int reason);
void OnTimerEvent(void);
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
void CreateGUI(void);
void CreateHeader(MuiContainer *parent);
void CreateInputs(MuiContainer *parent);
void Recalculate(void);
void OnBuy(MuiRoot &root);
virtual void OnMuiEvent(MuiRoot &root,const MuiEventData &event);
};
Naming rules
Use m_ for class member variables.
MuiRoot m_ui;
MuiWindow *m_wWindow;
MuiContainer *m_cHeader;
MuiButtonAction *m_bBuy;
MuiCheckbox *m_cbConfirm;
MuiSpinEditHost *m_seRisk;
MuiSlider *m_sRiskReward;
MuiLabel *m_lRisk;
MuiBadge *m_bdSymbol;
MuiDrawer *m_dSettings;
Common prefixes:
| Prefix | Meaning |
|---|---|
m_w |
Window |
m_c |
Container |
m_b |
Button |
m_cb |
Checkbox |
m_se |
Spin edit |
m_tb |
Text box |
m_ta |
Text area |
m_l |
Label |
m_bd |
Badge |
m_s |
Slider |
m_d |
Drawer |
m_dlg |
Dialog |
m_sb |
Status bar |
Event IDs
Use enums to name UI intent.
enum TRADE_PANEL_ACTION
{
TP_ACTION_OPEN_SETTINGS,
TP_ACTION_BUY,
TP_ACTION_SELL,
TP_ACTION_INPUT_CHANGED,
TP_ACTION_CONFIRM_TRADES
};
Assign IDs directly after creating interactive controls.
m_bBuy=Mui::Button(row,"BUY");
m_bBuy.Id(TP_ACTION_BUY);
MuiLabeledSpinEdit risk=Mui::LabeledSpinEdit(row,"Risk %",0.01,5.0,0.05,1.0,2);
m_seRisk=risk.control;
m_seRisk.Id(TP_ACTION_INPUT_CHANGED);
Event routing
Connect the event sink once after m_ui.Init(...) succeeds.
if(!m_ui.Init(ChartID(),0,"TradePanel_",512,16))
return INIT_FAILED;
m_ui.SetEventSink((MuiEventSink*)GetPointer(this));
Handle UI events in one method.
void CTradePanel::OnMuiEvent(MuiRoot &root,const MuiEventData &event)
{
if(event.EventId()==MUI_EVENT_CLICK)
{
if(event.ControlId()==TP_ACTION_BUY)
OnBuy(root);
else if(event.ControlId()==TP_ACTION_SELL)
OnSell(root);
return;
}
if(event.EventId()==MUI_EVENT_VALUE_CHANGED && event.ControlId()==TP_ACTION_INPUT_CHANGED)
Recalculate();
}
Do not write one custom handler class per control in normal EAs.
UI creation rules
Split UI creation into section methods.
void CreateGUI(void);
void CreateHeader(MuiContainer *parent);
void CreateRiskInputs(MuiContainer *parent);
void CreateExecutionButtons(MuiContainer *parent);
void CreateSettingsDrawer(void);
Each creation method should create one section, store important controls in m_ members, assign IDs to interactive controls, and keep layout settings near creation.
Native entry points
Native MQL5 entry points should stay thin.
CTradePanel Panel;
int OnInit()
{
return Panel.OnInitEvent();
}
void OnDeinit(const int reason)
{
Panel.OnDeinitEvent(reason);
}
void OnTimer()
{
Panel.OnTimerEvent();
}
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
Panel.OnChartEvent(id,lparam,dparam,sparam);
}
Related pages
- Quick Start
- Event Bus
- Controls reference
- Factory helpers (reference) —
LabeledSpinEdit,Section,AppWindow - Inputs — spin edit, checkbox, slider events
- Overlays — drawer and dialog events
- Root and Events
- Build your first panel