WPF-почему нет переопределяемого метода "OnDataContextChanged"?


Объект FrameworkElement имеет событие DataContextChanged. Однако не существует метода OnDataContextChanged, который можно переопределить.

Есть идеи, почему?

4 4

4 ответа:

Если метод является виртуальным, то у пользователя есть возможность либо увеличить функциональность базовой функции, вызвав метод базового класса, либо заменить функциональность базового класса, не вызвав метод базового класса. Для методов OnEvent (), если вы не вызываете метод базового класса, то событие не будет вызвано (это ответственность метода базового класса.) Если базовый класс выполняет некоторый вид управления состоянием внутри метода OnEvent, это означает, что производный класс может если пользователь решит пропустить вызов метода базового класса, то это может привести к случайной недействительности состояния объекта. В документации можно указать "пожалуйста, всегда вызывайте метод базового класса", но нет способа принудительно применить его.

Когда я вижу событие, у которого нет виртуального метода OnEvent (), я обычно предполагаю, что метод выполняет какое-то внутреннее управление состоянием, и разработчики класса хотят гарантировать, что их управление состоянием выполняется. Это не так в FrameworkElement, и это не единственное событие, которое не следует шаблону, поэтому мне любопытно, каковы рассуждения.

Я покопался в рефлекторе, чтобы посмотреть, смогу ли я найти причину. Существует метод OnDataContextChanged (), но это обработчик изменения свойств зависимостей и не следует стандартному шаблону событий. Это, вероятно, причина того, что он не стал защищенным виртуальным. Это нестандартно, так что это будет запутанно. Он статичен, так что вы все равно не сможете его переопределить. С он вызывается автоматически фреймворком свойств зависимостей, и вы не можете переопределить его, я полагаю, что у нас есть причина, по которой он является частным, а не статическим виртуальным.

Вы можете использовать другой шаблон для отображения нормального шаблона событий:

class FrameworkElement
{
    // difference: use DataContextPropertyChanged as the change callback
    public static readonly DependencyProperty DataContextProperty = ...

    protected virtual void OnDataContextChanged(...)
    {
        // raise the DataContextChanged event
    }

    private static void DataContextPropertyChanged(...)
    {
        ((FrameworkElement)d).OnDataContextChanged(...);
    }
}

Я догадываюсь, почему они этого не сделали? Обычно вы вызываете OnEvent (), чтобы вызвать событие. Событие автоматически вызывается при изменении DataContext, и нет смысла вызывать его в любое другое время.

Хороший вопрос.

Я только предполагаю, но, глядя в зеркало, я бы сказал, что это просто лень, возможно, с щепоткой (необоснованной?) проблемы производительности. FrameworkElement имеет общий EventHandlersStore, который отвечает за сохранение информации о событиях (делегатов) для целого набора событий. Логика добавления и удаления в событиях CLR (например, DataContextChanged) просто вызывает EventHandlersStore с соответствующим ключом.

Существует общий метод RaiseDependencyPropertyChanged, который вызывается для вызова всех различных видов событий. Существует также частный метод OnDataContextChanged, который вызывает метод RaiseDependencyPropertyChanged. Однако он статичен и зарегистрирован как часть метаданных d-prop.

Короче говоря, я не вижу технической причины не включать переопределяемый метод OnDataContextChanged. Просто мне кажется, что это короткий путь в реализации. Является ли это просто академическим, или вы пытаетесь достичь чего-то здесь?

Silverlight Примечание:

На Silverlight Beta 4 нет события DataContextChanged (ну его не публичного по крайней мере).

Отчет об ошибке Microsoft Connectбыл помечен как "исправлено", но без указания того, что это на самом деле означает.

В то же время вам нужен обходной путь, такой как Этот из CodeProject - который очень прост и должен быть легко переключен, если Microsoft когда-либо действительно сделает событие общедоступным.

Свойства зависимостей обычно не имеют соответствующих виртуальных методов для вызова события, поскольку ожидается, что событиями изменения будет управлять сама система свойств dependecy.

Однако то, что вы можете переопределить, чтобы обработать любое изменение свойства зависимости, - это DependencyObject.OnPropertyChanged примерно так:

class MyClass : FrameworkElement {

    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) {
        base.OnPropertyChanged(e);
        if (e.Property == FrameworkElement.DataContextProperty) {
            // do something with e.NewValue/e.OldValue
        }
    }

}