Чтобы избежать логических циклов, WPF решительно сопротивляется попыткам установить значение DependencyProperty
в его текущее значение. Это проблема во время инициализации, когда вы хотите, чтобы зависимая логика запускалась для установки всего в первый раз на основе DefaultValue
, записанного в метаданных, для каждого из различных зависимых свойств. WPF этого не сделает, потому что, в особом случае, все эти свойства уже получают значения по умолчанию, даже не выполнив никакой такой логики.
Насколько я могу судить, ни одна комбинация InvalidateProperty
, CoerceValue
или ClearValue
не убедит WPF выполнить эту работу. Тривиальным решением было бы каким-то образом вызвать безнадежно безопасное значение, отличное от значения по умолчанию, чтобы изменить его на, а затем сбросить значение с помощью ClearValue
. Это кажется хакерским, поскольку определение «безвредной» ценности может оказаться непрактичным. Возможно, еще хуже, этот подход излишне будет волновать граф зависимостей дважды, а не один раз.
В качестве, возможно, более элегантного решения вы можете вызвать следующий метод расширения в конструкторе каждого соответствующего объекта, чтобы вручную вызвать логику PropertyChanged
для каждого DependencyProperty
, для которого необходимо распространить свои изменения. Этот помощник будет инициировать последующие изменения в соответствии со значением по умолчанию, хранящимся в соответствующих метаданных.
public static void ResetValue(this DependencyObject _this, DependencyProperty dp)
{
var md = dp.GetMetadata(_this.GetType());
if (_this.GetValue(dp).Equals(md.DefaultValue))
{
var args = new DependencyPropertyChangedEventArgs(dp, DependencyProperty.UnsetValue, md.DefaultValue);
md.PropertyChangedCallback(_this, args);
}
else
_this.ClearValue(dp);
}
Как показано, функция включает проверку, чтобы увидеть, будет ли ClearValue
действительно эффективным, поэтому помимо сценариев инициализации вы также можете использовать эту функцию в качестве замены для ClearValue
в целом. Для наглядности демонстрации этот метод не проверяет, что хост-объект фактически предоставляет какую-либо логику изменения свойств, но это было бы простым изменением. В C # 7, например:
// ...
md.PropertyChangedCallback?.Invoke(_this, args);
Пример использования в вашем классе:
class MyClass : DependencyObject
{
public static readonly DependencyProperty MyDp = /* ... */
public MyClass()
{
this.ResetValue(MyDp);
/* ... */
}
};