Привязка OneWayToSource кажется нарушенной в .NET 4.0 - PullRequest
32 голосов
/ 02 февраля 2011

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 Binding в .NET 4.0?Я просто хочу, чтобы TextBox подтолкнул его значение в TextProperty, а не наоборот.

Обновление
Добавление награды к этому вопросу, поскольку это сталоМэр неудобства в моем проекте, и я хотел бы знать причину, по которой это изменилось.Кажется, что get вызывается после того, как Binding обновил источник.Является ли это желаемым поведением для OneWayToSource Binding в .NET 4.0?

Если да

  • В чем была проблема с тем, как он работал в 3.5?
  • В каких случаях это новое поведение лучше?

Или действительно ли это ошибка, которую мы можем надеяться исправить в следующем выпуске?

Ответы [ 6 ]

9 голосов
/ 05 февраля 2011

Блог Карла Шиффлетта и ответ @ 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" для ресурса.

6 голосов
/ 04 февраля 2011

Это действительно задумано. Обычно это не должно создавать проблем, но я бы сказал, что реализация вашей собственности по крайней мере нетрадиционна. Получатели и установщики (средства доступа) должны действительно делать не больше, чем получать и устанавливать, и каждое получение должно соответствовать последнему соответствующему набору. (извините, источника для этого нет, это просто то, что мы назвали хорошей гражданственностью во всех командах разработчиков, в которых я участвовал).

Подробнее об этой функции можно узнать здесь: http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/

4 голосов
/ 02 февраля 2011

Ошибка, определенно.

если это "особенность", то она очень плохая ...

кажется, что они добавили вызов функции get () после завершения set (), даже в режиме OneWayToSource ... кто-нибудь может объяснить, почему?

также, спасибо за указание на это, он объясняет проблему, возникшую у меня с тех пор, как я обновил свой проект до .net 4.0, и которую я не мог объяснить до сих пор ...

только примечание: я решил это, используя свойства зависимостей в конце.

2 голосов
/ 14 июля 2011

Это явно ошибка.Похоже, что кто-то сообщил об этом хотя бы один раз.https://connect.microsoft.com/VisualStudio/feedback/details/612444/onewaytosource-broken-in-net-4-0

Я согласен со многими другими, что односторонний период должен быть односторонним.

Мой сценарий сложен, и изменение функциональности между версиями платформы вызвало у меня настоящую головную боль.

У меня есть текстовое поле, привязанное к свойству.У меня есть конвертер, который меняет формат на лету.Например, я ввожу EU5 в текстовое поле, свойство получает EU005.У меня есть набор привязки для запуска свойства изменен, так как мне нужно делать поиск в ViewModel, как пользовательские типы.Новая реализация изменяет значение текстового поля по мере ввода текста.Поэтому, если я хочу набрать EU512, я не смог бы легко, так как текст текстового поля будет продолжать меняться.

Я пробовал много вещей - явное связывание (где вы решаете, когда обновлять и каким образом.) Это имеет то же самоепроблема.Если я говорю, UpdateSource, он делает, но также перечитывает свойство и тоже меняет цель.

Я пробовал OneWayToSource и у меня была та же проблема.Я не нашел способа обойти это без внесения досадных изменений в мою виртуальную машину.Единственным другим способом было бы удалить привязку в этом поле и начать запускать события, которые были бы ужасны.

Если бы MS заставляла привязку вести себя так, как ее называли логически, моя проблема исчезла бы.Даже свойство привязки, позволяющее отказаться от реализации .net4 и вести себя как 3.5, сработало бы для меня.

У кого-нибудь есть какие-либо предложения для меня, как мне обойти это?

0 голосов
/ 31 октября 2012

У меня был вариант этой проблемы для двухсторонней привязки. Я понимаю, что это не совсем то же самое, что и обсуждаемая проблема, но этот ответ вышел на первое место во время поиска, и это привело к моему решению. Надеюсь, кто-то найдет это полезным.

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

0 голосов
/ 07 февраля 2011

Это желаемое поведение для привязки 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;
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...