Связывание текстового блока WPF со списком <string> - PullRequest
12 голосов
/ 06 декабря 2008

Кто-нибудь знает, есть ли простой способ привязать текстовый блок к списку. До сих пор я создал представление списка и связал его со списком, а затем у меня в шаблоне просмотра есть шаблон, который использует один текстовый блок.

что я действительно хотел бы сделать, это просто связать список с текстовым блоком и отобразить все строки.

В Winforms было свойство "Линии", в которое я мог просто добавить Список, но я не вижу его в текстовом блоке WPF или TextBox.

Есть идеи?

я что-то упустил простой?

Вот код

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

и класс 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;}

Я использую Prism, чтобы создать элемент управления и поместить его в WindowRegion

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

спасибо

Ответы [ 4 ]

33 голосов
/ 06 декабря 2008

Преобразуйте ваш список в одну строку с "\ r \ n" в качестве разделителя между ними. и связать это с TextBlock. Убедитесь, что TextBlock не ограничен своей высотой, чтобы он мог расти в зависимости от количества строк. Я бы реализовал это как преобразователь значений в привязку XAML, которая преобразует список строк в одну строку с новой строкой, добавленной между

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

ListToStringConverter будет выглядеть так:

[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();
    }
}
4 голосов
/ 09 ноября 2011

если вы используете конвертер, он работает в первый раз идеально, но если один или несколько журналов попадают в список журналов, обновление вашей привязки не происходит, поскольку конвертер работает только в первый раз. все элементы управления, которые не являются элементами управления элементами, не подписываются на событие listaged!

вот небольшой код для этого сценария

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>

конвертер

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

вот быстрое решение проблемы обновления (это также можно сделать с помощью прикрепленного свойства)

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

вот использование для CustomTextBlock (не проверено)

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

@ Я надеюсь, что это поможет

2 голосов
/ 24 января 2012

Я постыдно опубликую ссылку на мой ответ на очень похожий вопрос: Привязка ObservableCollection <> к TextBox .

Как сказал punker76, если вы связываете свой текст с коллекцией, он будет обновляться при настройке коллекции, а не при изменении коллекции. Эта ссылка демонстрирует альтернативу решению punker76 (хитрость заключается в мультисвязывании с количеством коллекций).

0 голосов
/ 31 декабря 2015

Для конкретной коллекции объектов:

    /// <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 думает, что нужно переопределить ToString () вашего объекта.

...