Wpf 绑定可观察集合<&燃气轮机;到文本框

Wpf 绑定可观察集合<&燃气轮机;到文本框,wpf,silverlight,data-binding,ivalueconverter,Wpf,Silverlight,Data Binding,Ivalueconverter,我以可观察集合的形式从web服务返回数据我想将集合绑定到只读文本框,以便用户可以选择并将数据复制到剪贴板 要将集合绑定到我创建的文本框的Text属性,请使用IValueConverter,该文本框将集合转换为文本字符串。这似乎是可行的,只是它只工作一次,就好像绑定不识别可观察集合的后续更改一样。下面是一个简单的应用程序,它重现了这个问题,只是为了确认绑定是否正常工作,我还绑定了一个“ListBox” 这是因为文本绑定简单不处理集合的更改事件吗 当然,我可以选择处理集合更改,并将这些更改传播到Te

我以
可观察集合的形式从web服务返回数据
我想将集合绑定到只读
文本框
,以便用户可以选择并将数据复制到剪贴板

要将集合绑定到我创建的文本框的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();
    }
  }
}
使用系统;
使用系统集合;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用系统文本;
使用System.Windows;
使用System.Windows.Data;
利用制度全球化;
命名空间WpfTextBoxBinding
{
/// 
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
公共ObservableCollection文本行{get;set;}
公共主窗口()
{
DataContext=this;
TextLines=新的ObservableCollection();
//添加一些初始数据,这表明
//文本框绑定第一次起作用
文本行。添加(“第一行”);
初始化组件();
}
私有无效按钮\u单击(对象发送者,路由目标e)
{
TextLines.Add(“Line:+TextLines.Count”);
}
}
公共类EnumarableToTextConverter:IValueConverter
{
公共对象转换(
对象值,类型targetType,
对象参数,CultureInfo(区域性)
{
if(值为IEnumerable)
{
StringBuilder sb=新的StringBuilder();
foreach(值为IEnumerable的var s)
{
某人追加(s.ToString());
}
使某人返回字符串();
}
返回字符串。空;
}
公共对象转换回(
对象值,类型targetType,
对象参数,CultureInfo(区域性)
{
抛出新的NotImplementedException();
}
}
}
这是因为文本绑定吗 simple不处理更改 收藏活动

的确如此。绑定仅在其源属性更改时更新。如果您通过设置一个全新的
ObservableCollection
来更改
TextLines
属性,并实现
INotifyPropertyChanged
,您的绑定将按预期工作。只有当集合绑定到侦听集合更改的属性(如
ItemsControl.ItemsSource
)时,向集合添加新元素才有意义

我当然有一个选择 处理集合更改和 将它们传播到文本属性 文本框绑定到的,即 好的

这将是另一个解决方案。

更新下面的代码

  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中定义string类型的属性,并将此属性绑定到下图所示的textbox文本属性a,无需立即删除ValueConverter

public string TextLines {get;set;}

 <TextBox Text="{Binding TextLines, Mode=OneWay/> 
公共字符串文本行{get;set;}

实现这一点的一种稍微优雅的方法是对Text属性使用MultiBinding并绑定到集合的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();
    }
}
公共静态类转换器
{
公共静态LogEntryCollectionToTextConverter LogEntryCollectionToTextConverter=新LogEntryCollectionToTextConverter();
}
公共类LogEntryCollectionToTextConverter:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
ObservableCollection logEntries=作为ObservableCollection的值[0];
if(logEntries!=null&&logEntries.Count>0)
返回logEntries.ToString();
其他的
返回字符串。空;
}
公共对象[]转换回(对象值,类型[]目标类型,对象参数,CultureInfo区域性)
{
抛出新的NotImplementedException();
}
}

在我的用例中,我不允许文本框更新其源代码(因此使用“Mode=”OneWay“'),但如果需要,转换器的ConvertBack方法将处理该问题。

您可能需要引发一个属性更改事件以更新文本框。@ChrisF,谢谢。我希望更改通知能通过绑定发送到文本框中。谢谢Julien,我希望我遗漏了一些东西,这会起作用。谢谢。在实际应用程序中,更新发生在视图模型中,视图模型不知道视图以及哪些控件绑定到属性。因此,我不能直接采用这种方法。很抱歉,我对这个问题的简单复制没有显示出设计的这一面。通常我会在我的视图上从我的vm和handel引发一个事件,这是一种黑客行为,并在我的视图和ViewModel之间创建一个绑定,但有时它不可能与MVVM 100%兼容,我可以告诉你一个典型的例子,比如处理DataGrid cell一级操作。但是在你的情况下,有可能不破坏MVVM,如果你想的话,我可以告诉你怎么做。