2011-02-02 2 views
31

OneWayToSource Binding кажется сломана в .NET 4.0OneWayToSource Binding кажется сломанной в .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, чтобы потерять Focus, TextBox возвращается к значению для TextProperty (что означает «Нельзя использовать в привязке OneWayToSource»). Это перечитывание предназначено для привязки OneWayToSource в .NET 4.0? Я просто хочу, чтобы TextBox нажал его значение на TextProperty, а не наоборот.

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

Если да

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

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

ответ

9

блог Карла Shifflett в и @ ответ Simpzon уже рассмотрим, почему именно они добавили эту функцию, и почему это не является проблемой для свойств, которые всегда получают то, что было установлено. В вашем собственном коде вы всегда используете промежуточное свойство, которое имеет надлежащую семантику для привязки и использует внутреннее свойство, которое имеет семантику, которую вы хотите. Я бы назвал промежуточное свойство «блокирующим», потому что он блокирует getter от вашего внутреннего свойства.

Но в случае, если у вас нет доступа к исходному коду для объекта, на который вы устанавливаете свойство, и вы хотите использовать старое поведение, вы можете использовать конвертер.

Вот блокирующий преобразователь с государством:

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" ресурса.

+4

Спасибо за ваш ответ и конвертер, но я не согласен. Его ответ и блог Карла Шиффлетта объяснили, почему эта функция была добавлена ​​для каждого режима, кроме «OneWayToSource». Скажем, что я делаю некоторое преобразование, форматирование или что-то другое в настройщике, я все равно не хочу, чтобы мой «TextBox» обновлялся до этого значения. Они не только нарушили способ, которым это работало раньше (о котором я никогда не слышал, чтобы кто-то жаловался), он теперь тоже промахивается, потому что он больше не является «OneWayToSource». Это где-то посередине, как «TwoWay» без обновления уведомления об изменении –

+1

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

+1

Также, когда кто-нибудь когда-нибудь захочет этого поведения? Вы хотите, чтобы 'TextBox' вызывал get после того, как он был установлен из' TextBox', но не если свойство, если оно установлено из другого места? Не имеет смысла для меня .. –

4

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

, если это «особенность», это очень плохо один ...

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

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

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

+0

Мое предположение заключается в том, чтобы отражать любую фильтрацию, которую вы могли применить во время набора, так что содержимое TextBox фактически отражает значение, которое было «действительно» установлено. Единственное, что отличается от OneWayToSource, это то, что он будет вызывать только get() после применения set(), а не вызывать get() всякий раз, когда отправляется уведомление об изменении свойства. –

+0

На стороне примечания, свойство, которое ведет себя таким образом (оно предоставляет геттер, но get не имеет абсолютно никакого отношения к тому, что было установлено) является довольно патологическим свойством. Похоже, что вы действительно хотите использовать метод, но упакован как свойство, позволяющее системе Binding вызвать вызов. Возможно, стоит посмотреть на создание пользовательского поведения, так как вам не придется злоупотреблять системой свойств. –

+0

@ Дэн Брайант: Вы комментируете ответ Дэвида или отвечаете на вопрос? :) Я должен добавить, что если вы удалите getter, 'TextBox' всегда будет пустым каждый раз, когда« LostFocus »будет поднят. Несомненно, это не может быть желательным способом для привязки «OneWayToSource» или как вы думаете? –

6

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

Более подробно о функции здесь: http://karlshifflett.wordpress.com/2009/05/27/wpf-4-0-data-binding-change-great-feature/

+1

Это всего лишь пример перепрофилирования моей проблемы, у меня нет реализации, которая выглядит так. Кроме того, если я удаляю сеттер, «TextBox» всегда будет пустым каждый раз, когда вызывается «LostFocus». Мне все равно не подходит –

+0

В любом случае +1 для вашего объяснения и ссылки –

+1

Блог karlshifflett больше не существует. – SezMe

0

Является ли это желательным поведением для привязки 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; 
     } 
    } 
+0

Привет, Алекс и спасибо за ваш ответ. Я прочитаю подробности вашего ответа позже сегодня вечером, чтобы переварить. Использование функции «UpdateSourceTrigger = PropertyChanged», по-видимому, работает сначала, но при этом более пристальный взгляд показывает, что свойство «Text» для TextBox больше не соответствует отображаемому тексту, который является довольно странной ошибкой, вызванной этим поведением (TextBox .Text все равно скажет '' Не следует использовать в привязке OneWayToSource '', но отображаемый текст - все, что вы набираете). –

+0

Конечно, TextBox.Text показывает «Не следует ...», потому что вы ЗАДАЛИ его значение, поэтому вы принудительно привязываете значение READ, чтобы вызвать свойство getter (которое возвращает строку «Should not ...»). Фактически, если вы выполняете приложение в режиме отладки, вы увидите, что getter еще вызван после каждого сеттера, поэтому UpdateSourceTrigger не изменяет поведение 4.0, но обрабатывается пользовательским интерфейсом по-разному (TextBlock.Text не сбрасывается до предыдущего значения). Я полагаю, что в конечном итоге команда Microsoft исправит этот хак, поэтому я бы рекомендовал вам написать хорошо продуманный getter/setter вместо использования UpdateSourceTrigger. –

+0

Вы полностью пропустили мой комментарий. Конечно, я не имел в виду, что я использовал пропуски между свойством Text и отображаемым текстом в качестве обходного пути. И способ, которым я предполагаю использовать привязку 'OneWayTwoSource', не включает использование свойства как своего рода« преобразователь значений ». Но я был над этим во всех других ответах, поэтому не нужно повторять это снова.Кроме того, код в Вопросе есть только для того, чтобы воспроизвести мою проблему, и это не то, что я использую, но, тем не менее, это было неясно. –

2

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

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

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

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

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

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

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

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

0

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

Мое решение блокирует повторное чтение свойства резервного копирования, но обновит пользовательский интерфейс, когда он будет изменен каким-либо другим источником. Я основывал свое решение на блокирующем конвертере в ответе Рика Слэдки.

Он просто добавляет чек конвертеру, чтобы увидеть, будет ли поле 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 
    } 
} 

В моем конкретном USECASE для этого я имел длину и размер суффикс, хранящийся в текстовом поле (10м, 100мм и т.д.). Преобразователь проанализировал это на двойное значение или добавил суффикс (в зависимости от направления преобразования). Без конвертера он добавит суффикс для каждого обновления текстового поля. Попытка ввести «10» приведет к «1m0», так как конвертер будет работать после первого нажатия клавиши.

Смежные вопросы