暂时取消订阅活动的简洁解决方案

时间:2013-09-27 13:18:49

标签: c# winforms reflection event-handling

我正在寻找一个简洁的解决方案来暂时取消订阅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;
  }
}

3 个答案:

答案 0 :(得分: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)) {
}

答案 1 :(得分:2)

你必须使用反射(所以不要忘记导入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";
}

答案 2 :(得分: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()中执行代码,该代码会更新另一个控件,无论文本是否因用户操作或应用程序逻辑而更改。