【问题标题】:Proper way to change contents of a VCL component更改 VCL 组件内容的正确方法
【发布时间】:2014-03-07 13:46:12
【问题描述】:

我在做 VCL 程序的时候经常会遇到这样的场景:

  • 我在表单上有许多组件,允许用户摆弄。最常见的是一堆编辑框。
  • 当用户手动输入内容时,这些编辑框的内容需要通过 OnChange 事件进行验证。
  • 在表单的其他位置,用户可以单击某些组件以将一些默认值加载到编辑框(在 TEdit::Text 中)。

现在我想要的是,每当用户在 TEdit::Text 中键入内容时,OnChange 事件必须处理用户输入。但是当我的程序将 TEdit::Text 设置为默认值时,这不是必需的,因为我知道该值是正确的。

不幸的是,编写myedit->Text = "Default"; 之类的代码会触发 OnChange 事件。

我倾向于用我认为相当丑陋的方法来解决这个问题:通过创建一个布尔变量is_user_inputTEdit::OnChange 会检查它。如果为真,则 TEdit::Text 将得到验证,否则将被忽略。但是当然,这并不妨碍程序在不必要的时候启动TEdit::OnChange

有没有更好或更清洁的方法来实现这一点?

OnChange 有没有办法检查谁调用了它?或者我想,暂时禁用 OnChange 事件的方法会更好。 TEdit::Enabled 似乎不会影响 OnChange 是否被触发。

【问题讨论】:

  • 您不认为验证也适用于您的默认值是件好事吗?如果您决定在某个时候更改默认值,并错误地将其设置在合法范围之外怎么办?
  • @barakmanos 通常,是的。但通常存在多个组件与同一组私有变量相关的情况。所以我让所有组件从它们各自的 OnChange 中调用相同的“更改”设置器函数。现在假设我还想在内部更改此变量 - 然后我必须更新所有组件以反映更改。如果我这样做,他们会一遍又一遍地自动触发 OnChange 并启动“更改”功能。所以有了这样的设计,我最终会得到奇怪的循环依赖。加上毫无意义的执行延迟。

标签: c++ c++builder vcl


【解决方案1】:

您可以简单地暂时取消分配 OnChange 事件处理程序:

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");

另外,RAII 也适合这种事情:

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";

【讨论】:

  • 谢谢,这是好东西。我假设 RAII 版本将被称为 DisasbleChangeEvent&lt;TEdit&gt;(myedit)-&gt;Text = "Default";?
  • 另外,__finally 是 C++ Builder 扩展还是 C++11 之类的?
  • 我省略了&lt;TEdit&gt; 部分,因为编译器应该能够根据传递的参数推断其值,但是是的,您也可以显式指定它。而__finally 是一个 C++Builder 编译器扩展。
【解决方案2】:

OnChange 有没有办法检查谁调用了它?

通常事件有一个参数Sender,你可以在事件函数中检查if(Sender ==ButtonSetDefaults)。如果 Button 触发 OnChange 事件并且现在无法对其进行测试,不确定会发生什么。

否则我认为全局变量除了时间问题之外没有问题,但你也可以遇到那些禁用事件的问题。

【讨论】:

  • 是的,但是当我在程序的某处输入myedit-&gt;Text = "hello"; 并触发 OnChange 时,谁将成为发送者?那么发件人不总是它的父表单吗?我如何使用它来知道是用户还是程序触发了该功能?
  • 这就是我不确定的意思,因为我这里没有正在运行的 Borland,但也许你可以尝试一下。禁用事件时,当 control->OnChange = NULL; 触发另一个事件时,您可能会遇到问题;使用互斥锁可能会有所帮助。
  • 我做了一个实验,它确实似乎Sender 始终是编辑框。此外,我不相信您会遇到您描述的那种问题,因为 GUI 中的所有事件通常都是从单个线程执行的。 IIrc,但是您可能会遇到类似的问题,但它们与 Windows 消息队列有关。类似...如果您执行的操作导致了 SendMessage 而不是 PostMessage,您可能会在另一个事件仍在运行时触发一个事件。我不记得它在 Builder 内部是如何工作的细节。
  • 好的,除非有一个函数 SetText(Sender,UncideString) 我看不出有办法摆脱这个。感谢您的尝试,并为浪费的时间感到抱歉!
猜你喜欢
  • 2011-04-21
  • 2018-03-21
  • 2018-09-25
  • 1970-01-01
  • 2023-04-09
  • 2021-06-29
  • 2020-06-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多