In many UI frameworks, controls raise an event when the state of the control changes. The Win32 edit control raises the EN_
notification, for example. This is sometimes desirable. For example, maybe you want the “Author” field to match the current Book ID.
void OnBookIdChanged() { auto id = ReadBookId(); auto author = LookUpAuthor(id); SetAuthorText(author); }
Even if the book ID is set programmatically, you still want the author to be updated.
But often, you want your program to respond to changes in a control if they were initiated by the user, but not if they were triggered by the program itself. For example, you want to update a table with the new author as the user types it, but you don’t want to trigger an update when the code initializes the author field. How can you tell whether a change notification was due to the user or due to your program?
The simple solution is to set a flag when you are changing the value from your program, and then check that flag in the notification to decide whether to respond to it.
bool m_authorSetProgrammatically = false; void SetAuthorProgrammatically(PCWSTR author) { m_authorSetProgrammatically = true; SetWindowText(m_authorText, author); m_authorSetProgrammatically = false; } void OnAuthorChanged() { if (m_authorSetProgrammatically) { // Ignore programmatic setting return; } ⟦ update the author in the table ⟧ }
This also means that if an external component programmatically changes the author, your code will treat it as if the user had changed it. This is a good thing, because that external component might be an assistive technology that is updating the edit box on behalf of the user.
Note that if there are multiple “author changed” handlers, and one of the other ones triggers additional changes that in turn change the author again (say, by applying an autocorrection to the new author), the second change will still be ignored because m_authorÂSetÂProgrammatically
is still true
. To avoid that, you could reset the flag on the first notification.
void OnAuthorChanged()
{
if (std::exchange(m_authorSetProgrammatically, false))
{
// Ignore programmatic setting
return;
}
⟦ update the author in the table ⟧
}
IMHO this is a design flaw - some UI libraries pass a Boolean indicating if the change notification was due to user interaction or not, which I *think* the library should be able to detect reliably. Of course nothing beats having your own private flag as in the code above but it's cumbersome given how often-needed it is. Most of the time I run into this when initializing a UI object, which, at least...
I’d probably use a simple RAII wrapper or gsl::finally for this so that the flag doesn’t get stuck on in the case of an exception
I don’t think setting a boolean is implicitly atomic in C++, so I’m guessing you need to be mindful of which thread is doing what.
I’m pretty used to this pattern when dealing with UserForms in VBA. It’s pretty much required for forms of medium complexity.
A simple boolean flag might not be sufficient when you need to update or respond to an update in cascading fashion. You may end in loop. A better flag would be a counter that you increment and decrement:
Only UI thread should modify UI state. So the flag should be stored and updated on the UI thread as well. If you want to do it from multiple threads, then you’d need a bit more complicated setup.