绑定ObservableCollection<>到TextBox

时间:2010-12-04 11:01:41

标签: wpf silverlight data-binding ivalueconverter

我有一个以ObservableCollection<string>形式从Web服务返回的数据我想将该集合绑定到只读TextBox,以便用户可以选择并将数据复制到剪贴板

要将集合绑定到我创建的TextBox的Text属性IValueConverter,它将集合转换为文本字符串。这似乎有效,除了它只工作一次,就好像绑定不能识别对Observable集合的后续更改。这是一个简单的应用程序,可以重现问题,只是为了确认绑定是否正常工作我还绑定到`ListBox'

这是因为Text binding simple不处理集合的更改事件吗?

一个选项当然是我处理集合更改并将它们传播到TextBox绑定的Text属性,这很好,但我想理解为什么在我看来这是一个明显的解决方案没有按预期工作。

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 个答案:

答案 0 :(得分:5)

  

这是因为Text绑定   简单不处理变化   集合的事件?

事实上。绑定仅在其源属性更改时更新。如果您通过设置全新TextLines并实施ObservableCollection来更改INotifyPropertyChanged属性,那么您的绑定将按预期工作。只有将新元素绑定到监听集合更改的ItemsControl.ItemsSource之类的属性时,才能将新元素添加到集合中。

  

一种选择当然适合我   处理集合的变化和   将它们传播给Text属性   TextBox绑定的,即   细

那将是另一种解决方案。

答案 1 :(得分:5)

更优雅的方法是在Text属性上使用MultiBinding并绑定到Collection的Count属性。这将在每次集合的Count更改时更新绑定,并根据您定义的MultiValueConverter更新Text。

<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更新其源代码(因此'Model =“OneWay”'),但如果需要,转换器的ConvertBack方法将处理它。

答案 2 :(得分:0)

更新以下代码

  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中字符串类型的属性,如下所示,并将此属性绑定到下面显示的文本框文本属性,并立即删除ValueConverter。

public string TextLines {get;set;}

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

2-我认为,你很可能使用命令处理程序处理按钮点击事件,说你的命令是AddMoreLines

所以在AddMoreLine命令处理程序中,在OBservrableCollection中添加一个新对象后,创建一个StringBuilder并附加Collection的所有内容,并将该字符串分配给在步骤1中创建的属性。

3-调用PropertyChanged Handler。