通过删除和恢复事件处理程序来防止事件处理

时间:2014-12-01 16:30:09

标签: c# event-handling

当设置为复选框或单选按钮等代码中的属性时,通常需要阻止某些控件的事件处理。为此,我使用以下帮助程序:

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;

    // ...
}

但是,我认为,如果没有在事件处理程序中编写if,但是通过将其名称作为附加参数或事件字段传递给某些事件来分离所有事件处理程序,可能会有更好的方法。本身和财产设置后重新连接它们。这甚至可能吗?

1 个答案:

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