wpf generics text binding

WPF textblock vinculante con List<string>



generics binding (4)

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

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.

¿Algunas ideas?

¿Extraño 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 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 ponerlo en una WindowRegion

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

Gracias


si usa el convertidor, funciona por primera vez como perfecto, pero si uno o más registros llegan a la lista de registro, no hay actualizaciones 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í hay un pequeño 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)); } } } }

el 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í hay una solución rápida para el problema de actualización (esto también se puede 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í está el uso de CustomTextBlock (no probado)

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

@Fueled espero que esto ayude


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. Implementaría esto como un Value Converter para XAML Binding, que convierte una lista de cadenas en una sola cadena con una nueva línea agregada 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(); } }


Para la colecció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 piensa sobreponer el ToString () de tu objeto.


Descaradamente publicaré un enlace a mi respuesta de una pregunta muy similar: Binding ObservableCollection <> a un 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).