Как использовать двухстороннее связывание данных, а не однозначное преобразование? - PullRequest
0 голосов
/ 05 февраля 2020

У меня есть TextBox, который показывает поле из модели данных. Тип поля - Список строк.

Я добавляю класс View Model, содержащий класс данных и реализующий интерфейсы INotifyPropertyChanged и IDataErrorInfo. View Model содержит свойство string PropName, и я связываю TextBox.Text с PropName в двух направлениях. PropName setter и getter преобразуют строку в List<string> и обратно. Они используют пробелы в качестве разделителей для разделения строки на части. Кроме того, у меня есть проверка для проверки строки, и я хочу, чтобы она работала сразу после изменения текста, поэтому я использую UpdateSourceTrigger="PropertyChanged" для немедленной проверки. Как это работает?

Рассмотрим пример. Я ожидаю, что пользователь вводит слово, затем разделитель, затем слово снова, и оно преобразуется в List<string>. Сразу после добавления пользователем разделителя кто-то выполняет сохранение значения в источнике, и происходит преобразование, которое обрезает последний разделитель как бесполезный. После этого кто-то снова читает свойство (я пытался не вызывать INotifyPropertyChanged.PropertyChanged, но безуспешно), и последний разделитель исчезает. Если пользователь вставляет разделитель в слово в середине, все работает нормально.

<TextBox>
    <TextBox.Text>
        <Binding Path="PropName" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <DataErrorValidationRule/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

class DataModel
{
    public List<string> PropName{get;set;}
};

class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private DataModel data;
    public string PropName
    {
        get { return data.PropName.Join(' '); }
        set
        { 
            data.PropName = value.Split(' ', StringSplitOptions.RemoveEmptyEntries);
            RaisePropertyChanged();// I can don't call it. It doesn't affect.
        }
    }
};

1 Ответ

1 голос
/ 05 февраля 2020

Во-первых, вы не должны реализовывать модель представления для просмотра преобразования внутри модели представления.
Для этого сценария вы должны внедрить IValueConverter (см. Документы Microsoft: преобразование данных ).

Чтобы решить вашу проблему, у вас есть два варианта: использовать Binding.UpdateSourceTrigger по умолчанию TextBox, то есть UpdateSourceTrigger.LostFocus, или отменить обновление привязки, если значение TextBox.Text заканчивается указанным c разделитель.

Рекомендую вернуться к значению UpdateSourceTrigger.LostFocus. Нет смысла проверять ввод для каждого символа. UpdateSourceTrigger.LostFocus имеет смысл в сценарии ios, например, при проверке орфографии или при наборе текста.

В следующем примере показано, как использовать правильное преобразование значения и подавить привязку:

ViewModel.cs

class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
  private DataModel Data { get; set; }

  private List<string> propName;
  public List<string> PropName
  {
    get => this.Data;
    set
    { 
      this.Data.PropName = value;
      RaisePropertyChanged();
    }
  }
}

ListToStringConverter.cs

[ValueConversion(typeof(List<string>), typeof(string))]
public class ListToStringConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    return value is IEnumerable<string> stringCollection 
      ? string.Join(";", stringCollection)
      : Binding.DoNothing;
  }

  public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    string[] stringSeparators = new string[] {";", " "};
    return value is string stringValue && stringValue.LastIndexOfAny(stringSeparators) < stringValue.Length - 1 
      ? stringValue.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries).ToList() 
      : Binding.DoNothing;
  }
}

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <TextBox>
    <TextBox.Text>
      <Binding Path="PropName" UpdateSourceTrigger="PropertyChanged">
        <Binding.Converter>
          <ListToStringConverter />
        </Binding.Converter>
        <Binding.ValidationRules>
          <DataErrorValidationRule />
        </Binding.ValidationRules>
      </Binding>
    </TextBox.Text>
  </TextBox>
</Window>
...