Привязка ObservableCollection <> к TextBox - PullRequest
9 голосов
/ 04 декабря 2010

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

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

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

Один из вариантов, конечно, для меня - обработать изменения коллекции и распространить их на свойство Text, с которым связан 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 ]

5 голосов
/ 13 января 2012

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

<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 преобразователя будет обрабатывать это.

5 голосов
/ 04 декабря 2010

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

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

Один из вариантов, конечно, будет для меня, чтобы обрабатывать изменения коллекции иПрисвойте им свойство Text, с которым связан TextBox, и это нормально.

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

0 голосов
/ 04 декабря 2010

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

  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 - различайте свойство типастрока в вашей ViewModel, как показано ниже, и привязать это свойство к текстовому свойству textbox a, как показано ниже, и теперь не нужно удалять ValueConverter.

public string TextLines {get;set;}

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

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

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

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

...