2008-12-05 14 views
10

alguien sabe si hay una manera simple de vincular un bloque de texto a una lista. Lo que he hecho hasta ahora es crear una vista de lista y vincularla a la Lista y luego tengo una plantilla dentro de la vista de lista que usa un solo bloque de texto.WPF textblock vinculante con la lista <string>

lo que realmente me gustaría hacer es simplemente enlazar la Lista a un bloque de texto y hacer que muestre todas las líneas.

En Winforms había una propiedad de "Líneas" en la que podía lanzar la Lista, pero no la veo en el bloque de texto de WPF ni en TextBox.

¿Alguna idea?

¿Echo de menos algo simple?

Aquí está el código

<UserControl x:Class="QSTClient.Infrastructure.Library.Views.WorkItemLogView" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Width="500" Height="400"> 
<StackPanel> 
    <ListView ItemsSource="{Binding Path=Logs}" > 
     <ListView.View> 
      <GridView> 
       <GridViewColumn Header="Log Message"> 
        <GridViewColumn.CellTemplate> 
         <DataTemplate> 
          <TextBlock Text="{Binding}"/> 
         </DataTemplate> 
        </GridViewColumn.CellTemplate> 
       </GridViewColumn> 
      </GridView> 
     </ListView.View> 
    </ListView> 
</StackPanel> 

y de la Clase WorkItem

public class WorkItem 
{ 
    public string Name { get; set; } 
    public string Description { get; set; } 
    public string CurrentLog { get; private set; } 
    public string CurrentStatus { get; private set; } 
    public WorkItemStatus Status { get; set; } 
    public ThreadSafeObservableCollection<string> Logs{get;private set;} 

estoy usando Prism para crear el control y lo puso en un WindowRegion

 WorkItemLogView newView = container.Resolve<WorkItemLogView>(); 
     newView.DataContext = workItem; 
     regionManager.Regions["ShellWindowRegion"].Add(newView); 

gracias

Respuesta

28

Convierta su lista en una sola cadena con "\ r \ n" como delimitador en el medio. y vincular eso al TextBlock. Asegúrese de que TextBlock no esté restringido con su altura, de modo que pueda crecer en función del número de líneas. que implementaría esto como un convertidor de valores a XAML de encuadernación que convierte una lista de cadenas de una sola cadena con la nueva línea añadida en el medio

<TextBlock Text="{Binding Path=Logs,Converter={StaticResource ListToStringConverter}}"/> 

El ListToStringConverter se vería así:

[ValueConversion(typeof(List<string>), typeof(string))] 
public class ListToStringConverter : IValueConverter 
{ 

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     if (targetType != typeof(string)) 
      throw new InvalidOperationException("The target must be a String"); 

     return String.Join(", ", ((List<string>)value).ToArray()); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 
+0

Eso funcionó perfectamente gracias Jobi. – Joshua

2

si utiliza el convertidor funciona por primera vez perfecto, pero si uno o más registros llegan a la lista de registro, no hay actualización en su enlace, porque el convertidor funciona solo la primera vez. ¡todos los controles que no son controles de elementos no se suscriben al evento listchanged!

aquí es un poco de código para este escenario

using System; 
using System.Collections.ObjectModel; 
using System.Windows; 

namespace BindListToTextBlock 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
    private WorkItem workItem; 

    public MainWindow() { 
     this.WorkItems = new ObservableCollection<WorkItem>(); 
     this.DataContext = this; 
     this.InitializeComponent(); 
    } 

    public class WorkItem 
    { 
     public WorkItem() { 
     this.Logs = new ObservableCollection<string>(); 
     } 

     public string Name { get; set; } 
     public ObservableCollection<string> Logs { get; private set; } 
    } 

    public ObservableCollection<WorkItem> WorkItems { get; set; } 

    private void Button_Click(object sender, RoutedEventArgs e) { 
     this.workItem = new WorkItem() {Name = string.Format("new item at {0}", DateTime.Now)}; 
     this.workItem.Logs.Add("first log"); 
     this.WorkItems.Add(this.workItem); 
    } 

    private void Button_Click_1(object sender, RoutedEventArgs e) { 
     if (this.workItem != null) { 
     this.workItem.Logs.Add(string.Format("more log {0}", DateTime.Now)); 
     } 
    } 
    } 
} 

la xaml

<Window x:Class="BindListToTextBlock.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:BindListToTextBlock="clr-namespace:BindListToTextBlock" 
     Title="MainWindow" 
     Height="350" 
     Width="525"> 
    <Grid> 
    <Grid.Resources> 
     <BindListToTextBlock:ListToStringConverter x:Key="ListToStringConverter" /> 
    </Grid.Resources> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto" /> 
     <RowDefinition Height="Auto" /> 
     <RowDefinition /> 
    </Grid.RowDefinitions> 
    <Button Grid.Row="0" 
      Content="Add item..." 
      Click="Button_Click" /> 
    <Button Grid.Row="1" 
      Content="Add some log to last item" 
      Click="Button_Click_1" /> 
    <ListView Grid.Row="2" 
       ItemsSource="{Binding Path=WorkItems}"> 
     <ListView.View> 
     <GridView> 
      <GridViewColumn Header="Name"> 
      <GridViewColumn.CellTemplate> 
       <DataTemplate> 
       <TextBlock Text="{Binding Path=Name}" /> 
       </DataTemplate> 
      </GridViewColumn.CellTemplate> 
      </GridViewColumn> 
      <GridViewColumn Header="Log Message"> 
      <GridViewColumn.CellTemplate> 
       <DataTemplate> 
       <TextBlock Text="{Binding Path=Logs, Converter={StaticResource ListToStringConverter}}" /> 
       </DataTemplate> 
      </GridViewColumn.CellTemplate> 
      </GridViewColumn> 
     </GridView> 
     </ListView.View> 
    </ListView> 
    </Grid> 
</Window> 

el convertidor

using System; 
using System.Collections; 
using System.Globalization; 
using System.Linq; 
using System.Windows; 
using System.Windows.Data; 

namespace BindListToTextBlock 
{ 
    public class ListToStringConverter : IValueConverter 
    { 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { 
     if (value is IEnumerable) { 
     return string.Join(Environment.NewLine, ((IEnumerable)value).OfType<string>().ToArray()); 
     } 
     return "no messages yet"; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { 
     return DependencyProperty.UnsetValue; 
    } 
    } 
} 

EDITAR

aquí es una solución rápida para la actualización propblem (esto se puede también hacer con una propiedad adjunta)

public class CustomTextBlock : TextBlock, INotifyPropertyChanged 
{ 
    public static readonly DependencyProperty ListToBindProperty = 
    DependencyProperty.Register("ListToBind", typeof(IBindingList), typeof(CustomTextBlock), new PropertyMetadata(null, ListToBindPropertyChangedCallback)); 

    private static void ListToBindPropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e) 
    { 
    var customTextBlock = o as CustomTextBlock; 
    if (customTextBlock != null && e.NewValue != e.OldValue) { 
     var oldList = e.OldValue as IBindingList; 
     if (oldList != null) { 
     oldList.ListChanged -= customTextBlock.BindingListChanged; 
     } 
     var newList = e.NewValue as IBindingList; 
     if (newList != null) { 
     newList.ListChanged += customTextBlock.BindingListChanged; 
     } 
    } 
    } 

    private void BindingListChanged(object sender, ListChangedEventArgs e) 
    { 
    this.RaisePropertyChanged("ListToBind"); 
    } 

    public IBindingList ListToBind 
    { 
    get { return (IBindingList)this.GetValue(ListToBindProperty); } 
    set { this.SetValue(ListToBindProperty, value); } 
    } 

    private void RaisePropertyChanged(string propName) 
    { 
    var eh = this.PropertyChanged; 
    if (eh != null) { 
     eh(this, new PropertyChangedEventArgs(propName)); 
    } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

aquí es el uso de la CustomTextBlock (no probado)

<TextBlock Text="{Binding Path=ListToBind, RelativeSource=Self, Converter={StaticResource ListToStringConverter}}" 
      ListToBind={Binding Path=Logs} /> 

esperanza @Fueled este ayuda

+0

¿Hay alguna solución para esto? Estoy teniendo este problema exacto en mi proyecto de registro. – Fueled

+0

@Fueled He actualizado mi respuesta – punker76

+0

Gracias, pero terminé usando un MultiValueConverter y vinculando a la propiedad Count de la colección, que tiene por efecto actualizar la propiedad Text cada vez que se actualiza la colección. Vea mi respuesta a otra pregunta similar: http://stackoverflow.com/questions/4353186/binding-observablecollection-to-a-textbox/8847910#8847910. – Fueled

1

Voy a publicar descaradamente un enlace a mi respuesta de una pregunta muy similar: Binding ObservableCollection<> to a TextBox.

Como dijo punker76, si vincula su texto a una colección, se actualizará cuando configure la colección, pero no cuando cambie la colección. Este enlace demuestra una alternativa a la solución de punker76 (el truco es vincularse a la cuenta de la colección también).

0

Para la recolección de objetos concat:

/// <summary>Convertisseur pour concaténer des objets.</summary> 
[ValueConversion(typeof(IEnumerable<object>), typeof(object))] 
public class ConvListToString : IValueConverter { 
    /// <summary>Convertisseur pour le Get.</summary> 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { 
     return String.Join(", ", ((IEnumerable<object>)value).ToArray()); 
    } 
    /// <summary>Convertisseur inverse, pour le Set (Binding).</summary> 
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { 
     throw new NotImplementedException(); 
    } 
} 

Juste piensan que la overide ToString() de su objeto.