Привязка ObservableCollection к текстовому окну

у меня есть данные возвращается из веб-службы в виде ObservableCollection<string> Я хочу привязать коллекцию к только для чтения TextBox чтобы пользователь мог выбрать и скопировать данные в буфер обмена.

чтобы получить коллекцию, привязанную к свойству Text текстового поля, которое я создал IValueConverter, который преобразует коллекцию в текстовую строку. Кажется, что это работает, за исключением того, что он работает только один раз, как будто привязка не распознает последующие изменения наблюдаемой коллекции. Вот простое приложение, которое воспроизводит проблему, просто чтобы подтвердить, что привязка работает правильно, я также привязываюсь к "ListBox"

это потому, что текст привязки просто не обрабатывает события изменения коллекции?

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

XAML

<Window x:Class="WpfTextBoxBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfTextBoxBinding"
        Title="MainWindow" Height="331" Width="402">
  <StackPanel>
    <StackPanel.Resources>
      <local:EnumarableToTextConverter x:Key="EnumarableToTextConverter" />
    </StackPanel.Resources>
    <TextBox Text="{Binding TextLines, Mode=OneWay, Converter={StaticResource EnumarableToTextConverter}}" Height="100" />
    <ListBox ItemsSource="{Binding TextLines}" Height="100" />
    <Button Click="Button_Click" Content="Add Line" />
  </StackPanel >
</Window>

Код

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Globalization;

namespace WpfTextBoxBinding
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public ObservableCollection<string> TextLines {get;set;}

    public MainWindow()
    {
      DataContext = this;

      TextLines = new ObservableCollection<string>();

      // Add some initial data, this shows that the 
      // TextBox binding works the first time      
      TextLines.Add("First Line");

      InitializeComponent();      
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
      TextLines.Add("Line :" + TextLines.Count);
    }
  }

  public class EnumarableToTextConverter : IValueConverter
  {
    public object Convert(
      object value, Type targetType, 
      object parameter, CultureInfo culture)
    {
      if (value is IEnumerable)
      {
        StringBuilder sb = new StringBuilder();
        foreach (var s in value as IEnumerable)
        {
          sb.AppendLine(s.ToString());
        }
        return sb.ToString();
      }
      return string.Empty;
    }

    public object ConvertBack(
      object value, Type targetType, 
      object parameter, CultureInfo culture)
    {
      throw new NotImplementedException();
    }
  }
}

3 ответов


это потому что привязка текста просто не обрабатывает изменения события коллекции?

действительно. Привязка обновляется только тогда, когда ее источник свойства изменения. Если вы измените TextLines свойство, установив совершенно новый ObservableCollection и реализовать INotifyPropertyChanged ваша привязка будет работать, как ожидалось. Добавление новых элементов в коллекцию будет иметь значение только в том случае, если оно связано с таким свойством, как ItemsControl.ItemsSource который слушает коллекцию изменения.

один вариант, конечно, будет для меня для обработки изменений коллекции и propogate те к свойству Text что TextBox привязан к, который штраф.

Это было бы другое решение.


немного более элегантный способ добиться этого-использовать MultiBinding для свойства Text и привязать к свойству Count коллекции. Это обновит привязку при каждом изменении количества коллекции и обновит текст в соответствии с определяемым вами многозначным преобразователем.

<TextBox>
    <TextBox.Text>
        <MultiBinding Converter="{x:Static l:Converters.LogEntryCollectionToTextConverter}">
            <Binding Path="LogEntries" Mode="OneWay"/>
            <Binding Path="LogEntries.Count" Mode="OneWay" />
        </MultiBinding>
    </TextBox.Text>
</TextBox>

и конвертер:

public static class Converters
{
    public static LogEntryCollectionToTextConverter LogEntryCollectionToTextConverter = new LogEntryCollectionToTextConverter();
}

public class LogEntryCollectionToTextConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        ObservableCollection<LogEntry> logEntries = values[0] as ObservableCollection<LogEntry>;

        if (logEntries != null && logEntries.Count > 0)
            return logEntries.ToString();
        else
            return String.Empty;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

в моем случае использования я не позволяю TextBox обновлять его источник( следовательно, Mode= "OneWay"), но если нужно, метод ConvertBack конвертера справлюсь с этим.


обновление ниже код

  private void Button_Click(object sender, RoutedEventArgs e)
    {
        TextLines.Add("Line :" + TextLines.Count);
      BindingExpression be =  BindingOperations.GetBindingExpression(txtName, TextBox.TextProperty);
      be.UpdateTarget();
    } 

где txtName ваше имя поле

MVVM путь

1-Difine свойство типа string в ViewModel, как показано ниже, и привязать это свойство к текстовому свойству textbox, как показано ниже,и удалить ValueConverter нет необходимости.

public string TextLines {get;set;}

 <TextBox Text="{Binding TextLines, Mode=OneWay/> 

2-я думаю, вы, скорее всего, обрабатывая событие нажатия кнопки с помощью обработчика команд, говорите, что ваша команда-AddMoreLines

Так, в обработчик команд AddMoreLine после добавления нового объекта в коллекцию OBservrableCollection создает StringBuilder, добавляет все содержимое коллекции и назначает строку свойству, созданному на шаге 1.

3-Вызов Обработчика PropertyChanged.