文本块绑定显示类名而不是空字符串。

10

我有一个 Windows RT 应用程序。我将一组字符串绑定到 TextBlock 的 ItemsControl 上。这会将空字符串显示为 "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 做了同样的例子,它正确地显示了空字符串。

编辑 这输出了相同的结果。

代码:

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>

我还没有测试过,但似乎这是平台上的一个错误,但无论如何 - 这似乎不是使用绑定的典型主流方式。人们通常直接将ItemsSource设置为集合,或者使用公开集合属性的视图模型类型,然后将其绑定到您的ItemsControl的ItemsSource。我从未见过有人将DataContext设置为集合,也没有看到任何好的理由。 - Filip Skakun
这只是一个示例。我正在构建的应用程序将DataContext分配给一个ViewModel,该ViewModel具有类型为List<String>的属性。 - David Klimek
4个回答

5
你可以通过将转换器添加到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;
    }
}

使用这个类,您可以像预期的那样使用 绑定(Binding):
<TextBlock Text="{Binding}" FontSize="25"/>

然而,在声明List时,你需要使用不同的类类型:

DataContext = new List<StringContext>() { "", "not empty string" };

使用隐式转换,它会将String转换成StringContext,从而“一切正常”。是的,这会增加创建不必要类的开销,但其确实可行。 :) 我更喜欢使用转换器选项。

我还创建了一个转换器来查看发生了什么。我猜这是一个错误。我将使用转换器解决问题。谢谢! - David Klimek
我也遇到了这个 bug。我更多地在使用 code-behind,所以我想我有一些更多的信息。看起来绑定属性 'TargetNullValue' 是有问题的。如果你给它一个空字符串,你会得到上面描述的行为。如果你给它一个单独的空格 ' ',那么它就会用单个空格填充空值。他们可能在背后做了一些 'IsNullOrEmpty',而他们只是想要与 null 进行比较。 - Quark Soup

1

我真的无法解释为什么,但当您直接设置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中的一个错误。


或者,您也可以设置 MainPage 类的 Name 属性,并编写如下绑定:

<Page ... x:Name="mainPage">
...
<ItemsControl ItemsSource="{Binding Items, ElementName=mainPage}">
    ...
</ItemsControl>

并且没有设置DataContext:

public MainPage()
{
    this.InitializeComponent();
    Items = new List<string>() { "", "not empty string" };
}

0

或者尝试在代码后台设置绑定:

//list is ItemsControl
var bin = new Binding();
var mylist = new List<string>(){"","not empty string"};
bin.Source = mylist;
list.SetBinding(ItemsControl.ItemsSourceProperty,bin);

这对我有用。


这个代码可以运行,但并不是因为它是 Code-Behind。而是因为你没有设置 DataContext,而是设置了 Binding Source。 - Clemens

0
其实,我偶然发现了一个更简单的解决方案。毫无疑问,这是一个错误。在WPF中相同的代码会产生预期的结果。错误的源头是TargetNullValue属性,它不能正确地解释空字符串。为了避免这个错误,你可以使用上面建议的IValueConverter(从我的经验来看,转换器解决方案存在性能问题),或者使用以下方法:
        <TextBlock Text="{Binding TargetNullValue=' '}" FontSize="25"/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接