Яка різниця між (спеціальною) властивістю залежності та прикріпленою властивістю у WPF? Яке використання для кожного? Чим зазвичай відрізняються реалізації?
Відповіді:
Оскільки я майже не знайшов документації з цього приводу, потрібно було трохи поколупати навколо вихідного коду , але ось відповідь.
Існує різниця між реєстрацією властивості залежності як звичайної та як додаткової властивості, відмінної від "філософської" ( регулярні властивості призначені для використання типом, що оголошує, та його похідні типи, прикріплені властивості призначені для використання як розширення у довільних DependencyObject
екземплярах ). "Філософський", оскільки, як зауважив @MarqueIV у своєму коментарі до відповіді @ ReedCopsey, регулярні властивості також можна використовувати з довільними DependencyObject
екземплярами.
Більше того, я повинен не погодитися з іншими відповідями, в яких зазначається, що додане властивість є "типом властивості залежності", оскільки воно вводить в оману - ніяких "типів" властивостей залежності не існує. Структуру байдуже, зареєстровано власність як прикріплену чи ні - це навіть неможливо визначити (в тому сенсі, що ця інформація не реєструється, оскільки вона не має значення). Насправді всі властивості реєструються так, ніби вони були прикріпленими, але у випадку звичайних виконуються деякі додаткові дії, які трохи змінюють їх поведінку.
Щоб уникнути проблем із самостійним переглядом вихідного коду, ось зведена версія того, що відбувається.
При реєстрації властивості без вказаних метаданих здійснюється виклик
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
дає точно такий же результат, як і дзвінок
DependencyProperty.RegisterAttached(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass))
Однак при зазначенні метаданих здійснюється виклик
DependencyProperty.Register(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass),
typeMetadata: new FrameworkPropertyMetadata
{
CoerceValueCallback = CoerceCallback,
DefaultValue = "default value",
PropertyChangedCallback = ChangedCallback
});
еквівалентно дзвінку
var property = DependencyProperty.RegisterAttached(
name: "MyProperty",
propertyType: typeof(object),
ownerType: typeof(MyClass),
defaultMetadata: new PropertyMetadata
{
DefaultValue = "default value",
});
property.OverrideMetadata(
forType: typeof(MyClass),
typeMetadata: new FrameworkPropertyMetadata
{
CoerceValueCallback = CoerceCallback,
DefaultValue = "default value",
PropertyChangedCallback = ChangedCallback
});
Ключовою (і єдиною) різницею між звичайними та приєднаними властивостями залежностей є метадані за замовчуванням, доступні через властивість DependencyProperty.DefaultMetadata . Про це навіть згадується у розділі " Зауваження ":
Для неприєднаних властивостей тип метаданих, що повертається цією властивістю, не може бути переданий до похідних типів типу PropertyMetadata , навіть якщо властивість спочатку було зареєстровано з похідним типом метаданих. Якщо ви хочете, щоб спочатку зареєстровані метадані, включаючи оригінальний, можливо, похідний тип метаданих, зателефонуйте натомість GetMetadata (Type) , передавши оригінальний тип реєстрації як параметр.
Для вкладених властивостей тип метаданих, що повертаються цією властивістю, буде відповідати типу, вказаному в оригінальному методі реєстрації RegisterAttached .
Це добре видно з наданого коду. Маленькі підказки також приховані в методах реєстрації, тобто RegisterAttached
параметр метаданих називається defaultMetadata
, тоді як для Register
нього називається typeMetadata
. Для вкладених властивостей надані метадані стають метаданими за замовчуванням. Однак у випадку звичайних властивостей метаданими за замовчуванням завжди є свіжий екземпляр PropertyMetadata
з лише DefaultValue
набором (або із наданих метаданих, або автоматично). Лише наступний виклик OverrideMetadata
фактично використовує надані метадані.
Основна практична відмінність полягає в тому, що у випадку регулярних властивостей CoerceValueCallback
і PropertyChangedCallback
застосовуються лише для типів, що походять від типу, оголошеного як тип власника, а для прикріплених властивостей вони застосовуються для всіх типів. Наприклад, у цьому сценарії:
var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");
зареєстрований PropertyChangedCallback
буде викликаний, якщо власність було зареєстровано як нерухоме майно, але не буде викликаний, якщо воно було зареєстровано як звичайне майно. Те саме стосується CoerceValueCallback
.
Вторинна різниця випливає з того факту, що OverrideMetadata
вимагає, щоб тип, що постачається, походив від DependencyObject
. На практиці це означає, що тип власника для звичайних властивостей повинен походити з DependencyObject
, тоді як для прикріплених властивостей в може бути будь-якого типу (включаючи статичні класи, структури, перелічення, делегати тощо).
Окрім пропозиції @ MarqueIV, я кілька разів стикався з думками, що звичайні та вкладені властивості відрізняються тим, як їх можна використовувати в XAML . А саме, що регулярні властивості вимагають неявного синтаксису імен на відміну від явного синтаксису імен, що вимагається прикріпленими властивостями. Це технічно не відповідає дійсності , хоча на практиці це зазвичай так. Для наочності:
<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" />
<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />
У чистому XAML єдиними правилами, що регулюють використання цих синтаксисів, є такі:
Задоволення цих умов дозволяє використовувати відповідний синтаксис незалежно від того, було зареєстровано властивість залежності резервного копіювання як звичайне чи додане.
Тепер згадана помилкова думка спричинена тим, що переважна більшість навчальних посібників (разом із кодовими фрагментами коду Visual Studio ) вказують вам використовувати властивість CLR для властивостей звичайних залежностей та отримувати / встановлювати засоби доступу для приєднаних. Але ніщо не заважає вам використовувати обидва одночасно, дозволяючи використовувати будь-який синтаксис, який вам більше подобається.
Вкладені властивості - це тип властивості залежності. Різниця полягає в тому, як вони використовуються.
За допомогою вкладеної властивості властивість визначається для класу, який не є тим самим класом, для якого він використовується. Зазвичай це використовується для розмітки. Хорошими прикладами є Panel.ZIndex або Grid.Row - ви застосовуєте це до елемента керування (тобто: Button), але насправді це визначається в Panel або Grid. Властивість "прикріплено" до екземпляра кнопки.
Це дозволяє контейнеру, наприклад, створювати властивості, які можна використовувати на будь-якому UIelement.
Що стосується різниць у реалізації - це в основному лише питання використання Register vs. RegisterAttached, коли ви визначаєте властивість.
Вкладені властивості в основному призначені для елементів контейнера. Як, якщо у вас є сітка і у вас є grid.row, тепер це вважається вкладеною властивістю елемента сітки. Також ви можете використовувати цю властивість у текстовому полі, кнопці тощо, щоб встановити його місце в сітці.
Властивість залежності подібна до властивості, яка в основному належить до якогось іншого класу і використовується в іншому класі. наприклад: як у вас є прямокутник, тут висота та ширина є звичайними властивостями прямокутника, але зліва та зверху властивість залежності, оскільки воно належить до класу Canvass.
Вкладені властивості - це особливий вид DependencyProperties. Вони дозволяють приєднати значення до об’єкта, який нічого не знає про це значення. Хорошим прикладом цієї концепції є панелі компонування. Кожній панелі макета потрібні різні дані для вирівнювання її дочірніх елементів. Полотно потребує верхнього і лівого, DockPanel - док-станції і т. Д. Оскільки ви можете написати власну панель макета, список нескінченний. Отже, бачите, неможливо мати усі ці властивості на всіх елементах керування WPF. До розчину додаються властивості. Вони визначаються елементом управління, якому потрібні дані іншого контролю в конкретному контексті. Наприклад, елемент, вирівняний за батьківською панеллю макета.
Я думаю, що ви можете визначити вкладене властивість у самому класі або ви можете визначити його в іншому класі. Ми завжди могли використовувати вкладене властивість для розширення стандартних елементів керування Microsoft. Але властивість залежності ви визначаєте у власному користувацькому контролі. Наприклад, ви можете успадкувати свій контроль від стандартного елемента управління та визначити властивість залежності у своєму власному контролі та використовувати його. Це еквівалентно для визначення прикріпленої властивості та використання цієї прикріпленої властивості у стандартному елементі управління.