Привязка 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", поскольку преобразователь будет работать после первого нажатия клавиши.

Другие вопросы по тегам