Да, есть простой способ «сделать доступным только для чтения DependencyProperty
для отображения значения другого свойства», но это может потребовать довольно фундаментального изменения в общей модели программирования свойств вашей приложение. Короче говоря, вместо использования от DependencyPropertyKey
до push значений в свойство, каждый доступный только для чтения DependencyProperty
может иметь обратный вызов CoerceValue , который создает его собственное значение извлечение всех исходных значений, от которых оно зависит.
В этом подходе параметр 'value', который передается в CoerceValue
, игнорируется. Вместо этого каждая функция DP * CoerceValue
пересчитывает свое значение «с нуля», напрямую выбирая все необходимые значения из экземпляра DependencyObject
, переданного в CoerceValue
(вы можете использовать для этого dobj.GetValue(...)
, если хотите избежать приведения к тип экземпляра владельца).
Старайтесь подавлять любые подозрения, что игнорирование значения, предоставленного для CoerceValue
, может привести к потере чего-либо. Если вы придерживаетесь этой модели, эти значения никогда не будут полезны, и общая работа будет такой же или меньше, чем «принудительная» модель, потому что исходные значения, которые не изменились, как всегда, кэшируются системой DP. Все, что изменилось, это то, кто отвечает за расчет и где он сделан. Здесь приятно то, что вычисление каждого значения DP всегда централизовано в одном месте и специально связано с этим DP, а не разбросано по приложению.
Вы можете выбросить DependencyPropertyKey
в этой модели, потому что она вам никогда не понадобится. Вместо этого, чтобы обновить значение любого DP, доступного только для чтения, вы просто вызываете CoerceValue
или InvalidateValue
в экземпляре владельца, указывая желаемый DP. Это работает, потому что эти две функции не требуют использования клавиши DP , они вместо этого используют открытый идентификатор DependencyProperty
и являются открытыми функциями, поэтому любой код может вызывать их из в любом месте.
Что касается того, когда и где делать эти CoerceValue
/ InvalidateValue
звонки, есть два варианта:
- Стремление: Вызовите
InvalidateValue
вызов для (целевого) DP в PropertyChangedCallback
каждого (исходного) DP, который упоминается в (целевых) функциях DP CoerceValueCallback
,
- или -
- Ленивый: Всегда вызывайте
CoerceValue
на DP непосредственно перед извлечением его значения.
Это правда, что этот метод не так удобен для XAML, но это не было требованием вопроса OP. Однако, учитывая, что при таком подходе вам даже не нужно извлекать или сохранять DependencyPropertyKey
вообще, кажется, что это может быть одним из самых изящных способов пойти, если вы сможете переосмыслить свое приложение вокруг семантика "pull".
В совершенно ином ключе есть еще одно решение, которое может быть даже проще:
Откройте INotifyPropertyChanged
на вашем DependencyObject
и используйте свойства CLR для свойств только для чтения, которые теперь будут иметь простое поле поддержки. Да, система привязки WPF будет правильно обнаруживать и контролировать оба механизма - DependencyProperty
и INotifyPropertyChanged
- в одном экземпляре класса. Для установки изменений этого свойства, доступного только для чтения, рекомендуется использовать установщик, частный или другой, и этот установщик должен проверить поле поддержки, чтобы обнаружить пустые (избыточные) изменения, в противном случае возникает событие CLR PropertyChanged
старого стиля.
Для привязки к этому свойству, доступному только для чтения, либо используйте перегрузку OnPropertyChanged
владельца (для самосвязывания), чтобы передать изменения из DP, либо, для привязки из произвольных внешних свойств, используйте System.ComponentModel.DependencyPropertyDescriptor.FromProperty
, чтобы получить DependencyPropertyDescriptor
для соответствующих DP-источников и используйте метод AddValueChanged
для установки обработчика, который выдвигает новые значения.
Конечно, для свойств, не относящихся к DP, или экземпляров, отличных от DependencyObject
, вы можете просто подписаться на их событие INotifyPropertyChanged
, чтобы отслеживать изменения, которые могут повлиять на ваше свойство только для чтения. В любом случае, независимо от того, каким образом вы передаете изменения в свойство только для чтения, событие, вызванное его установщиком, гарантирует, что изменения свойства только для чтения правильно распространяются далее на любые дополнительные зависимые свойства, будь то WPF / DP, CLR, данные или иначе.