2014-12-01 170 views
1

常常有必要防止一些控件的事件设置时的处理是一样的复选框或单选按钮等。为了实现这一目标代码的特性,我用的是下面的帮助:防止事件通过删除和恢复的事件处理程序处理

public static class PropertySetter 
{ 
    private static readonly object locker = new object(); 

    public static object CurrentObject { get; private set; } 

    public static string CurrentPropertyName { get; private set; } 

    public static void SetValueFor(this object obj, string propertyName, object value) 
    { 
     if (obj == null) { throw new ArgumentNullException("obj"); } 
     if (string.IsNullOrEmpty(propertyName) == true) { throw new ArgumentNullException("'propertyName"); } 

     PropertyInfo propertyInfo = obj.GetType().GetProperty(propertyName); 

     if (propertyInfo == null) 
     { 
      throw new ArgumentOutOfRangeException("propertyName", "Property not found."); 
     } 

     lock (locker) 
     { 
      CurrentObject = obj; 
      CurrentPropertyName = propertyName; 
      try 
      { 
       propertyInfo.SetValue(obj, value, null); 
      } 
      catch (Exception) 
      { 
       throw; 
      } 
      finally 
      { 
       CurrentObject = null; 
       CurrentPropertyName = null; 
      } 
     } 
    } 

    public static bool CanHandleEventFor(this object obj, string propertyName) 
    { 
     return object.ReferenceEquals(obj, CurrentObject) == false && CurrentPropertyName.Equals(propertyName) == false; 
    } 
} 

用法:

checkBox.SetValueFor("Checked", false); 

void checkBox_Checked(object sender, EventArgs e) 
{ 
    if (sender.CanHandleEventFor("Checked")) return; 

    // ... 
} 

但是我thougt或许有不写if事件处理程序中,而是由要么分离为一些事件的所有事件处理程序传递给它的名字作为additinal参数或者更好的办法事件字段本身和属性设置后重新附加它们。这甚至可能吗?

+0

分离事件处理程序是可行的 - 它们只是代表的集合(粗略地说)。但它不是我遇到的模式。为什么你不希望订阅者知道发生了变化事件 - 它是MVVM的基础。听众需要根据系统的状态来判断如何处理它所传递的值。我会承认已经把一个脏的表单级别变量“SettingUp_IgnoreValueChanges = true”并在事件处理程序中检查它。可怕,但很简单。 – PhillipH 2014-12-01 16:40:01

+0

当你举起你的事件并让事件监听者决定他们是否应该回应时,你能否通过一个参数? – sQuir3l 2014-12-01 16:47:58

+0

您在此处显示的代码已损坏。首先,以这种方式使用反射会降低程序的速度。其次,'CanHandleEventFor()'中的表达式应该是这样的:'!object.ReferenceEquals(obj,CurrentObject)|| !CurrentPropertyName.Equals(propertyName的);'。即如果对象和名称都相同,它只是相同的属性,所以如果两者都不相同则可以设置。最后,你不应该一般这样做。在需要执行此操作的情况下显示一个具体的场景,并且在没有此帮手的情况下,您将收到有关如何处理该问题的良好建议。 – 2014-12-01 18:28:49

回答

1

我终于设法解决这个问题:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var fc = new TestCheckbox(); 
     fc.CheckedChanged += (sender, e) => 
     { 
      int i = 0; 
     }; 

     // 'CheckedChanged' event is not raised. 
     fc.SetValueFor(x => x.Checked = true, "CheckedChanged"); 

     // 'CheckedChanged' event it raised. 
     fc.Checked = false; 
    } 
} 

class TestCheckbox 
{ 
    private bool _checked; 

    public event EventHandler CheckedChanged; 

    public bool Checked 
    { 
     get { return _checked; } 
     set 
     { 
      _checked = value; 
      if (CheckedChanged != null) 
      { 
       CheckedChanged(this, EventArgs.Empty); 
      } 
     } 
    } 
} 

public static class PropertySetter 
{ 
    private static readonly object locker = new object(); 

    public static void SetValueFor<T>(this T obj, Action<T> action, string eventName) 
    { 
     lock (locker) 
     { 
      try 
      { 
       // Get event handlers. 
       Type type = obj.GetType(); 
       var eventInfo = type.GetEvent(eventName); 
       var fieldInfo = type.GetField(eventName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); 
       var multicastDelegate = fieldInfo.GetValue(obj) as MulticastDelegate; 
       Delegate[] delegates = multicastDelegate.GetInvocationList(); 

       // Remove event handlers. 
       foreach (var item in delegates) 
       { 
        eventInfo.RemoveEventHandler(obj, item); 
       } 

       try 
       { 
        action(obj); 
       } 
       catch (Exception) 
       { 
        throw; 
       } 
       finally 
       { 
        // Restore event handlers. 
        foreach (var item in delegates) 
        { 
         eventInfo.AddEventHandler(obj, item); 
        } 
       } 
      } 
      catch (Exception) 
      { 
       throw; 
      } 
     } 
    } 
} 

不幸的是这只能在理论和因为每个事件没有专属领域根本不是那么容易.NET,它需要一些hacking所以我我会坚持最简单的解决方案,这是我认为的:

class Program 
{ 
    static void Main(string[] args) 
    { 
     CheckBox checkBox = new CheckBox(); 
     checkBox.CheckedChanged += (sender, e) => 
     { 
      if (!sender.CanHandleEvent("CheckedChanged")) return; 
      int i = 0; 
     }; 

     checkBox.SetValueFor(x => x.Checked = true, "CheckedChanged"); 
     checkBox.Checked = false; 
    } 
}  

public static class PropertySetter 
{ 
    private static readonly object locker = new object(); 

    public static object CurrentObject { get; private set; } 

    public static string CurrentEventName { get; private set; } 

    public static void SetValueFor<T>(this T obj, Action<T> action, string eventName) 
    { 
     lock (locker) 
     { 
      CurrentObject = obj; 
      CurrentEventName = eventName; 
      try 
      { 
       action(obj); 
      } 
      catch (Exception) 
      { 
       throw; 
      } 
      finally 
      { 
       CurrentObject = null; 
       CurrentEventName = null; 
      } 
     } 
    } 

    public static bool CanHandleEvent(this object obj, string eventName) 
    { 
     return !(object.ReferenceEquals(obj, CurrentObject) == true && CurrentEventName.Equals(eventName) == true); 
    } 
}