3
votes

Quite often when I make VCL programs, I run into a scenario like this:

  • I have a number of components on the form, that the users are allowed to fiddle with. Most commonly a bunch of edit boxes.
  • The contents of these edit boxes need to be verified by the OnChange event when the user types in stuff manually.
  • Somewhere else on the form, there's some component that the user can click on to get some default values loaded into the edit boxes (in TEdit::Text).

Now what I want is that whenever the user is typing something in the TEdit::Text, the OnChange event must process the user input. But when my program is setting the TEdit::Text to a default value, this isn't necessary, because then I know that the value is correct.

Unfortunately, writing code like myedit->Text = "Default"; triggers the OnChange event.

I tend to solve this with what I think is a rather ugly approach: by creating a bool variable is_user_input, which TEdit::OnChange checks. If it is true, the TEdit::Text will get validated, otherwise it will get ignored. But of course, this doesn't prevent the program from launching TEdit::OnChange when it is unnecessary.

Is there a better or cleaner way to achieve this?

Is there a way for OnChange to check who called it? Or I suppose, a way of disabling the OnChange event temporarily would be even better. TEdit::Enabled doesn't seem to affect whether OnChange gets triggered or not.

2
Don't you consider it good that the verification is applied on your default values as well? What if you decide to change a default value at some point, and by mistake set it outside the legal range?barak manos
@barakmanos Normally, yes. But often there is a scenario where multiple components are related to the same set of private variables. So I let all of the components call the same "change" setter function from their individual OnChange. Now suppose I also want to change this variable internally - then I must update all components to reflect the change. And if I do, they automatically trigger their OnChange and launch the "change" function, over and over. So with such a design, I end up with weird, circular dependencies. Plus pointless execution delays.Lundin

2 Answers

6
votes

You could simply unassign the OnChange event handler temporarily:

template <typename T>
void SetControlTextNoChange(T *Control, const String &S)
{
    TNotifyEvent event = Control->OnChange;
    Control->OnChange = NULL;
    try {
        Control->Text = S;
    }
    __finally {
        Control->OnChange = event;
    }
 }

SetControlTextNoChange(myedit, "Default");

Alternatively, RAII is good for this kind of thing:

template <typename T>
class DisableChangeEvent
{
private:
    T *m_control;
    TNotifyEvent m_event;
public:
    DisableChangeEvent(T *control);
    {
        m_control = control;
        m_event = control->OnChange;
        control->OnChange = NULL;
     }

    ~DisableChangeEvent();
    {
        m_control->OnChange = m_event;
    }

    T* operator->() { return m_control; }
};

DisableChangeEvent(myedit)->Text = "Default";
0
votes

Is there a way for OnChange to check who called it?

Usually the events have a parameter Sender, you can check if(Sender ==ButtonSetDefaults) in the event function. Not sure what really happens if the Button triggers an OnChange event and have no way to test it now.

Otherwise I see no problem with a global variable except timing issues, but you can run into those with disabling events as well.