2014-12-01 169 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); 
    } 
}