Привязка OneWayToSource кажется сломанной в.NET 4.0
OneWayToSource
Связывание кажется нарушенным в.NET 4.0
У меня есть этот простой кусок Xaml
<StackPanel>
<TextBox Text="{Binding TextProperty, Mode=OneWayToSource}"/>
<Button/>
</StackPanel>
И мой код выглядит так
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private string m_textProperty;
public string TextProperty
{
get
{
return "Should not be used in OneWayToSource Binding";
}
set
{
m_textProperty = value;
}
}
В.NET 3.5 это работает, как вы могли бы, за исключением. Поместите некоторый текст в TextBox
нажмите Tab, чтобы он потерял фокус, и TextProperty
обновления с любым текстом, который был введен в TextBox
В.NET 4.0, если я наберу текст в TextBox
а затем нажмите клавишу Tab, чтобы он потерял фокус, TextBox
возвращается к значению для TextProperty
(что означает "Не следует использовать в привязке OneWayToSource"). Это перечитывание предназначено для OneWayToSource
Связывание в.NET 4.0? Я просто хочу TextBox
подтолкнуть его значение в TextProperty
и не наоборот.
Обновить
Добавление вознаграждения к этому вопросу, поскольку это стало неудобством мэра в моем проекте, и я хотел бы знать причину, по которой это изменилось. Кажется, что get
вызывается после того, как Binding обновил источник. Это желаемое поведение для OneWayToSource
Связывание в.NET 4.0?
Если да
- В чем была проблема с тем, как он работал в 3.5?
- В каких случаях это новое поведение лучше?
Или это на самом деле ошибка, которую мы можем надеяться исправить в следующем выпуске?
6 ответов
Блог Карла Шиффлетта и ответ @Simpzon уже рассказывают, почему они добавили эту функцию и почему это не проблема для свойств, которые всегда получают то, что было установлено. В вашем собственном коде вы всегда используете промежуточное свойство, которое имеет правильную семантику для связывания, и используете внутреннее свойство, которое имеет желаемую семантику. Я бы назвал промежуточное свойство "блокирующим" свойством, потому что оно блокирует геттер от достижения вашего внутреннего свойства.
Но в случае, когда у вас нет доступа к исходному коду для объекта, для которого вы устанавливаете свойство, и вы хотите использовать старое поведение, вы можете использовать конвертер.
Вот блокирующий конвертер с состоянием:
public class BlockingConverter : IValueConverter
{
public object lastValue;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return lastValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
lastValue = value;
return value;
}
}
и вы можете использовать его для своего примера, как это:
<Grid>
<Grid.Resources>
<local:BlockingConverter x:Key="blockingConverter" x:Shared="False"/>
</Grid.Resources>
<StackPanel>
<TextBox Text="{Binding TextProperty, Mode=OneWayToSource, Converter={StaticResource blockingConverter}}"/>
<Button Content="Click"/>
</StackPanel>
</Grid>
Обратите внимание, что, поскольку конвертер имеет состояние, вам нужен отдельный экземпляр при каждом использовании ресурса, и для этого мы можем использовать x:Shared="False"
атрибут на ресурсе.
Это действительно по замыслу. Обычно это не должно создавать проблем, но я бы сказал, что реализация вашей собственности по крайней мере нетрадиционна. Методы получения и установки (методы доступа) должны действительно не намного больше, чем получить и установить, и каждое получение должно соответствовать последнему соответствующему набору. (извините, источника для этого нет, это просто то, что мы назвали хорошей гражданственностью во всех командах разработчиков, в которых я был).
Подробнее об этой функции можно узнать здесь: http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/
Ошибка, безусловно.
если это "фича", то очень плохая...
кажется, что они добавили вызов функции get() после завершения set(), даже в режиме OneWayToSource... кто-нибудь может объяснить почему?
также, спасибо за указание на это, это объясняет проблему, с которой я столкнулся с тех пор, как обновил свой проект до.net 4.0, и которую я не мог объяснить до сих пор...
только примечание стороны: я решил это, используя свойства зависимости в конце.
Это довольно явно ошибка. Похоже, что кто-то сообщил об этом хотя бы один раз. https://connect.microsoft.com/VisualStudio/feedback/details/612444/onewaytosource-broken-in-net-4-0
Я согласен со многими другими, что односторонний период должен быть односторонним.
Мой сценарий сложен, и изменение функциональности между версиями фреймворка вызвало у меня настоящую головную боль.
У меня есть текстовое поле, привязанное к свойству. У меня есть конвертер, который меняет формат на лету. Например, я ввожу EU5 в текстовое поле, свойство получает EU005. У меня есть набор привязок для запуска свойства изменен, так как мне нужно делать поиск в ViewModel, как пользовательские типы. Новая реализация изменяет значение текстового поля по мере ввода текста. Поэтому, если я хочу набрать EU512, я не смог бы легко, так как текст текстового поля будет продолжать меняться.
Я пробовал много вещей - явное связывание (где вы решаете, когда обновлять и каким образом.) Это имеет ту же проблему. Если я говорю, UpdateSource, он делает, но также затем перечитывает свойство и изменяет цель тоже.
Я пробовал OneWayToSource и имел ту же проблему. Я не нашел способа обойти это без внесения досадных изменений в мою виртуальную машину. Единственным другим способом было бы удалить привязку на этом поле и начать запускать события, которые были бы ужасны.
Если MS заставит привязку вести себя так, как ее называют логически, моя проблема исчезнет. Даже свойство привязки отказаться от реализации.net4 и вести себя как 3.5 будет работать для меня.
У кого-нибудь есть предложения для меня, как я могу обойти это?
Это желаемое поведение для привязки OneWayToSource в.NET 4.0?
Да. Это сделано для того, чтобы разработчик мог изменять предоставленное значение без неуклюжих конвертеров.
В чем была проблема с тем, как он работал в 3.5?
Нет проблем. То, как это работало в 3.5, не позволило исправить приведенные значения.
В каких случаях это новое поведение лучше?
Когда вам нужно исправить предоставленные значения. Если вам это не нужно, то вам просто нужно написать правильные свойства getter и setter.
public string TextProperty
{
get;
set;
}
Однако, как я вижу, изменение UpdateSourceTrigger на "PropertyChanged" предохраняет значения от перечитывания (так что вы можете оставить старое объявление свойства):
<StackPanel>
<TextBox Text="{Binding TextProperty, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"/>
<Button/>
</StackPanel>
private string m_textProperty;
public string TextProperty
{
get
{
return "Should not be used in OneWayToSource Binding";
}
set
{
m_textProperty = value;
}
}
У меня был вариант этой проблемы для двухстороннего связывания. Я понимаю, что это не совсем то же самое, что и обсуждаемая проблема, но этот ответ вышел на первое место во время поиска, и это привело к моему решению. Надеюсь, кто-то найдет это полезным.
Мое решение блокирует перечитывание свойства backing, но обновит интерфейс, когда он будет изменен каким-либо другим источником. Я основал свое решение на блокирующем конвертере в ответе Рика Сладки.
Это просто добавляет проверку к преобразованию, чтобы увидеть, если lastValue
поле будет преобразовано в то же значение резервного хранилища. Если нет, значение резервного хранилища должно быть изменено из другого источника, а пользовательский интерфейс должен быть обновлен.
public class MyConverter : IValueConverter
{
public object lastValue;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (LastValue != null && MyConvertBack(LastValue).Equals(value))
return lastValue;
else
return MyConvert(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
lastValue = value;
return MyConvertBack(value);
}
private object MyConvertBack(Object value)
{
//Conversion Code Here
}
private object MyConvert(Object value)
{
//Conversion Code Here
}
}
В моем конкретном случае использования для этого я имел суффикс длины и размера, хранящийся в текстовом поле (10 м, 100 мм и т. Д.). Преобразователь проанализировал это до двойного значения или добавил суффикс (в зависимости от направления преобразования). Без конвертера это добавило бы суффикс к каждому обновлению текстового поля. Попытка ввести "10" приведет к "1m0", поскольку преобразователь будет работать после первого нажатия клавиши.