2

我正在寻找一个巧妙的解决方案来临时取消订阅 WinForm 事件。例如,如果我有一个带有以下事件处理程序的 textBox1 控件:

private void textBox1_TextChanged(object sender, EventArgs eventArgs)
{
  // Do something
}

当我以编程方式更改文本框的内容时,我不希望触发此事件。但不是写:

  try
  {
    textBox1.TextChanged -= textBox1_TextChanged;
    // Change textbox contents without triggering event
  }
  finally
  {
    textBox1.TextChanged += textBox1_TextChanged;
  }

我正在寻找可以使用类似于此的东西:

using(new EventInhibitor(textBox1.TextChanged, textBox1_TextChanged))
{
  // Change textbox contents without triggering event
}

问题是如何将 textBox1.TextChanged 事件传递给构造函数?反射?表达式?

public class EventInhibitor : IDisposable
{
  private bool _disposed = false;
  private XXX _event;
  private EventHandler _handler;

  public EventInhibitor(XXX event, EventHandler handler)
  {
    _event   = event;
    _handler = handler;
    _event -= _handler;
  }

  public void Dispose()
  {
    if(_disposed)
      return:
    _event += _handler;
    _event = null;
    _handler = null;
    _disposed = true;
  }
}
4

3 回答 3

3

另一个解决方案..它只是在之前和之后调用函数:

public class EventInhibitor : IDisposable {
    private bool _disposed;
    private Action _after;

    public EventInhibitor(Action before, Action after)
    {
        before();
        _after = after;
    }

    public void Dispose() {
        if (_disposed)
            return;

        _disposed = true;
        _after();
    }
}

用法:

using(new EventInhibitor(() => textBox1.TextChanged -= textBox1_TextChanged, 
                         () => textBox1.TextChanged += textBox1_TextChanged)) {
}
于 2013-09-27T14:04:32.563 回答
2

您必须使用反射(所以不要忘记 import System.Reflection)。您可以将控件(或对象,如果您希望它更通用)和事件名称作为字符串传递:

public class EventInhibitor : IDisposable
{
  private bool _disposed = false;
  private EventInfo _event;
  private EventHandler _handler;
  private Control _control;

  public EventInhibitor(Control c, string EventName, EventHandler handler)
  {
    _event = c.GetType().GetEvent(EventName); 
    _handler = handler;
    _control = c;
    _event.RemoveEventHandler(_control, _handler);
  }

  public void Dispose()
  {
      if (_disposed)
          return;

      _event.AddEventHandler(_control, _handler);
    _event = null;
    _handler = null;
    _control = null;
    _disposed = true;
  }
}

然后使用它:

using (new EventInhibitor(textBox1, "TextChanged", textBox1_TextChanged))
{
    textBox1.Text = "Hello World";
}
于 2013-09-27T13:34:57.197 回答
0

作为您想要的解决方案的替代方案,这就是我过去一直解决这个问题的方式。

TextBox myTextBox;

private string _text;
public string Text
{
    get
    {
        return _text;
    }
    set
    {
        if (_text != value)
        {
            _text = value;

            OnTextChanged();
        }
    }
}

private void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
    if (Text != myTextBox.Text)
    {
        // the user must have edited the TextBox
        Text = myTextBox.Text;
    }
}

private void OnTextChanged()
{
    if (myTextBox.Text != Text)
        myTextBox.Text = Text;
}

这样,当用户通过在框中键入来更改文本时,它会引发事件并执行您选择的一些代码。但是,如果您手动设置 Text 属性,它只会引发一次而不是两次。如果您想在用户编辑文本框时做出不同的反应,那么您可以在事件处理程序中添加一些额外的代码,它只会在文本因用户输入而改变时执行。

通过这种方式,您可以分离处理文本更改的不同方面。例如,您可能希望执行OnTextChanged()更新另一个控件的代码,而不管 Text 是否由于用户操作或您的应用程序逻辑而更改。

于 2013-09-27T13:45:45.427 回答