Связывание TextBlock отображает имя класса вместо пустой строки
У меня есть следующее приложение Windows RT. Я связываю List of Strings с ItemsControl TextBlocks. Это отображает пустые строки как "System.Collections.Generic.List'1 [System.String]" вместо пустой строки. Я хотел бы, чтобы он отображал пустую строку вместо типа DataContext.
код позади:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new List<string>() { "", "not empty string" };
}
}
XAML:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="25"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
выход:
System.Collections.Generic.List'1[System.String]
non empty string
Я сделал тот же пример с традиционным wpf, и он корректно отображает пустые строки.
Edit
Это приводит к тому же.
код позади:
public class Model
{
private readonly List<string> items = new List<string>() { "", "non empty string" };
public List<string> Items
{
get { return items; }
}
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new Model();
}
}
XAML:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ItemsControl ItemsSource="{Binding Path=Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="25"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Ответы
Ответ 1
Фактически вы можете увидеть, что это ошибка (или нечетная намеренная функция), добавив конвертер в TextBlock
Binding
.
Добавить статический ресурс:
<Page.Resources>
<local:NoNullsConverter x:Key="fixNulls"></local:NoNullsConverter>
</Page.Resources>
Затем измените привязку для ссылки на конвертер:
<TextBlock Text="{Binding Converter={StaticResource fixNulls}}" FontSize="25"/>
Добавьте этот класс:
public class NoNullsConverter : IValueConverter
{
// This converts the value object to the string to display.
public object Convert(object value, Type targetType,
object parameter, string language)
{
return value is string ? value : "";
}
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
throw new NotImplementedException();
}
}
Если вы поместите точку прерывания в оператор return
, вы увидите, что первое значение, которое фактически передано, - это весь список. Да, неожиданно. Однако, если вы используете этот Конвертер в письменном виде, он обрабатывает эту странность и просто возвращает более логичную пустую строку.
Или вы можете сделать что-то более интересное и создать простой класс-оболочку:
public class StringContext
{
public string Value { get; set; }
public static implicit operator StringContext(string value)
{
return new StringContext() { Value = value };
}
public override string ToString()
{
return Value;
}
}
С помощью этого класса вы можете просто использовать привязку, как ожидалось:
<TextBlock Text="{Binding}" FontSize="25"/>
Однако вам нужно использовать другой тип класса в объявлении списка:
DataContext = new List<StringContext>() { "", "not empty string" };
Используя неявный листинг, он "просто работает", поскольку он преобразует String
в StringContext
. Да, это добавит накладные расходы на создание ненужного класса, но это действительно сработает.:) Я бы предпочел вариант конвертера.
Ответ 2
Я действительно не могу объяснить, почему, но он работает так, как ожидалось, когда вы прямо устанавливаете свойство ItemsSource
:
<ItemsControl x:Name="itemsControl">
...
</ItemsControl>
public MainPage()
{
this.InitializeComponent();
itemsControl.ItemsSource = new List<string>() { "", "not empty string" };
}
Я также пробовал это:
<ItemsControl ItemsSource="{Binding Items}">
...
</ItemsControl>
public MainPage()
{
this.InitializeComponent();
Items = new List<string>() { "", "not empty string" };
DataContext = this;
}
public IEnumerable Items { get; set; }
но это приводит к отображению
TextBlockBindingTest.MainPage
не пустая строка
По-видимому, когда привязка элемента оценивается как null или empty, он возвращается к унаследованному DataContext. Я предполагаю, что это ошибка в WinRT.
В качестве альтернативы вы также можете установить свойство Name
класса MainPage и написать привязку следующим образом:
<Page ... x:Name="mainPage">
...
<ItemsControl ItemsSource="{Binding Items, ElementName=mainPage}">
...
</ItemsControl>
и не задайте DataContext:
public MainPage()
{
this.InitializeComponent();
Items = new List<string>() { "", "not empty string" };
}
Ответ 3
или попробуйте установить код привязки позади:
//list is ItemsControl
var bin = new Binding();
var mylist = new List<string>(){"","not empty string"};
bin.Source = mylist;
list.SetBinding(ItemsControl.ItemsSourceProperty,bin);
Это сработало для меня.
Ответ 4
Собственно, я наткнулся на более простое решение. Это, без сомнения, ошибка. Тот же код в WPF дает ожидаемые результаты. Источником ошибки является свойство TargetNullValue, которое неправильно интерпретирует пустые строки. Чтобы обойти ошибку, вы можете либо использовать предложенный выше IValueConverter (решения для конвертеров имеют проблемы с производительностью из моего опыта), либо использовать:
<TextBlock Text="{Binding TargetNullValue=' '}" FontSize="25"/>