【问题标题】:Why can't I use {x:Bind {RelativeSource Self}} in a data template?为什么我不能在数据模板中使用 {x:Bind {RelativeSource Self}}?
【发布时间】:2016-03-06 01:03:25
【问题描述】:

如果我在数据模板中使用{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>

在代码隐藏文件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();
        }
    }
}

【问题讨论】:

  • 如果您想知道为什么我将command 传递为StaticRessource:我没有找到通过X:Bind 引用数据模板中的outer 属性的方法。使用静态资源也不起作用(创建另一个 NullReferenceException)。
  • 对不起,你是对的。看看这个吧:stackoverflow.com/questions/32372073/…
  • 哦,你是对的。答案是:RelativeSource (with x:Bind) 不受支持。所以我必须改变我的模型。我只搜索了datatemplate,所以我错过了这个答案。
  • 当您说“模板化对象”时,您是指模板在视觉上代表的视图模型吗?我想如果你只使用{Binding},那应该可以。来自x:Bind 的文档:“您不能将 {x:Bind} 与 Object 类型的 DataContext 属性一起使用,并且在运行时也会发生变化。”。 IE。任何依赖于运行时类型信息的技术(例如绑定到模板化对象)都无法与x:Bind 一起使用。您需要转而使用{Binding}
  • 恕我直言,您自己应该以工作形式发布代码作为答案,然后自行接受答案。还包括文档和解释中的引用。这将提供未来读者需要的信息,并清楚地将问题标记为已回答。我不愿自己发布答案,因为(正如我所提到的)我还没有升级到 VS2015,因此我自己实际上无法测试任何 UWP/Windows 10 代码;我确实相信您的说法,即使用 CommandParameter={x:Bind} 可以按预期工作,但我会尽量确保我发布的任何答案都只包含我个人验证过的代码。

标签: c# winrt-xaml uwp


【解决方案1】:

虽然看起来你的问题已经解决了,但我还是想澄清一下,避免混淆,让以后的读者看清楚。

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

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

在您的情况下,您在DataTemplate 中使用Command,因此您可以在NewsItem 中添加OpenCommand 属性并将此属性绑定到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. 错误。您可以在 Bind 之后添加一个空格,例如 {x:Bind } 作为解决方法。

【讨论】:

  • 感谢您非常详细的解释。但是有一个小评论:通过使用RelativeSource... 作为命令参数,我试图引用当前的NewsItem,而不是按钮。所以我也误解了那个标记扩展。
【解决方案2】:

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

这就是说 x:bind 的数据上下文是固定的,并且根据使用它的位置,我可以在不查看 XAML 的情况下告诉您数据上下文。为什么这么死板?部分是为了使围绕它的代码生成更简单。此外,为了使实现更简单。无论哪种情况,这是一个固定规则,RelativeSource、ElementName 和 Source 在 x:bind 中不受支持。

这并不意味着您不能引用相对源自身,您只需使用指定的 x:name 即可。你会做这样的事情&lt;Tag x:Name="Jerry" Tag="Nixon" Text="{x:Bind Jerry.Tag}" /&gt;

为什么那个特定的样本会失败?与{binding}不同,{x:bind}需要匹配类型,这意味着设置Text的字符串可以向下转换并设置为Tag的对象,但Tag的对象不能向上转换并设置为Text的字符串值。对您来说,使用 x:bind 意味着您的类型必须匹配。

我希望这能帮助你走得更远。

祝你好运。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-20
    • 2014-05-09
    相关资源
    最近更新 更多