Суть проблемы: пишется составной контрол — Slider + TextBox, оба элемента привязаны к двухсторонней привязкой к DependencyProperty "Value" этого контрола. Т.е. дергаем слайдер — меняется текст, вводим текст — перемещается слайдер. Соответственно к Value извне также привязывается свойство объекта, над которым мы все эти действия и производим. Внутри самого контрола значение корректируется через CoerceValueCallBack (выход за границы, количество знаков после запятой и т.д.), но вот наружу по биндингу прилетает значение не скорректированное. Получается что биндинг происходит раньше установки самого свойства и, соответственно, до вызова CoerceValueCallback! Как с этим бороться?
20.07.09 15:37: Перенесено модератором из '.NET' — AndrewVK
А>Суть проблемы: пишется составной контрол — Slider + TextBox, оба элемента привязаны к двухсторонней привязкой к DependencyProperty "Value" этого контрола. Т.е. дергаем слайдер — меняется текст, вводим текст — перемещается слайдер. Соответственно к Value извне также привязывается свойство объекта, над которым мы все эти действия и производим. Внутри самого контрола значение корректируется через CoerceValueCallBack (выход за границы, количество знаков после запятой и т.д.), но вот наружу по биндингу прилетает значение не скорректированное. Получается что биндинг происходит раньше установки самого свойства и, соответственно, до вызова CoerceValueCallback! Как с этим бороться?
Поищите в msdn "Dependency Property Value Precedence", почитайте, может понятнее будет.
А практически, когда в PropertyChangedCallback прилетает новое значение, там можно проверить, если значение свойства не скорректированное, то явно вызвать CoerceValue для этого свойства. Как-то так:
if (!DependencyPropertyHelper.GetValueSource(this, e.Property).IsCoerced)
{
CoerceValue(e.Property);
// return;
}
Re[2]: Binding в WPF
От:
Аноним
Дата:
21.07.09 03:45
Оценка:
Здравствуйте, notacat, Вы писали:
А>>Суть проблемы: пишется составной контрол — Slider + TextBox, оба элемента привязаны к двухсторонней привязкой к DependencyProperty "Value" этого контрола. Т.е. дергаем слайдер — меняется текст, вводим текст — перемещается слайдер. Соответственно к Value извне также привязывается свойство объекта, над которым мы все эти действия и производим. Внутри самого контрола значение корректируется через CoerceValueCallBack (выход за границы, количество знаков после запятой и т.д.), но вот наружу по биндингу прилетает значение не скорректированное. Получается что биндинг происходит раньше установки самого свойства и, соответственно, до вызова CoerceValueCallback! Как с этим бороться?
N>Поищите в msdn "Dependency Property Value Precedence", почитайте, может понятнее будет.
N>А практически, когда в PropertyChangedCallback прилетает новое значение, там можно проверить, если значение свойства не скорректированное, то явно вызвать CoerceValue для этого свойства. Как-то так:
N>
MSDN я читал, решения и даже намека на причины такой проблемы там нет. Скорее всего это не проблема DependencyProperty, а проблема биндинга. Получается, что биндинг либо происходит в обход промежуточных свойств (т.е. сразу от конечного источника к конечной цели), либо передает базовое значение. В самом источнике проверять значения очень не хотелось бы, тем более таким изощренным способом, тем более что он является оберткой над нативным кодом и наследоваться от DependencyObject возможности нет.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, notacat, Вы писали:
А>>>Суть проблемы: пишется составной контрол — Slider + TextBox, оба элемента привязаны к двухсторонней привязкой к DependencyProperty "Value" этого контрола. Т.е. дергаем слайдер — меняется текст, вводим текст — перемещается слайдер. Соответственно к Value извне также привязывается свойство объекта, над которым мы все эти действия и производим. Внутри самого контрола значение корректируется через CoerceValueCallBack (выход за границы, количество знаков после запятой и т.д.), но вот наружу по биндингу прилетает значение не скорректированное. Получается что биндинг происходит раньше установки самого свойства и, соответственно, до вызова CoerceValueCallback! Как с этим бороться?
А>MSDN я читал, решения и даже намека на причины такой проблемы там нет. Скорее всего это не проблема DependencyProperty, а проблема биндинга. Получается, что биндинг либо происходит в обход промежуточных свойств (т.е. сразу от конечного источника к конечной цели), либо передает базовое значение. В самом источнике проверять значения очень не хотелось бы, тем более таким изощренным способом, тем более что он является оберткой над нативным кодом и наследоваться от DependencyObject возможности нет.
А что именно не получается? Что имеется ввиду под "наружу по биндингу прилетает значение не скорректированное"? Наружу — это куда? Возможно стоит привести примеры кода.
Re: Binding в WPF
От:
Аноним
Дата:
21.07.09 09:04
Оценка:
Проблема приняла более ясные очертания.
В процессе написания примера для пояснения проблемы выяснилось что при TwoWay-биндинге корректируются только значения, которые посылаются от источника к приемнику. При изменении данных в приемнике, источник получает не скорректированное значение.
Код примера будет позже. Проблема остается.
А>MSDN я читал, решения и даже намека на причины такой проблемы там нет. Скорее всего это не проблема DependencyProperty, а проблема биндинга. Получается, что биндинг либо происходит в обход промежуточных свойств (т.е. сразу от конечного источника к конечной цели), либо передает базовое значение. В самом источнике проверять значения очень не хотелось бы, тем более таким изощренным способом, тем более что он является оберткой над нативным кодом и наследоваться от DependencyObject возможности нет.
Подождите, совсем ничего не понятно. А кто и где у вас вызывает CoerceValueCallback, если это даже не DependencyObject?
Тогда уж показывайте код
Re: Binding в WPF
От:
Аноним
Дата:
21.07.09 14:09
Оценка:
Возможно я плохо объяснил суть проблемы.
Вот обещанный код (это просто пример):
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = new Data {Value = 5};
}
}
public class Data
{
public decimal Value
{
get;
set;
}
}
В этом случае в TextBox`е мы видим значение не скорректированое (Slider выступает как цель привязки)
А>Что это, баг или фича?
На этот вопрос ответа не знаю. Скорей всего фича, если бы был баг, то его бы уже исправили.
A>И что теперь с этим делать?
Я советую в класс MySlider для ValueProperty добавить PropertyChangedCallBack и посмотреть, что там происходит. По идее, то, что я писала первый раз — в этом методе явно вызывать CoerceValue, должно сработать. Если этого почему-либо не хочется делать, то хотя бы в отладчике поизучаете, какое значение прилетает и откуда. DependencyPropertyHelper.GetValueSource(this, e.Property) вернет ValueSource, из которого это можно будет понять.
public static DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof (decimal),
typeof (MySlider),
new FrameworkPropertyMetadata((decimal) 0,
new PropertyChangedCallback(OnValueChanged),CoerceValueCallback));
private static object CoerceValueCallback(DependencyObject d, object basevalue)
{
return Math.Round((decimal) basevalue, 2);
}
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MySlider slider = d as MySlider;
ValueSource source = DependencyPropertyHelper.GetValueSource(slider, e.Property);
// вот тут можно поизучать source if (!source.IsCoerced)
{
slider.CoerceValue(e.Property);
}
}
Здравствуйте, Аноним, Вы писали:
А>Возможно я плохо объяснил суть проблемы. А>Вот обещанный код (это просто пример):
Да, не очень хорошо. Из описания казалось, что TextBox и Slider, оба, лежат в UserControl.
А>В этом случае в TextBox`е мы видим значение не скорректированое (Slider выступает как цель привязки) А>то видим то, что и ожидалось — значение округляется. В этом случае Slider — источник данных. А>Что это, баг или фича? И что теперь с этим делать?
Ну, это такая реализация DependencyProperty. Если поглядеть call stack при установке свойства в классе Data, то он выведет нас на DependencyObject.SetValueCommon. В этом методе мы увидим, что сначала MySlider проставляет значения через Binding:
...
bool flag2 = false;
if ((expression2 != null) && (expr == null))
{
if (flag)
{
value = ((DeferredReference) value).GetValue(BaseValueSourceInternal.Local);
flag = false;
}
flag2 = expression2.SetValue(this, dp, value); // здесь BindingExpression...
entryIndex = this.CheckEntryIndex(entryIndex, dp.GlobalIndex);
}
...
Правильно это или нет, баг или фича, я затрудняюсь ответить. Но сделано так. И в бете 4-го Framework тоже также. Поэтому, как вариант, привязаться к слайдеру. Или еще лучше, отказаться от двусторонней привязки внутри UserControl и реагировать на смену значения Slider через событие. И, кстати, для Slider не нужно указывать Mode=TwoWay, его свойство Value, аналогично TextBox.Text, в метаданных имеет флаг BindsTwoWayByDefault.
Здравствуйте, notacat, Вы писали:
N>Я советую в класс MySlider для ValueProperty добавить PropertyChangedCallBack и посмотреть, что там происходит. По идее, то, что я писала первый раз — в этом методе явно вызывать CoerceValue, должно сработать. Если этого почему-либо не хочется делать, то хотя бы в отладчике поизучаете, какое значение прилетает и откуда. DependencyPropertyHelper.GetValueSource(this, e.Property) вернет ValueSource, из которого это можно будет понять.
Этот подход не работает. Абсолютно ничего не меняется. Возможно после корректировки значения нужно принудительно обновлять источник, но сил и желания разбираться с этим уже нет.
Здравствуйте, MxKazan, Вы писали:
А>>Возможно я плохо объяснил суть проблемы. А>>Вот обещанный код (это просто пример): MK>Да, не очень хорошо. Из описания казалось, что TextBox и Slider, оба, лежат в UserControl.
На самом деле контрол еще сложнее, я просто выделил саму проблему
MK>Правильно это или нет, баг или фича, я затрудняюсь ответить. Но сделано так. И в бете 4-го Framework тоже также.
Это я уже и сам проверил. Подобная реализация вызывает огромное недоумение, был бы признателен, если б мне объяснили — почему так.
MK> Поэтому, как вариант, привязаться к слайдеру. Или еще лучше, отказаться от двусторонней привязки внутри UserControl и реагировать на смену MK>значения Slider через событие.
Если не найду другого способа, то придется реализовывать через события... или, скорее всего, через конвертер.
Я бы еще попробовала в классе MySlider убрать байндинг в xaml'e с элемента slider1, а из кода установить обратный байндинг. Т.е. не slider1.Value = 'байндинг к MySlider', а MySlider.Value = 'байндинг к slider1'
Re[5]: Binding в WPF
От:
Аноним
Дата:
22.07.09 02:42
Оценка:
Здравствуйте, notacat, Вы писали:
N>Я бы еще попробовала в классе MySlider убрать байндинг в xaml'e с элемента slider1, а из кода установить обратный байндинг. Т.е. не slider1.Value = 'байндинг к MySlider', а MySlider.Value = 'байндинг к slider1'
Я уже пытался это сделать, но получается что при установке привязки на MySlider.Value извне, привязка просто будет заменять внутреннюю, так как MySlider.Value выступает в обоих случаях как цель привязки. Сейчас попытаюсь разрулить эту ситуацию через MultiBinding и дополнительное private свойство.