2008-12-05 10 views
10

weiß jemand, ob es eine einfache Möglichkeit gibt, einen Textblock an eine Liste zu binden. Was ich bisher gemacht habe, ist eine Listenansicht zu erstellen und sie an die Liste zu binden, und dann habe ich eine Vorlage innerhalb der Listenansicht, die einen einzelnen Textblock verwendet.WPF-Textblockbindung mit Liste <string>

was ich wirklich gerne tun würde ist nur die Liste an einen Textblock binden und es alle Zeilen anzeigen lassen.

In WinForms gab es eine "Linien" -Eigenschaft, die ich nur in die Liste werfen konnte, aber ich sehe es nicht auf dem WPF-Textblock oder TextBox.

Irgendwelche Ideen?

habe ich etwas einfaches vermisst?

Hier ist der Code

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

und die WorkItem Klasse

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

ich Prism bin mit dem Steuerelement zu erstellen und legte ihn in eine WindowRegion

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

danke

Antwort

28

Konvertieren Sie Ihre Liste in eine einzelne Zeichenfolge mit "\ r \ n" als Trennzeichen dazwischen. und binden Sie das an den TextBlock. Stellen Sie sicher, dass der TextBlock nicht auf seine Höhe beschränkt ist, sodass er basierend auf der Anzahl der Zeilen wachsen kann. Ich würde das als Value Converter implementieren Bindung an XAML, die mit neuer Zeile eine Liste von Strings zu einem einzigen String konvertiert zwischen

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

The ListToStringConverter folgendermaßen aussehen würde hinzugefügt:

[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

Das hat perfekt funktioniert, danke Jobi. – Joshua

2

wenn Du verwendest den Konverter, der zum ersten Mal funktioniert, perfekt, , aber wenn eine oder mehrere Loggings in die Logging-Liste kommen, gibt es kein Update auf deine Bindung, weil der Konverter nur beim ersten Mal funktioniert. Alle Steuerelemente, die keine Elementsteuerelemente sind, abonnieren das listchanged-Ereignis nicht!

hier ist ein wenig Code für dieses Szenario

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

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> 

den Konverter

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

EDIT

hier ist eine schnelle Lösung für das Update propblem (dies kann auch mit einer angefügten Eigenschaft gemacht werden)

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

hier ist die Verwendung für die CustomTextBlock (nicht getestet)

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

@Fueled hoffen, dass diese hilft

+0

Gibt es einen Workaround dafür? Ich habe genau dieses Problem in meinem Log-Projekt. – Fueled

+0

@Fueled Ich habe meine Antwort aktualisiert – punker76

+0

Danke, aber ich endete mit einem MultiValueConverter und Bindung an die Count-Eigenschaft der Sammlung, die für die Aktualisierung der Text-Eigenschaft jedes Mal aktualisiert wird, wenn die Sammlung aktualisiert wird. Siehe meine Antwort auf eine ähnliche Frage: http://stackoverflow.com/questions/4353186/binding-observablecollection-to-a-textbox/8847910#8847910. – Fueled

1

Ich werde schamlos einen Link zu meiner Antwort auf eine sehr ähnliche Frage: Binding ObservableCollection<> to a TextBox.

Wie punker76 sagte, wenn Sie Ihren Text an eine Sammlung binden, wird er aktualisiert, wenn Sie die Sammlung festlegen, aber nicht, wenn sich die Sammlung ändert. Dieser Link zeigt eine Alternative zur Lösung von punker76 (der Trick besteht darin, auch an die Anzahl der Sammlungen zu binden).

0

Für concat Sammlung von Objekten:

/// <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 denkt, die ToString() des Objekts außer Kraft zu setzen.

Verwandte Themen