I have the following Windows RT app. I bind a List of Strings to an ItemsControl of TextBlocks. This will display the empty strings as "System.Collections.Generic.List'1[System.String]" instead of just an empty string. I would like it to display an empty string instead of the type of the DataContext.
code behind:
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>
output:
System.Collections.Generic.List'1[System.String]
non empty string
I made the same example with traditional wpf and it displays the empty strings correctly.
Edit This outputs the same thing.
code behind:
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>
You can actually see that it's a bug (or odd intentional feature) by adding a Converter to the TextBlock
Binding
.
Add a static resource:
<Page.Resources>
<local:NoNullsConverter x:Key="fixNulls"></local:NoNullsConverter>
</Page.Resources>
Then change the Binding to reference the Converter:
<TextBlock Text="{Binding Converter={StaticResource fixNulls}}" FontSize="25"/>
Add this class:
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();
}
}
If you put a break point on the return
statement, you'll see that the first value that is passed actually is the entire list. Yes, unexpected. However, if you use this Converter as written, it handles that oddity and just returns a more logical empty string.
Or, you can do something more interesting and create a simple wrapper class:
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;
}
}
With this class, you can just use the Binding as expected:
<TextBlock Text="{Binding}" FontSize="25"/>
However, you would need to use a different class type in the declaration of the List:
DataContext = new List<StringContext>() { "", "not empty string" };
Using the implicit cast, it "just works" as it converts the String
to a StringContext
. Yes, it would add the overhead of creating an unnecessary class, but it does work. :) I'd prefer the Converter option.
I really can't explain why, but it works as expected when you directly set the ItemsSource
property:
<ItemsControl x:Name="itemsControl">
...
</ItemsControl>
public MainPage()
{
this.InitializeComponent();
itemsControl.ItemsSource = new List<string>() { "", "not empty string" };
}
I also tried this:
<ItemsControl ItemsSource="{Binding Items}">
...
</ItemsControl>
public MainPage()
{
this.InitializeComponent();
Items = new List<string>() { "", "not empty string" };
DataContext = this;
}
public IEnumerable Items { get; set; }
but it results in displaying
TextBlockBindingTest.MainPage
not empty string
Apparently, when the item binding evaluates to null or empty, it falls back to the inherited DataContext. I guess this is a bug in WinRT.
Alternatively, you may also set the Name
property of the MainPage class and write a binding like this:
<Page ... x:Name="mainPage">
...
<ItemsControl ItemsSource="{Binding Items, ElementName=mainPage}">
...
</ItemsControl>
and not set the DataContext:
public MainPage()
{
this.InitializeComponent();
Items = new List<string>() { "", "not empty string" };
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With