为什么我不能在数据模板中使用{x:绑定{RelativeSource Self}}?

时间:2015-12-01 22:51:21

标签: c# winrt-xaml uwp

如果我在数据模板中使用{x:Bind {RelativeSource Self}},则在编译时会出现以下错误:

  

对象引用未设置为对象的实例。

我们的想法是将模板化对象传递给类似命令参数的属性。这是一个示例 MainPage.xaml

<Page
    x:Class="XBindTest5.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XBindTest5"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <ResourceDictionary>
            <local:OpenItemCommand x:Key="OpenCommand"/>
        </ResourceDictionary>
    </Page.Resources>

    <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <ItemsControl ItemsSource="{x:Bind NewsItems, Mode=OneWay}">
            <ItemsControl.ItemTemplate>
                <DataTemplate x:DataType="local:NewsItem">
                    <StackPanel>
                        <Button Command="{x:Bind {StaticResource OpenCommand}}" CommandParameter="{x:Bind {RelativeSource Self}}">
                            <TextBlock Text="{x:Bind Title}"/>
                        </Button>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>
</Page>

在code-behinde文件 MainPage.xaml.cs 中定义了一个简单的模型:

using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using Windows.UI.Xaml.Controls;


namespace XBindTest5 {

    public class NewsItem {
        public string Title { get; set; }
    }

    /// <summary>
    ///     command to open the item
    /// </summary>
    public class OpenItemCommand : ICommand {

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter) {
            return true;
        }

        public void Execute(object parameter) {
            // ... example ...
        }
    }

    public sealed partial class MainPage : Page {

        public ObservableCollection<NewsItem> NewsItems { get; set; }
            = new ObservableCollection<NewsItem>(new[] {
                new NewsItem() { Title = "Item 1" },
                new NewsItem() { Title = "Item 2" } });

        public MainPage() {
            this.InitializeComponent();
        }
    }
}

2 个答案:

答案 0 :(得分:11)

虽然看起来你已经解决了问题,但我仍然想做一些澄清以避免混淆,并为未来的读者清楚地说明。

正如@Peter Duniho所提到的,{x:Bind}无法使用DataContext属性而{x:Bind}没有Source属性,因此您无法使用StaticResource作为{x:Bind}中的数据上下文,但您可以使用属性或静态路径。使用{x:Bind}时,它使用背景类作为其数据上下文。例如,当您设置ItemsSource="{x:Bind NewsItems, Mode=OneWay}"时,它使用XBindTest5.MainPage类作为其数据上下文,并将此类的NewsItems属性绑定到ItemsSource。在DataTemplate中,{x:Bind}使用x:DataType中声明的类作为其数据上下文。请在DataTemplate and x:DataType中注明以下说明:

  

DataTemplate (无论是用作项目模板,内容模板还是标题模板)中,路径的值不会在路径的上下文中解释页面,但在被模板化的数据对象的上下文中。因此,在编译时可以验证其绑定(以及为它们生成有效的代码), DataTemplate 需要使用 x:DataType 声明其数据对象的类型。

在您的情况下,您使用Command中的DataTemplate,因此您可以在OpenCommand中添加NewsItem属性,并将此属性绑定到Command用它。

在您的代码隐藏中:

public class NewsItem
{
    public string Title { get; set; }
    public OpenItemCommand OpenCommand { get; set; }
}

在XAML中:

<DataTemplate x:DataType="local:NewsItem">
    <StackPanel>
        <Button Command="{x:Bind OpenCommand}" CommandParameter="{x:Bind}">
            <TextBlock Text="{x:Bind Title}" />
        </Button>
    </StackPanel>
</DataTemplate>

同样{x:Bind}不支持{RelativeSource},通常您可以将该元素命名并在Path中使用其名称作为替代。有关更多信息,请参阅{x:Bind} and {Binding} feature comparison

但是这不能在DataTemplate中使用,因为所有Path都应该是NewsItem的属性。在您的情况下,我认为您要传递的是NewsItem而不是Button,因此您可以使用CommandParameter="{x:Bind}"NewsItem作为CommandParameter传递}。

PS:XAML设计器中存在一个小错误,您可能仍会收到 Object reference not set to an instance of an object. 错误。您可以在绑定后添加空格,例如{x:Bind }作为解决方法。

答案 1 :(得分:4)

让我更具体地回答这个问题。 x:bind只有一个可能的数据上下文,那就是底层类。在页面上,它是页面(或代码隐藏)。在数据模板中,它是数据模板的targettype属性中指定的支持类。另外,在控件模板中,根本不支持x:bind - 尽管它只是时间问题。

这就是说x:bind的数据上下文是固定的,并且根据它的使用位置,我可以告诉你数据上下文而不用看你的XAML。为什么这么僵硬?部分是为了使代码生成更简单。此外,为了使实现更简单。在任何一种情况下,这都是固定的规则,而且是RelativeSource,ElementName和Source,在x:bind中不受支持。

这并不意味着你不能引用relativesource self,你只需要用指定的x:name来做。你会做这样的事情<Tag x:Name="Jerry" Tag="Nixon" Text="{x:Bind Jerry.Tag}" />

为什么特定样本会失败?与{binding}不同,{x:bind}需要匹配类型,这意味着设置Text的字符串可以向下转换并设置为Tag的对象,但Tag的对象无法启动-cast并设置为Text的字符串值。你的外卖是使用x:bind意味着你的类型必须匹配。

我希望这有助于您进一步发展。

祝你好运。