Здравствуйте, stalcer, Вы писали:
MM>>Для такого есть CoerceValueCallback. Пишешь его для IsolationProperty, проверяя внутри наличие локального значения при помощи GetValueSource. S>Че-то ты путаешь, имхо. CoerceValueCallback — вааще не для этого.
В-общем случае, это метод корректировки значения. Он здесь вполне подойдет.
S>Вот, кстати GetValueSource. Возвращает System.Windows.ValueSource в котором есть BaseValueSource. Очень интересный енум: S>Что означает, что DependencyObject нифига не независимая технология. И все это просто вшито в WPF. Тогды понятно, вопрос снят.
Рановато ты вопрос снял, не разобравшись. Да, DependencyObject не независимая технология. Его, кстати, никто никогда таким и не позиционировал. Тем не менее, он отделен от визуального слоя, потому что лежит в сборке WindowsBase. Тип BaseValueSource лежит в PresentationFramework, которая суть конечный код реализации WPF. Поэтому явно DependencyObject на WPF не завязан, но предназначен для нее. DependencyObject отлично подходит для случаев, где нужно использовать Dependency Property. Твой пример отчасти таков (пишу "отчасти", т.к. мы не знаем всех подробностей). В нем значение свойства зависит от нескольких источников, от локального значения и от сессии.
Никак не пойму вот что: Механизм Dependency Properties вроде бы не зависит от WPF. Т.е. наоборот WPF строится на основе Dependency Properties. Т.е. получается что я могу этот механизм использовать в своей иерархии классов, предположим — совсем не относящихся к UI. Нужно только отнаследовать мои классы от DependencyObject. Правильно?
Вопрос: Как в этом случае указать по какой цепочке нужно искать значение свойства? Ну вот в WPF значение ищется локально, потом в стиле, потом в паренте, ну и т.п (это я упрощенно и, наверное, не совсем верно написал). Как мне свою цепочку задать для моих классов? Что то не вижу подходящего API в DependencyObject...
S>Никак не пойму вот что: Механизм Dependency Properties вроде бы не зависит от WPF. Т.е. наоборот WPF строится на основе Dependency Properties. Т.е. получается что я могу этот механизм использовать в своей иерархии классов, предположим — совсем не относящихся к UI. Нужно только отнаследовать мои классы от DependencyObject. Правильно?
Правильно.
S>Вопрос: Как в этом случае указать по какой цепочке нужно искать значение свойства? Ну вот в WPF значение ищется локально, потом в стиле, потом в паренте, ну и т.п (это я упрощенно и, наверное, не совсем верно написал). Как мне свою цепочку задать для моих классов? Что то не вижу подходящего API в DependencyObject...
Здравствуйте, Хэлкар, Вы писали:
Х>AFAIK стандартного нет.
Дык, я вообще не понимаю, как сделать это хоть каким-нибудь способом. Ведь при поиске значения свойства, система пробегает по объектам сама, не спрашивая меня. Откуда ж она знает по каким объектам пробегать? Вроде кажется, что стандартного способа не быть просто не может. Может он просто недоступный? Ведь как-то сам WPF то они реализовали.
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, Хэлкар, Вы писали:
Х>>AFAIK стандартного нет.
S>Дык, я вообще не понимаю, как сделать это хоть каким-нибудь способом. Ведь при поиске значения свойства, система пробегает по объектам сама, не спрашивая меня. Откуда ж она знает по каким объектам пробегать? Вроде кажется, что стандартного способа не быть просто не может. Может он просто недоступный? Ведь как-то сам WPF то они реализовали.
Система не пробегает по дереву объектов.Почитайте про то, как эти свойства хранятся, и многое станет ясным. Вообще сама концепция DependencyProperty была введена, чтобы уменьшить количество потребляемой памяти из-за дикого количества свойств у объектов WPF. Если каждому объекту в дереве еще и задавать все свойства, то настанет OutOfMemory достаточно скоро. Да и поиск будет совсем неэффективен.
Здравствуйте, Codechanger, Вы писали:
C>Система не пробегает по дереву объектов.Почитайте про то, как эти свойства хранятся, и многое станет ясным. Вообще сама концепция DependencyProperty была введена, чтобы уменьшить количество потребляемой памяти из-за дикого количества свойств у объектов WPF. Если каждому объекту в дереве еще и задавать все свойства, то настанет OutOfMemory достаточно скоро. Да и поиск будет совсем неэффективен.
Не-не-не. Цель Dependency Property главным образом вытекает из названия, а именно — зависимость значения свойства от окружения (зачастую, положения в визуальном дереве). Экономить память научились еще в Windows Forms, но никаких Dependency Property там нет.
Здравствуйте, stalcer, Вы писали:
S>Вопрос: Как в этом случае указать по какой цепочке нужно искать значение свойства? Ну вот в WPF значение ищется локально, потом в стиле, потом в паренте, ну и т.п (это я упрощенно и, наверное, не совсем верно написал). Как мне свою цепочку задать для моих классов? Что то не вижу подходящего API в DependencyObject...
Ты несколько неправильно понял. Да, можно сказать, что значение "ищется", но на самом деле это не так. Рассмотрим твои примеры. Локальное значение — это значение, лежащее прямо в свойстве. Здесь никакого поиска и не нужно. Далее, значение из стиля. Когда WPF применяет к элементу стиль, тогда оно копирует значение из стиля в свойство. Просто оно ставит в свойстве флажок, говорящий о том, что значение взято из стиля, а значит, если появится локальное значение, оно должно "затереть" значение из стиля. Что еще? Наследование значения. И снова нет никакого поиска, т.к. такое значение идет "сверху-вниз", то бишь, от родителя к дочернему элементу, а не наоборот. И главное, DependencyObject, о котором мы говорим — все-таки механизм ориентированный на WPF. Об этом говорит хотя-бы тот факт, что есть еще WCF, где тоже есть свой собственный DependencyObject.
Re[4]: Dependency Properties
От:
Аноним
Дата:
02.08.11 18:24
Оценка:
Здравствуйте, Codechanger, Вы писали:
C>Система не пробегает по дереву объектов.Почитайте про то, как эти свойства хранятся, и многое станет ясным. Вообще сама концепция DependencyProperty была введена, чтобы уменьшить количество потребляемой памяти из-за дикого количества свойств у объектов WPF. Если каждому объекту в дереве еще и задавать все свойства, то настанет OutOfMemory достаточно скоро. Да и поиск будет совсем неэффективен.
Да понимаю я как они хранятся. Я не понимаю как указать для MyTransaction что если значение не локальное, то оно должно браться из Session? Т.е. откуда функция GetValue, вызываемая из MyTransaction.Isolation.get вообще узнает про Session?
public class MyTransaction: DependencyObject
{
public int Isolation
{
get { return (int)GetValue(IsolationProperty); }
set.....
}
public MySession Session
{
get....
set....
}
}
public class MySession: DependencyObject
{
public int Isolation
{
get { return (int)GetValue(MyClass.IsolationProperty); }
}
}
Здравствуйте, Аноним, Вы писали:
А>Да понимаю я как они хранятся. Я не понимаю как указать для MyTransaction что если значение не локальное, то оно должно браться из Session? Т.е. откуда функция GetValue, вызываемая из MyTransaction.Isolation.get вообще узнает про Session?
Для такого есть CoerceValueCallback. Пишешь его для IsolationProperty, проверяя внутри наличие локального значения при помощи GetValueSource. И не забудь добавить обработчик изменения свойства Session, который будет вызываеть CoerceValue для IsolationProperty.
Здравствуйте, MxMsk, Вы писали:
MM>Для такого есть CoerceValueCallback. Пишешь его для IsolationProperty, проверяя внутри наличие локального значения при помощи GetValueSource.
Че-то ты путаешь, имхо. CoerceValueCallback — вааще не для этого.
Вот, кстати GetValueSource. Возвращает System.Windows.ValueSource в котором есть BaseValueSource. Очень интересный енум:
Здравствуйте, MxMsk, Вы писали:
MM>Рановато ты вопрос снял, не разобравшись. Да, DependencyObject не независимая технология. Его, кстати, никто никогда таким и не позиционировал. Тем не менее, он отделен от визуального слоя, потому что лежит в сборке WindowsBase. Тип BaseValueSource лежит в PresentationFramework, которая суть конечный код реализации WPF.
Ну да. Вижу. DependencyPropertyHelper.GetValueSource. На Helper то я внимания и не обратил. Тогда уже лучше...
MM>Поэтому явно DependencyObject на WPF не завязан, но предназначен для нее. DependencyObject отлично подходит для случаев, где нужно использовать Dependency Property. Твой пример отчасти таков (пишу "отчасти", т.к. мы не знаем всех подробностей).
Дык и я не знаю. Это ж просто пример .
Я думаю что сам WPF не использует CoerceValueCallback для организации цепочек (объект-стиль-парент-...). Тогда как он сам то сделан?
Здравствуйте, stalcer, Вы писали:
S>Я думаю что сам WPF не использует CoerceValueCallback для организации цепочек (объект-стиль-парент-...). Тогда как он сам то сделан?
На пальцах я уже объяснял
. Есть приоритет значений и есть моменты, когда те или иные значения присваиваются. Это только выглядит, как цепочка поиска, а на самом деле, это скорее "приоритет присваивания".
. Есть приоритет значений и есть моменты, когда те или иные значения присваиваются. Это только выглядит, как цепочка поиска, а на самом деле, это скорее "приоритет присваивания".
Вопросов три:
1) Откуда такая инфа? Можешь дать ссылку, где почитать?
2) Можешь, если не лень, в мой пример вписать код, чтобы получилось так как это делает WPF. А то не совсем понятно...
3) Да и потом. Как-то странно получается: если это "приоритет присваивания", как ты пишешь, то какая же тогда экономия памяти — это значит что в каждом объекте для каждого свойства есть место под значение, да еще и с флагами. Ну, то есть, если один стиль применяется к 100-а объектам, то по твоей логике получается, что значение свойств из этого стиля копируется во все 100 объектов.
Здравствуйте, stalcer, Вы писали:
S>Вопросов три: S>1) Откуда такая инфа? Можешь дать ссылку, где почитать?
Инфа личная, полученная из чтения исходников WPF через Reflector и ILSpy. Возможно эта ссылка будет полезна.
S>2) Можешь, если не лень, в мой пример вписать код, чтобы получилось так как это делает WPF. А то не совсем понятно...
Под рукой Студии нет, так что условно:
public class MyTransaction: DependencyObject
{
public static readonly IsolationProperty = DependencyProperty.Register(...,
new FrameworkPropertyMetadata(..., new CoerceValueCallback(CoerceIsolationProperty)));
private static object CoerceIsolationProperty(DependencyObject d, object baseValue)
{
if (DependencyPropertyHelper.GetValueSource(d, IsolationProperty).BaseValueSource != BaseValueSource.Local
&&
Session != null)
{
return Session.Isolation;
}
return baseValue;
}
public int Isolation
{
get { return (int)GetValue(IsolationProperty); }
set.....
}
public MySession Session
{
get....
set
{
....
CoerceValue(IsolationProperty);
}
}
}
Если Session тоже Dependency Property, то CoerceValue надо перенести в обработчик OnPropertyChanged, который следует указать при регистрации свойства, аналогично CoerceIsolationProperty.
S>3) Да и потом. Как-то странно получается: если это "приоритет присваивания", как ты пишешь, то какая же тогда экономия памяти — это значит что в каждом объекте для каждого свойства есть место под значение, да еще и с флагами. Ну, то есть, если один стиль применяется к 100-а объектам, то по твоей логике получается, что значение свойств из этого стиля копируется во все 100 объектов.
Все значения свойств хранятся в виде object. Поэтому копируется только ссылка. В случае незамораживаемых Freezable-ов в некоторых случаях делается полное копирование, но в такие дебри влезать не охота
Здравствуйте, MxMsk, Вы писали:
MM>Инфа личная, полученная из чтения исходников WPF через Reflector и ILSpy. Возможно эта ссылка будет полезна.
Статья ничего по сути вопроса не объясняет, к сожалению.
MM>Под рукой Студии нет, так что условно:
Через CoerceValueCallback я понял... Я имел ввиду написать так, как в WPF это сделано. Т.е. не через CoerceValueCallback.
MM>Все значения свойств хранятся в виде object. Поэтому копируется только ссылка. В случае незамораживаемых Freezable-ов в некоторых случаях делается полное копирование, но в такие дебри влезать не охота
Имхо в 90% случаев ссылка (т.е. указатель) по размеру больше или равно размеру значения (Color, Width, Height, IsPressed и т.п.). Честно говоря, я все таки думаю, что физический поиск все-таки есть. Т.е. значения хранятся в какой-нибудь хэш-таблице, и даже для того, чтобы проверить установлено ли локальное значение нужно искать в этой таблице по составному ключу — (объект + DP).
Не говоря уже о цепочках (стили, паренты). Хотя я допускаю, что как раз цепочки делаются "присвоением", как ты говоришь. Тогда экономия памяти работает только на том, утановлено ли вообще значение или нет. Ну т.е. если значение установлено, то оно хранится в хэш-таблице, как описано выше. А если не установлено — то берется дефолтное из PropertyMetadata. Таким образом на дефолтные значения для каждого объекта память не тратится.
Другими словами, я могу поверить в то что: сам DependencyObject поддерживает только два уровня:
— дефолтное значение, хранящееся в PropertyMetadata (один раз для всех объектов)
— и локальное значение, хранящееся для каждого объекта отдельно только в том случае, если оно установлено.
Здравствуйте, stalcer, Вы писали:
MM>>Под рукой Студии нет, так что условно: S>Через CoerceValueCallback я понял... Я имел ввиду написать так, как в WPF это сделано. Т.е. не через CoerceValueCallback.
Гм. Я что-то перестаю понимать. Что означает "как сделано в WPF"? В WPF это вообще никак не сделано. Это же ортогональные вещи: базовая реализация свойств и ее использование.
MM>>Все значения свойств хранятся в виде object. Поэтому копируется только ссылка. В случае незамораживаемых Freezable-ов в некоторых случаях делается полное копирование, но в такие дебри влезать не охота S>Имхо в 90% случаев ссылка (т.е. указатель) по размеру больше или равно размеру значения (Color, Width, Height, IsPressed и т.п.). Честно говоря, я все таки думаю, что физический поиск все-таки есть. Т.е. значения хранятся в какой-нибудь хэш-таблице, и даже для того, чтобы проверить установлено ли локальное значение нужно искать в этой таблице по составному ключу — (объект + DP).
Конечно, свойства хранятся внутри хэш-таблицы, но мы вроде не об этом. С размером тоже не могу согласиться. Как-правило, 50% свойств — это ссылки на другие объекты или структуры, более сложные нежели элементарные типы.
S>Не говоря уже о цепочках (стили, паренты). Хотя я допускаю, что как раз цепочки делаются "присвоением", как ты говоришь. Тогда экономия памяти работает только на том, утановлено ли вообще значение или нет. Ну т.е. если значение установлено, то оно хранится в хэш-таблице, как описано выше. А если не установлено — то берется дефолтное из PropertyMetadata. Таким образом на дефолтные значения для каждого объекта память не тратится. S>Другими словами, я могу поверить в то что: сам DependencyObject поддерживает только два уровня: S>- дефолтное значение, хранящееся в PropertyMetadata (один раз для всех объектов) S>- и локальное значение, хранящееся для каждого объекта отдельно только в том случае, если оно установлено.
Примерно так и есть: либо дефолтное в метаданных, либо отдельное значение в паре с приоритетом. В пользу такой реализации говорит и наличие метода ClearValue. Почему вообще всплыла эта тема с экономией памяти? Dependency Property придумали не для экономии, а как некоторый селектор эффективного значения из разных источников.
Здравствуйте, MxMsk, Вы писали:
S>>Другими словами, я могу поверить в то что: сам DependencyObject поддерживает только два уровня: S>>- дефолтное значение, хранящееся в PropertyMetadata (один раз для всех объектов) S>>- и локальное значение, хранящееся для каждого объекта отдельно только в том случае, если оно установлено. MM>Примерно так и есть: либо дефолтное в метаданных, либо отдельное значение в паре с приоритетом.
Надо добавить, что есть еще один уровень: выражения. Реализация выражений — это BindingExpression и DynamicResourceExpression, которые обеспечивают работу привязки данных и динамических ссылок на ресурсы соответственно. В сущности, выражения используются как расширения источников значений для Dependency Property. Значения выражений при этом всё-равно кладутся по месту.
Здравствуйте, MxMsk, Вы писали:
MM>Гм. Я что-то перестаю понимать. Что означает "как сделано в WPF"? В WPF это вообще никак не сделано. Это же ортогональные вещи: базовая реализация свойств и ее использование.
Ну вот в WPF, я беру и спокойно наследюсь от какого-нибудь стандартного класса, например от Control. Добавляю туда новое DP. И оно у меня автоматически будет работать также как все остальные WPF свойства, т.е. значение будет подтягиваться из стиля, если оно там есть, и потом из парента и т.д. Мне же не нужно для этого в самом этом свойстве делать всякие CoerceValueCallback.
Вот я тоже самое хочу в моем примере. Чтобы была базовая система, т.е. чтобы все свойства которые я объявлю в транзакции (или кто-то другой объявит потом в наследнике от моей транзакции) сами собой подтягивались из сессии (или из наследника от моей сессии), без спец реализации для каждого свойства (без CoerceValueCallback).
MM>Примерно так и есть: либо дефолтное в метаданных, либо отдельное значение в паре с приоритетом. В пользу такой реализации говорит и наличие метода ClearValue.
Ну, то что хэш таблица там есть — это понятно. Я просто думал что она используется немного по другому. Типа ищем локальное значение по ключу (Control+DP); если не находится — ищем по ключу (Control.Style+DP), если не находится... ну и т.п. Ну и потом если ниче не нашлось, тогда берем default из метаданных. Тогда вот получается реальная экономия памяти.
MM>Почему вообще всплыла эта тема с экономией памяти?
Дык так везде написано, в том числе и в их же доках. Я понимаю, что это не единственное для чего их придумали.
MM>Dependency Property придумали не для экономии, а как некоторый селектор эффективного значения из разных источников.
Вот тут меня и клинит. Потому что, если это "некоторый селектор эффективного значения из разных источников", то почему же в DependencyObject нет методов с помощью которых можно было бы указать эти "разные источники" для данного объекта.
Здравствуйте, MxMsk, Вы писали:
MM>>Примерно так и есть: либо дефолтное в метаданных, либо отдельное значение в паре с приоритетом. MM>Надо добавить, что есть еще один уровень: выражения. Реализация выражений — это BindingExpression и DynamicResourceExpression, которые обеспечивают работу привязки данных и динамических ссылок на ресурсы соответственно. В сущности, выражения используются как расширения источников значений для Dependency Property. Значения выражений при этом всё-равно кладутся по месту.
Согласен. Биндинг, как часть базовой системы, имеющий свое собственное API, вполне может быть вшит в DependencyObject изнутри.
Только я одного не пойму: вот имеем мы в WPF какое-либо свойство, например, с типом int. Для установки значения, базовая система, т.е. DependencyObject, нам предоставляет только метод SetValue. Как ты говоришь WPF записывает значение с признаком (с флагом, откуда это значение взято). Значит WPF не может записать при помощи SetValue просто int, т.к. надо куда то сохранить и флаг. Куда флаг то сохраняется? Я конечно понимаю, что можно обернуть наш int в некую структуру, в которой будет два поля: наш int и флаг, и записать эту структуру при помощи SetValue. Тока, тогда (не говоря уже о том, что система не даст этого сделать, т.к. тип значения не совпадает с метаданными) GetValue вернет потом совсем не int...
Т.о. вопрос: куда и каким API базовой системы WPF сохраняет флаг?
Здравствуйте, stalcer, Вы писали:
MM>>Почему вообще всплыла эта тема с экономией памяти? S>Дык так везде написано, в том числе и в их же доках. Я понимаю, что это не единственное для чего их придумали.
Подразумевают, что в объекте присутствуют данные для тех свойств, которые действительно использовали. В FrameworkElement несколько десятков свойств, но фактически память для них будет выделяться только для тех, которым присвоят отличные от default значения. На объектах, у которых два-три свойства, особой экономии, естественно, не выйдет.
MM>>Dependency Property придумали не для экономии, а как некоторый селектор эффективного значения из разных источников. S>Вот тут меня и клинит. Потому что, если это "некоторый селектор эффективного значения из разных источников", то почему же в DependencyObject нет методов с помощью которых можно было бы указать эти "разные источники" для данного объекта.
Потому что "список источников" определен в WPF. Никто не обещал, что DependencyObject предоставляет безграничную свободу. Есть дюжина возможных источников, у каждого есть свой приоритет. Этим и предлагается пользоваться.
S>Согласен. Биндинг, как часть базовой системы, имеющий свое собственное API, вполне может быть вшит в DependencyObject изнутри.
Строго говоря, Binding не вшит в DependencyObject. Привязка данных реализована в PresentationFramework, как подвид выражений. Вот эти самые выражения и поддерживает DependencyObject.
S>Только я одного не пойму: вот имеем мы в WPF какое-либо свойство, например, с типом int. Для установки значения, базовая система, т.е. DependencyObject, нам предоставляет только метод SetValue. Как ты говоришь WPF записывает значение с признаком (с флагом, откуда это значение взято). Значит WPF не может записать при помощи SetValue просто int, т.к. надо куда то сохранить и флаг. Куда флаг то сохраняется? Я конечно понимаю, что можно обернуть наш int в некую структуру, в которой будет два поля: наш int и флаг, и записать эту структуру при помощи SetValue. Тока, тогда (не говоря уже о том, что система не даст этого сделать, т.к. тип значения не совпадает с метаданными) GetValue вернет потом совсем не int... S>Т.о. вопрос: куда и каким API базовой системы WPF сохраняет флаг?
Внутри себя DependencyObject использует такие структуры, как EntryIndex и EffectiveValueEntry. EntryIndex является ключом хэш-таблицы, а EffectiveValueEntry — значением. EffectiveValueEntry хранит в себе значение свойства и различные флаги, в том числе, если не ошибаюсь, при необходимости и ссылку на выражение. Соответственно, GetValue извлекает значение из поля EffectiveValueEntry.
Здравствуйте, MxMsk, Вы писали:
MM>Внутри себя DependencyObject использует такие структуры, как EntryIndex и EffectiveValueEntry. EntryIndex является ключом хэш-таблицы, а EffectiveValueEntry — значением. EffectiveValueEntry хранит в себе значение свойства и различные флаги, в том числе, если не ошибаюсь, при необходимости и ссылку на выражение. Соответственно, GetValue извлекает значение из поля EffectiveValueEntry.
Допускаю, что так оно и есть. Вопрос то не в этом. Еще раз: есть две подсистемы:
1) DependencyObject
2) WPF
Первая от второй не зависит. Так как (ты сам это подметил) первая находится в отдельной сборке. Следовательно, чтобы сохранить что-то в хэш-таблице, которая находится в первой подсистеме, вторая подсистема должна использовать какое-либо публичное API первой подсистемы.
Вопрос: При помощи какого API первой подсистемы вторая подсистема (т.е. WPF) кладет флаг (признак) того, что значение взято из, например, стиля?
Здравствуйте, stalcer, Вы писали:
S>Вопрос: При помощи какого API первой подсистемы вторая подсистема (т.е. WPF) кладет флаг (признак) того, что значение взято из, например, стиля?
Аааааа Ну, для этого используется сочетание области видимости internal с атрибутом InternalsVisibleTo. Засчет этого PresentationFramework "видит" в WindowsBase больше того, что видно нам. Но это внутренние механизмы — не стоит уделять им так много внимания.
Здравствуйте, MxMsk, Вы писали:
MM>Аааааа Ну, для этого используется сочетание области видимости internal с атрибутом InternalsVisibleTo. Засчет этого PresentationFramework "видит" в WindowsBase больше того, что видно нам. Но это внутренние механизмы — не стоит уделять им так много внимания.