XControls: Execution Explained
If you still haven't read the introduction to XControls, here it is!
Now that you know what a XControl really is, it's time to discover how it runs under the hood.
Develop, Debug, Repeat
Do not forget: your XControl is loaded into the caller's memory even if the latter is in edit mode! So you cannot strictly use or test your XControl while developing it. Fortunately, LabVIEW grants us a way to "easily" switch between the development and execution modes through a context menu option on the .xctl file in the library/project window.
When you unlock the XControl to edit it, the calling VI(s) break: internally, it's just like you passed an invalid VI reference to the subpanel hosting the façade VI. And once you apply your changes after fixing/developing something in the XControl, its instance(s) are reinitialized.
Unfortunately for our developer experience, there's (always?) been a memory leak in that process, which explains why LabVIEW becomes super slow after switching modes a few times. Do not hesitate to restart your LabVIEW process once in a while when developing a XControl, this often removes fancy behaviors that can be caused by this very memory leak.
Events -- in the Alphabetical Order!
The façade VI event structure not only responds to user events such as clicks but is also triggered when some special "XControl events" happen in the calling VI, which names are Data Change, Direction Change, Display State Change & Execution State Change. Depending on the actions happening on the XControl, one or more of these events will be triggered, and here's a non-exhaustive mapping of what triggers which events:
As you can see, there's a trick to remember the order of executed event cases when a new XControl instance is created, they are executed in the lexical order, at least on an english version (if someone with a localized LabVIEW version wants to check that'd be great !).
The timeout event case is also triggered after the events on the façade VI have executed. The zero-timeout value is mandatory, do not alter it, or any interaction with your XControl will slow down the UI thread.
XControl & Threading
You'll soon find that XControls are great for a number of things, but (and it's a big but) they're not designed to run heavy tasks. In fact, most of the XControl code you will write will execute in the UI thread (XControl property/invoke nodes, the façade VI...) which ends up slowing down the UI thread. For instance, everytime you write data to the XControl terminal, the Data Change is raised in the façade VI in order to handle how the data updates in the XControl. If anything is blocking an event case in the façade, the UI thread is getting stuck. Here's a simple demo, where I'm spamming the "Click me!" button but the UI cannot respond until the façade VI has finished executing (after counting 3 seconds):
Another common issue is to produce too many events for the XControl. As events are queued, your XControl may start accumulating delays to process these events. There are 2 ways to avoid this behavior: keeping the code in the façade VI as simple and as fast as possible, or prevent your XControl from queueing events from the calling VI: only one occurrence of each event is then permitted in the façade VI event queue. This can be achieved by hacking into the .xctl file (opened in a Notepad) where you must add the following line:
<Property Name="NI.XCtl.OptimizeDataUpdate" Type="Bool">true</Property>
Keep in mind this has its flaws and that it may result in unexpected behaviors. However it can be a life-saver, for instance when dealing with charts.
In some cases where the XControl is made of multiple controls that all need to update in the same event case, it's also a good practice to use the Defer Panel Updates node on the façade VI before jumping on the previous hack.
Finally, as implicitly advised above, because the façade VI execution must end as soon as possible, you must not add any helper loop (or any loop at all!) to the façade VI. Delegating extra processing inside a XControl is an advanced technique (and will be discussed later, when you're an experimented XControl developer :))
Data from/to Façade
Have you ever tried to change the name of a default terminal on the Façade VI (like, Data in)? It instantly breaks your XControl. Same, the connector pane is fixed and cannot be changed. All these oddities are clues that these terminals are needed by some internal LabVIEW mock-up code to keep track of the state and the value of the XControl instances (stored in the calling VI memory).
Every time the façade runs, the XControl data and/or state may be changed in some events. When this happens, the Action cluster at the bottom of the façade must be maintained accordingly (set the State Changed? or Data Changed? to True):
When you do so, you're telling the mock-up code to store the new value/state, so that it can be propagated and reused by the next execution of the façade (think of it as a shift register outside the façade VI), or by the execution of custom property/invoke nodes, as those also need to access the display state. (Not) Maintaining these Action flags is one of the most common errors when developing your first XControls.
Additionally, you can set a name for the Action to describe what is happening to the data or state, and this name will be available in the Edit-> Undo/Redo menu of the calling VI. Hitting the Undo (Ctrl-Z) reverts the state of the XControl to its previous value, BUT you will have to handle the cosmetic changes of the undo transaction by yourself. Fortunately, as mentioned in the table from earlier, the Undo/Redo triggers the Display State Change event, so you can gracefully handle the cosmetic changes there.
Disappointingly, we can't get the undo/redo action name in the Display State Change, which may make the undo difficult to handle as the XControl gains complexity...
Next time: Develop a proper (simple) XControl!