WPF 文本块绑定与 List<string>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/345385/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF textblock binding with List<string>
提问by Joshua
does anyone know if there is a simple way to bind a textblock to a List. What I've done so far is create a listview and bind it to the List and then I have a template within the listview that uses a single textblock.
有谁知道是否有一种简单的方法可以将文本块绑定到列表。到目前为止,我所做的是创建一个列表视图并将其绑定到列表,然后我在列表视图中有一个使用单个文本块的模板。
what I'd really like to do is just bind the List to a textblock and have it display all the lines.
我真正想做的只是将列表绑定到文本块并让它显示所有行。
In Winforms there was a "Lines" property that I could just throw the List into, but I'm not seeing it on the WPF textblock, or TextBox.
在 Winforms 中有一个“Lines”属性,我可以将列表放入其中,但我没有在 WPF 文本块或文本框上看到它。
Any ideas?
有任何想法吗?
did I miss something simple?
我错过了一些简单的东西吗?
Here's the 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>
and the WorkItem Class
和 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;}
I'm using Prism to create the control and put it into a WindowRegion
我正在使用 Prism 创建控件并将其放入 WindowRegion
WorkItemLogView newView = container.Resolve<WorkItemLogView>();
newView.DataContext = workItem;
regionManager.Regions["ShellWindowRegion"].Add(newView);
thanks
谢谢
回答by Jobi Joy
Convert your List to a single string with "\r\n" as the delimiter in between. and bind that to the TextBlock. Make sure that the TextBlock is not restricted with its height , so that it can grow based on the number of lines. I would implement this as a Value Converter to XAML Binding which converts a List of strings to a single string with new line added in between
将您的 List 转换为单个字符串,其中“\r\n”作为中间的分隔符。并将其绑定到 TextBlock。确保 TextBlock 不受其高度限制,以便它可以根据行数增长。我会将其实现为 XAML 绑定的值转换器,它将字符串列表转换为单个字符串,并在其间添加新行
<TextBlock Text="{Binding Path=Logs,Converter={StaticResource ListToStringConverter}}"/>
The ListToStringConverter would look like this:
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();
}
}
回答by punker76
if you use the converter it works for the first time perfect, but if one or more logging comes to the logging list, there is no update on your binding, because the converter works only at the first time. all controls that are no item controls doesn't subscribe to the listchanged event!
如果您使用转换器,它第一次运行完美,但如果一个或多个日志记录进入日志记录列表,则您的绑定不会更新,因为转换器仅在第一次运行。所有不是项目控件的控件都不会订阅 listchanged 事件!
here is a little code for this scenario
这是这个场景的一些代码
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));
}
}
}
}
the xaml
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>
the converter
转换器
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
编辑
here is a quick solution for the update propblem (this can be also made with a attached property)
这是更新问题的快速解决方案(也可以使用附加属性制作)
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;
}
here is the usage for the CustomTextBlock
(not tested)
这是CustomTextBlock
(未测试)的用法
<TextBlock Text="{Binding Path=ListToBind, RelativeSource=Self, Converter={StaticResource ListToStringConverter}}"
ListToBind={Binding Path=Logs} />
@Fueled hope this helps
@Fueled 希望这有帮助
回答by Fueled
I'll shamelessly post a link to my answer of a very similar question: Binding ObservableCollection<> to a TextBox.
我会无耻地发布一个链接到我对一个非常相似的问题的回答:Binding ObservableCollection<> to a TextBox。
Like punker76 said, if you bind your Text to a collection it will update when you set the collection, but not when the collection changes. This link demonstrates an alternative to punker76's solution (the trick is to multi-bind to the collection's count too).
就像 punker76 所说的那样,如果您将 Text 绑定到一个集合,它会在您设置集合时更新,但不会在集合更改时更新。此链接演示了 punker76 解决方案的替代方案(诀窍是多绑定到集合的计数)。
回答by david
For concat collection of objects :
对于 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 think to overide the ToString() of your object.
Juste 认为要覆盖对象的 ToString() 。