Объявите EventHandlers как статические или нестатические в элементах управления WPF - PullRequest
2 голосов
/ 06 января 2011

Я работал над созданием пользовательского элемента управления с командным поведением и столкнулся с чем-то странным.Некоторые статьи, которые я обнаружил, объявили CanExecuteChangedHandler EventHandler как статические, а другие не были статичными.Документация Microsoft SDK показывает статическое состояние, но когда я объявляю его статическим, при использовании нескольких элементов управления я получаю странное поведение.

private static EventHandler canExecuteChangedHandler;

private void AddSecureCommand(ISecureCommand secureCommand)
{
    canExecuteChangedHandler = new EventHandler(CanExecuteChanged);
    securityTypeChangedHandler = new EventHandler(SecurityTypeChanged);

    if (secureCommand != null)
    {
        secureCommand.CanExecuteChanged += canExecuteChangedHandler;
        secureCommand.SecurityTypeChanged += securityTypeChangedHandler;
    }
}

Кто-нибудь знает правильный путь?Я делаю что-то не так, что статический EventHandler не работает?

1 Ответ

1 голос
/ 07 января 2011

Заявленная причина сохранения локальной копии EventHandler заключается в том, что командная подсистема WPF внутренне использует слабые ссылки, и поэтому нам нужно сохранить ссылку на конкретный объект делегата, который добавляется к событию CanExecuteChanged. Если это так, то всякий раз, когда мы добавляем к любому командному событию подсистемы, мы также должны соблюдать эту практику, как вы делаете для SecurityTypeChanged.

Краткий ответ на ваш вопрос: canExecuteChangedHandler может быть статичным, но вы должны быть осторожны, чтобы инициализировать его только один раз . Причина, по которой он может быть статическим, заключается в том, что все new EventHandler(CanExecuteChanged) будут делать то же самое, если CanExecuteChanged статичен. Причина, по которой он инициализируется один раз, состоит в том, что разные экземпляры различны.

Частная собственность, которая имеет правильную семантику только для чтения:

static EventHandler canExecuteChangedHandler
{
    get
    {
        if (internalCanExecuteChangedHandler == null)
            internalCanExecuteChangedHandler = new EventHandler(CanExecuteChanged);
        return internalCanExecuteChangedHandler;
    }

}
static EventHandler internalCanExecuteChangedHandler;

но это работает, только если CanExecuteChanged является статическим. Если это не так, удалите квалификаторы static. В любом случае вы должны быть осторожны, чтобы использовать свойство.

В этом конкретном примере второй раз, когда AddSecureCommand называется первым canExecuteChangedHandler, подвергается риску сбора мусора.

Наконец, если все это звучит как черная магия, вот пример кода, чтобы показать, что происходит.

public class Container
{
    private WeakReference reference;
    public object Object
    {
        get { return reference.IsAlive ? reference.Target : null; }
        set { reference = new WeakReference(value); }
    }
}

public class DelegateTest
{
    private EventHandler eventHandler;
    private Container container1;
    private Container container2;

    void MyEventHandler(object sender, EventArgs args)
    {
    }

    public DelegateTest()
    {
        this.eventHandler = new EventHandler(MyEventHandler);
        this.container1 = new Container { Object = this.eventHandler };
        this.container2 = new Container { Object = new EventHandler(MyEventHandler) };
        GC.Collect();
        Console.WriteLine("container1: {0}", this.container1.Object == null);
        Console.WriteLine("container2: {0}", this.container2.Object == null);
    }
}

Это выдаст:

container1: False
container2: True

, который указывает, что во время сборки мусора у второго контейнера была EventHandler уборка мусора "из-под него". Это спроектировано так, как работают слабые ссылки, и объяснение для вас необходимо сохранить ссылку на него самостоятельно.

...