Как я могу создать пользовательское свойство зависимости * только для записи *?

Мне нужно знать, какова процедура создания свойства-зависимости только для записи. Я вижу, что класс DependencyProperty не имеет специального метода «Регистрация» для свойств только для записи, но я понятия не имею, может ли метод RegisterAttached применяться к тому, что я пытаюсь сделать.

Это свойство должно быть свойством зависимости, а не простым свойством CLR. Внутренне мой класс требует PropertyChangedCallback для этого свойства, чтобы оставаться стабильным.

Я знаю, что могут быть созданы свойства-зависимости только для записи, потому что это четко указано в:
Pro C# 2008 и платформа .NET 3.5, стр. 1061.
Однако это единственное место, где я могу найти "свойство зависимости " и "только для записи" на одной странице. И этот автор, по-видимому, не считал необходимым показывать читателю процедуру для чего-либо, кроме базового свойства-зависимости чтения-записи. Конечно, эта книга могла быть полной чушью, но эта книга выглядит довольно стандартно, так что я думаю, можно с уверенностью сказать, что автор прав. Я предполагаю, что отсутствие информации в Интернете связано с тем, что обычно никому не нужно делать такую ​​​​собственность.

Я знаю, что это звучит очень сомнительно, если вы хотите создать собственное свойство-зависимость только для записи. Уверяю вас, это имеет смысл там, где я этого хочу. В моем классе есть свойство, значение которого полезно только для объекта, который его устанавливает. Если бы другой объект впоследствии запросил значение этого свойства, он не смог бы извлечь из этого значения никакого рационального смысла, не зная исходного контекста установщика.

Это свойство не предназначено для использования в информационных целях. Попытки внешних объектов использовать значение свойства таким образом проблематичны, опасны и представляют угрозу безопасности. Поэтому я считаю, что лучший дизайн — запретить операции чтения для этого свойства. Любой, кто использует мой класс, обнаружит, что он вынужден использовать класс так, как он был задуман, что в итоге будет работать намного лучше и чище.


person Giffyguy    schedule 22.08.2009    source источник
comment
Очень интересно услышать, каков реальный сценарий по этому поводу. Вы говорите, что это рискованно, я думаю, что дерьмо, это запустит ядерные бомбы, если сеттер будет вызван, поэтому я хочу услышать больше!   -  person Jeff Wilcox    schedule 25.08.2009
comment
Вы спросили, как я могу создать, а не существуют ли они, немного беспокоит то, что вы выбрали ответ «это невозможно», а не вот как выполнить ответ вашего сценария.   -  person Jeff Wilcox    schedule 31.08.2009
comment
Он не выбирал мой ответ, он просто не заходил на сайт как минимум 2 дня, поэтому он был автоматически принят после окончания баунти-периода (вы можете увидеть это, наведя курсор на галочку: автоматическое принятие дает половину только награда). Я думаю, что оба наших ответа внесли свой вклад в тему: я поставил под сомнение предполагаемый «официальный» способ, а вы все еще предоставили потенциальный обходной путь. Система вознаграждений просто недостаточно гибка, чтобы приспособиться к этому прямо сейчас (среди некоторых других спорных вопросов см. meta.stackexchange.com/ вопросы/помечены/баунти).   -  person Steffen Opel    schedule 01.09.2009


Ответы (4)


Вы не можете, это, кажется, по замыслу. Хотя я могу понять ваш подход к упомянутой книге и никоим образом не ставлю под сомнение ее качество, я все же предполагаю, что это своего рода копирование и вставка или подобная проблема. Вот мои рассуждения:

Код системы свойств WPF

Дизайн системы свойств WPF

  • Что еще более важно, 'Текущая реализация процессора XAML в WPF по своей сути учитывает свойства зависимостей. Процессор WPF XAML использует методы системы свойств для свойств зависимостей при загрузке двоичного XAML и обработке атрибутов, которые являются свойствами зависимостей. Это эффективно обходит обертки свойств». см. Загрузка XAML и зависимость Свойства.
  • Самое главное, 'Свойства зависимостей обычно следует рассматривать как общедоступные свойства. Природа системы свойств Windows Presentation Foundation (WPF) не позволяет гарантировать безопасность значения свойства зависимостей». см. Безопасность свойств зависимостей.

В частности, последние два пункта определяют ограничение проекта, заключающееся в том, что значения свойств зависимостей всегда доступны через GetValue()/SetValue(), независимо от того, ограничен ли доступ к их оболочкам CLR или они вообще доступны, за единственным исключением, специально учитываемым для Свойства зависимостей только для чтения.

Следовательно, как Jeffs Ответ уже подразумевает, простое удаление геттера, например, на самом деле не мешает любому получить доступ к свойству через GetValue(), хотя это может по крайней мере 'уменьшить непосредственно доступное пространство имен пользовательского класса'. Полезность любого такого семантического обходного пути, позволяющего сделать значение свойства несколько менее видимым/доступным, а полученное значение по своей сути бесполезным для клиентов, как это предлагается Jeff, конечно, зависит от вашего конкретного сценария.

person Steffen Opel    schedule 25.08.2009

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

Вы бы рассмотрели идею предоставления недопустимого значения (например, null) для чтения через привязку или GetValue, просто не имея получателя CLR?

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

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

Сейчас я трачу большую часть своего времени на разработку элементов управления Silverlight, поэтому это свойство работает в WPF и Silverlight-land и не использует принуждение или что-то в этом роде. Хотя, возможно, это направит вас на верный путь.

    /// <summary>
    /// Sets the write-only dependency property.
    /// </summary>
    public string MyWriteOnlyDependencyProperty
    {
        set { SetValue(MyWriteOnlyDependencyPropertyProperty, value); }
    }

    private string _theRealSetValue;

    private bool _ignorePropertyChange;

    /// <summary>
    /// Identifies the MyWriteOnlyDependencyProperty dependency property.
    /// </summary>
    public static readonly DependencyProperty MyWriteOnlyDependencyPropertyProperty =
        DependencyProperty.Register(
            "MyWriteOnlyDependencyProperty",
            typeof(string),
            typeof(TemplatedControl1),
            new PropertyMetadata(null, OnMyWriteOnlyDependencyPropertyPropertyChanged));

    /// <summary>
    /// MyWriteOnlyDependencyPropertyProperty property changed handler.
    /// </summary>
    /// <param name="d">TemplatedControl1 that changed its MyWriteOnlyDependencyProperty.</param>
    /// <param name="e">Event arguments.</param>
    private static void OnMyWriteOnlyDependencyPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        TemplatedControl1 source = d as TemplatedControl1;
        if (source._ignorePropertyChange)
        {
            source._ignorePropertyChange = false;
            return;
        }
        string value = e.NewValue as string;

        source._theRealSetValue = value;

        // Revert, since this should never be accessible through a read
        source._ignorePropertyChange = true;
        source.SetValue(e.Property, e.OldValue);
    }
person Jeff Wilcox    schedule 22.08.2009
comment
Я не собираюсь набирать очки репутации, выдумывая вещи, я хотел бы сначала услышать, что думают другие люди. Должны быть какие-то эксперты WPF! У меня действительно есть книга, и я тоже в замешательстве! - person Jeff Wilcox; 22.08.2009
comment
source.SetValue(e.Property, e.OldValue); Будет ли эта строка также вызывать обратный вызов OnMyWriteOnlyDependencyPropertyPropertyChanged? Такое ощущение, что это может создать своего рода бесконечный цикл. Но я не уверен, что это так. - person Giffyguy; 28.08.2009
comment
Вы правы: я забыл добавить вызов ignorePropertyChange=true прямо перед SetValue. Обновил мой ответ. - person Jeff Wilcox; 28.08.2009
comment
Это хакерский способ сделать это. В 2009 году обратного вызова принуждения не существовало? - person Ben Voigt; 12.06.2014
comment
Silverlight != WPF несколько лет назад, и до сих пор имеет другой набор функций, в том числе без принуждения. - person Jeff Wilcox; 17.07.2014

Похоже, вы можете использовать CoerceValueCallback, связанный со свойством, через FrameworkPropertyMetadata, примененный в определении свойства зависимостей. Просто установите обратный вызов, который принимает второй аргумент, новое значение, передает его объекту через ваш собственный механизм только для записи, а затем возвращает null (или для типов значений default(T)).

Это правда, что «.NET запоминает исходное значение до приведения», но оно не будет распространяться через привязку данных. Вызовы GetValue вернут принудительное значение, которое ничего не пропускает.

Я использую это для реализации односторонних удобных установщиков для значения моего основного свойства, которое представляет собой последовательность байтов. Пользователь может привязать строку, например, чтобы задать в качестве основного свойства закодированные байты (ASCII или UTF-8, в зависимости от того, какое свойство установлено). Но не все последовательности байтов допустимы в UTF-8, поэтому невозможно отменить преобразование и прочитать строку обратно через свойство удобства.

public string AsciiData
{
    set { BinaryArray = Encoding.ASCII.GetBytes(value); }
}

public static readonly DependencyProperty AsciiDataProperty =
    DependencyProperty.Register("AsciiData",
        typeof(string),
        typeof(HexView),
        new FrameworkPropertyMetadata(null, CoerceAsciiData));

private static object CoerceAsciiData(DependencyObject target, object value)
{
    (target as HexView).AsciiData = value as string;
    return null;
}

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

person Ben Voigt    schedule 12.06.2014

Я не понимаю, почему вы не можете просто получить ничего полезного?

Но, кроме того, возможно, вы просто не реализуете «OnMyWriteOnlyDependencyPropertyPropertyChanged» в примере Джеффа.

Нет реальной причины проводить мероприятие, если никто не может его прочитать, верно?

person Noon Silk    schedule 25.08.2009
comment
Никаких изменений в счете. Однако бывают ситуации с привязкой данных (или в любом коде!), когда вы можете вызвать GetValue вместо использования геттера CLR. Вот почему очень важно НЕ помещать код в геттеры/сеттеры CLR для ваших DependencyProperties. - person Jeff Wilcox; 25.08.2009
comment
Но кто вызывает GetValue? Сам? Или какой-то контроль, над которым он не имеет контроля? - person Noon Silk; 25.08.2009
comment
В том-то и дело, что никогда не знаешь, кто вызывает GetValue :-/ поэтому, например, недопустимое значение, вероятно, лучше, чем последнее установленное значение. - person Jeff Wilcox; 25.08.2009
comment
Я знаю, что теоретически мы не знаем, но я просто говорю, что в этом конкретном случае кто-нибудь так звонит? Или вы предполагаете, что под капотом иногда фреймворк будет вызывать метод, а иногда — свойство? - person Noon Silk; 25.08.2009
comment
Другие элементы управления, созданные другими разработчиками, будут иметь прямой доступ к этому свойству. Я не могу позволить им пытаться использовать значение этого свойства для чего-либо. Это никогда не будет безопасно для них. - person Giffyguy; 25.08.2009
comment
Итак, вам нужно активно мешать им вызывать .GetValue()? Или можно просто не писать геттер? Между прочим, я действительно заинтересован в том, что он держит, на случай, если есть другой способ обойти это. - person Noon Silk; 25.08.2009
comment
Мне пришлось бы запретить им вызывать GetValue(), так как это свойство зависимости. Но если это невозможно сделать, как предлагали другие, тогда мне придется заставить GetValue() возвращать явно бесполезное фиктивное значение. - person Giffyguy; 25.08.2009