2010-12-04 10 views
9

Tengo datos que regresan del servicio web en forma de ObservableCollection<string> Quiero vincular la colección a TextBox de solo lectura para que el usuario pueda seleccionar y copiar los datos en el portapapeles.Vinculable ObservableCollection <> a un TextBox

Para obtener la colección vinculada a la propiedad Text del TextBox Creé IValueConverter que convierte la colección en una cadena de texto. Esto parece funcionar, excepto que solo funciona una vez, es como si el enlace no reconociera los cambios posteriores a la colección Observable. Aquí hay una aplicación simple que reproduce el problema, solo para confirmar que el enlace funciona correctamente también me enlace a un `ListBox '

¿Esto se debe a que el enlace de texto simple no maneja los eventos de cambio de la colección?

Una opción sería, por supuesto, manejar los cambios de colección y propocionarlos a una propiedad Text a la que está vinculado el TextBox, lo cual está bien, pero me gustaría entender por qué lo que me pareció obvio soluciones no está funcionando como se esperaba.

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> 

Código Detrás

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(); 
    } 
    } 
} 
+0

Es posible que debas generar un evento de propiedad modificada para que el cuadro de texto se actualice. – ChrisF

+0

@ChrisF, gracias. Esperaba que las notificaciones de cambio se propagaran al TextBox a través del enlace. –

Respuesta

5

Es esto debido a la unión sencilla no maneja los eventos de cambio de la colección de texto?

Indeed. Un enlace se actualiza solo cuando cambia su fuente propiedad. Si cambia la propiedad TextLines configurando un nuevo ObservableCollection e implementa INotifyPropertyChanged, su enlace funcionará como se esperaba. Agregar nuevos elementos a la colección solo tendrá sentido si está vinculado a una propiedad como ItemsControl.ItemsSource que escucha los cambios en la colección.

Una opción sería, por supuesto para mí para manejar los cambios de recolección y propagar los a una propiedad de texto que el cuadro de texto está limitado a, que es bien.

Esa sería otra solución.

+0

Gracias Julien, tenía la esperanza de que me faltaba algo y esto funcionaría. –

0

actualización continuación Código

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

donde txtName es el nombre de su campo respectivo

MVVM manera

1- Difine una propiedad de tipo cadena en su modelo de vista como se muestra a continuación y se unen esta propiedad para la propiedad de texto del cuadro de texto a se muestra a continuación y eliminar ValueConverter no es necesario ahora.

public string TextLines {get;set;} 

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

2- creo, lo más probable es el manejo de eventos de clic de botón utilizando un controlador de comandos diga el comando es AddMoreLines

así en el controlador de comandos AddMoreLine, después de añadir un nuevo objeto en su OBservrableCollection, crear una StringBuilder y anexe todo el contenido de su Colección y asigne la cadena a la propiedad creada en el paso 1.

3- Llamar PropertyChanged Handler.

+0

gracias. En la aplicación real, la actualización está ocurriendo en el Modelo de visualización, y el Modelo de vista no tiene conocimiento de la vista y qué controles están vinculados a las propiedades. Por lo tanto, no puedo aplicar este enfoque directamente. Lo siento, mi reproducción simple del problema no mostró este lado del diseño. –

+0

normalmente lo hago de una manera que planteo un evento de mi vm y he visto mi tipo de hack y crea un enlace entre mi vista y ViewModel PERO en algún momento no es posible cumplir 100% con MVVM y puedo decirte uno ejemplo clásico como el manejo de operaciones de nivel celular de DataGrid. PERO en su caso existe la posibilidad de no romper el MVVM, si lo desea puedo mostrarle cómo hacerlo. – TalentTuner

4

Una forma un poco más elegante de lograr eso es usar MultiBinding en la propiedad Text y enlazar a la propiedad Count de Collection. Esto actualizará el enlace cada vez que el recuento de la colección cambie y actualice el texto de acuerdo con un MultiValueConverter que usted defina.

<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> 

y el convertidor:

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(); 
    } 
} 

En mi caso de uso, no permito que el cuadro de texto para actualizar su fuente (de ahí el'Mode = "OneWay" '), pero si es necesario el método ConvertBack del convertidor manejaría eso.

Cuestiones relacionadas