Как отобразить ObservableCollection<string> в UserControl
Я новичок в WPF и нашел несколько похожих вопросов, но не могу понять последнюю часть. У меня есть ViewModel с ObservableCollection, которая содержит сообщения об ошибках. Я хочу отобразить их в форме И позволить пользователю выбрать и скопировать все или часть сообщений. (В прошлом в приложениях WinForm я использовал для этого RichTextBox, но я не могу понять, как привязать его к коллекции в WPF.)
Я получил вид, который мне нужен, с помощью следующего xaml, но нет встроенного способа выбора и копирования, как я мог бы с RichTextBox. Кто-нибудь знает, какой элемент управления я должен использовать, или есть ли способ включить / скопировать содержимое всех TextBlocks, или способ связать это с RichTextBox?
<Grid Margin="6">
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="40" Grid.Column="0" Margin="6">
<ItemsControl ItemsSource="{Binding ErrorMessages}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Mode=OneWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
[Редактировать] @ Андрей Швыдки - Это не вписывается в комментарий. Мне потребовалось некоторое время, чтобы выяснить правильный синтаксис (особенно /, вещь), но в итоге я получил синтаксис Flow Document, показанный ниже. Это выглядит правильно в форме и на первый взгляд, кажется, поддерживает выбрать все / копия. Но когда я вставляю после выбора все / копия, ничего не появляется. Кто-нибудь знает почему?
<Grid Margin="6">
<FlowDocumentScrollViewer>
<FlowDocument >
<Paragraph>
<ItemsControl ItemsSource="{Binding ErrorMessages, Mode=OneWay}" />
<Run Text="{Binding /, Mode=OneWay}" />
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</Grid>
4 ответа
Может быть полезным для создания FlowDocument и отображения этого документа в FlowDocumentReader. Попробуйте начать с этой статьи: Обзор документооборота.
Пример генерации:
void ShowErrors(FlowDocumentReader reader, Exception[] errors) {
FlowDocument doc = new FlowDocument();
foreach (var e in errors) {
doc.Blocks.Add(new Paragraph(new Run(e.GetType().Name)) {
Style = (Style)this.FindResource("header")
});
doc.Blocks.Add(new Paragraph(new Run(e.Message)) {
Style = (Style)this.FindResource("text")
});
}
reader.Document = doc;
}
В этом примере я добавил несколько стилей для текста в flowdocument. Пожалуйста, посмотрите на XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="header" TargetType="{x:Type Paragraph}">
<Setter Property="FontWeight" Value="Bold"/>
</Style>
<Style x:Key="text" TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="30, 0, 0, 0"/>
</Style>
</Window.Resources>
<FlowDocumentReader Name="reader">
</FlowDocumentReader>
Результат:
Если у вас нет большого количества сообщений, может пригодиться простой конвертер:
<TextBox IsReadOnly="True">
<TextBox.Text>
<Binding Path="Messages" Mode="OneWay">
<Binding.Converter>
<vc:JoinStringsConverter />
</Binding.Converter>
</Binding>
</TextBox.Text>
</TextBox>
public class JoinStringsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var strings = value as IEnumerable<string>;
return string.Join(Environment.NewLine, strings);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
Самый простой способ:
Предполагая, что ваша viewmodel реализует INotifyPropertyChange, создайте обработчик событий для события ObservableCollection PropertyChanged. Создайте свойство, которое объединяет все элементы в наблюдаемой совокупности в одну строку. Всякий раз, когда наблюдаемая коллекция изменяется, инициируйте событие уведомления для вашего нового свойства. Привязать к этому свойству
public class ViewModel : INotifyPropertyChange
{
public ViewModel()
{
MyStrings.CollectionChanged += ChangedCollection;
}
public ObservableCollection<string> MyStrings{get;set;}
public void ChangedCollection(args,args)
{
base.PropertyChanged("MyAllerts");
}
public string MyAllerts
{
get
{
string collated = "";
foreach(var allert in MyStrings)
{
collated += allert;
collated += "\n";
}
}
}
}
Я знаю, что этот код чреват ошибками (я написал его в SO вместо VS), но он должен дать вам некоторое представление.
<Grid Margin="6">
<ScrollViewer VerticalScrollBarVisibility="Auto" Height="40" Grid.Column="0" Margin="6">
<ItemsControl ItemsSource="{Binding ErrorMessages}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding ViewModelMemberRepresentingYourErrorMessage}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>