January 6th, 2025

How can I tell whether a change to a control was due to the user or due to my program?

In many UI frameworks, controls raise an event when the state of the control changes. The Win32 edit control raises the EN_CHANGED 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 ⟧
}
Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

5 comments

  • alan robinson 2 hours ago

    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...

    Read more
  • LB

    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

  • Tim Dean Johnson

    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.

    • Damir Valiulin

      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:

      int m_UISetProgrammatically = 0;
      
      void SetAuthorProgrammatically(PCWSTR author)
      {
          m_UISetProgrammatically++;
          SetWindowText(m_authorText, author);
          m_UISetProgrammatically--;
      }
      
      void OnAuthorChanged()
      {
          if (m_UISetProgrammatically > 0)
          {
              // Ignore programmatic setting
              return;
          }
      
          [ update the author in the table ]
      }
    • Damir Valiulin

      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.