2010-12-04 17 views
9

J'ai des données revenant du service Web sous la forme d'un ObservableCollection<string> Je veux lier la collection à un TextBox en lecture seule afin que l'utilisateur puisse sélectionner et copier les données dans le presse-papiers.Liaison ObservableCollection <> à un TextBox

Pour obtenir la collection liée à la propriété Text de la zone de texte, j'ai créé IValueConverter qui convertit la collection en une chaîne de texte. Cela semble fonctionner, sauf que cela ne fonctionne qu'une seule fois, c'est comme si la liaison ne reconnaissait pas les modifications ultérieures de la collection Observable. Voici une application simple qui reproduit le problème, juste pour confirmer que la liaison fonctionne correctement. Je lie également à un ListBox

Est-ce parce que la liaison de texte simple ne gère pas les événements de changement de la collection? Une option serait bien sûr pour moi de gérer les changements de collection et de les propager à une propriété Text à laquelle le TextBox est lié, ce qui est bien, mais je voudrais comprendre pourquoi ce qui me semblait être une évidence les solutions ne fonctionnent pas comme prévu.

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> 

code Derrière

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

Vous devrez peut-être déclencher un événement de modification de propriété pour la zone de texte à mettre à jour. – ChrisF

+0

@ChrisF, merci. J'espérais que les notifications de changement seraient propagées au TextBox via la liaison. –

Répondre

5

Est-ce parce que le texte contraignant simple ne gère pas les changements événements de la collection?

En effet. Une mise à jour de liaison uniquement lorsque sa propriété source change. Si vous modifiez la propriété TextLines en définissant un nouveau ObservableCollection et implémentez INotifyPropertyChanged, votre liaison fonctionnera comme prévu. L'ajout de nouveaux éléments à la collection n'aura de sens que s'il est lié à une propriété telle que ItemsControl.ItemsSource qui écoute les modifications de collection.

Une option serait bien sûr pour moi pour gérer les changements de collecte et à ceux qui se propagent propriété Text que la zone de texte est lié, ce qui est bien .

Cela constituerait une autre solution.

+0

Merci Julien, j'espérais qu'il me manquait quelque chose et ça marcherait. –

0

mise à jour ci-dessous le code

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

où txtName est votre nom de votre zone de texte

MVVM façon

1- Difine une propriété de type chaîne dans votre ViewModel comme indiqué ci-dessous et se lient cette propriété à la propriété de texte de la zone de texte a montré ci-dessous et supprimer ValueConverter pas besoin maintenant.

public string TextLines {get;set;} 

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

2- Je pense, vous le plus manipulation probablement événement click de bouton à l'aide d'un gestionnaire de commande de dire que votre commande est AddMoreLines

donc dans le gestionnaire de commande AddMoreLine, après avoir ajouté un nouvel objet dans votre OBservrableCollection, créer un StringBuilder et ajoutez tout le contenu de votre collection et affectez la chaîne à la propriété créée à l'étape 1.

3- Appelez PropertyChanged Handler.

+0

merci. Dans l'application réelle, la mise à jour se produit dans le modèle de vue, et le modèle de vue n'a aucune connaissance de la vue et quels contrôles sont liés aux propriétés. Je ne peux donc pas appliquer directement cette approche. Désolé ma reproduction simple du problème n'a pas montré ce côté de la conception. –

+0

normalement je le fais d'une manière que je soulève un événement de mon vm et porte à mon avis son genre de piratage et crée une liaison entre ma vue et ViewModel MAIS parfois il n'est pas possible de compléter à 100% avec MVVM et je peux vous dire exemple classique comme la gestion des opérations au niveau des cellules DataGrid. MAIS dans votre cas il y a une possibilité de ne pas casser le MVVM, si vous voulez je peux vous montrer comment le faire. – TalentTuner

4

Une méthode légèrement plus élégante consiste à utiliser MultiBinding sur la propriété Text et à lier à la propriété Count de la collection. Cela mettra à jour la liaison chaque fois que le Count de la collection change et met à jour le Text en fonction d'un MultiValueConverter que vous définissez.

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

Et le convertisseur:

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

Dans mon cas d'utilisation, je ne permets pas de mettre à jour la zone de texte sa source (d'où le'Mode = "OneWay"), mais en cas de besoin la méthode ConvertBack du convertisseur gérerait cela.