Why can't I use {x: Bind {RelativeSource Self}} in a data template?

If I use {x:Bind {RelativeSource Self}} in the data template, at compile time I get the following error:

The reference to the object is not installed in the instance of the object.

The idea is to pass a template object to a property similar to a command parameter. Here is an example of 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> 

A simple model is defined in the file with code lag 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(); } } } 
+5
source share
2 answers

Although it seems that you have solved your problem, I still want to make some clarifications in order to avoid confusion and make it understandable to future readers.

As @Peter Duniho noted, {x:Bind} cannot work with the DataContext property, and {x:Bind} does not have the Source property, so you cannot use StaticResource as the data context in {x:Bind} , but instead you can use a property or a static path. When using {x:Bind} it uses the background class as its data context. For example, when you set ItemsSource="{x:Bind NewsItems, Mode=OneWay}" , it uses the XBindTest5.MainPage class as its data context and binds the NewsItems property of this class to ItemsSource . And, while inside the DataTemplate, {x:Bind} uses the class declared in x:DataType as its data context. Please pay attention to the following explanation in DataTemplate and x: DataType :

Inside a DataTemplate (used as an element template, content template, or header template), the value Path is not interpreted in the context of the page, but in the context of the data template. So that its bindings can be checked (and the efficient code generated for them) at compile time, the DataTemplate must declare the type of its data object using x: DataType .

In your case, you use the Command in the DataTemplate , so you can add the OpenCommand property to the NewsItem and bind this property to Command to use it.

In your code:

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

In XAML:

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

Also {x:Bind} does not support {RelativeSource} , usually you can name this element and use its name in Path as an alternative. See the {x: Bind} and {Binding} function comparison for more information.

But this cannot be used in a DataTemplate , since all Path must be a NewsItem property. And in your case, I think you want to pass the NewsItem not a Button , so you can use CommandParameter="{x:Bind}" to pass the NewsItem as a CommandParameter .

PS: There is a small mistake in the XAML design, you can still get the error Object reference not set to an instance of an object. . You can add a space after Bind as {x:Bind } as a workaround.

+9
source

Let me answer this more specifically. There is only one possible data context for x: bind, and that is the base class. A page is a page (or code). In a data template, this is the support class specified in the targettype property of the data template. As an aside, in the control template, x: bind is not supported at all, although it is only a matter of time.

All that means that the x: bind data context is fixed, and depending on where it is used, I can tell you the data context without looking at your XAML. Why so tough? Partly to make it easier to create code around it. In addition, making the implementation easier. In any case, this is a fixed rule and RelativeSource, ElementName and Source and is not supported in x: bind.

This does not mean that you cannot reference the selfourceource, you just need to do this with the specified name x :. You would do something like this <Tag x:Name="Jerry" Tag="Nixon" Text="{x:Bind Jerry.Tag}" /> .

Why does this particular pattern fail? Unlike {binding} , {x:bind} requires matching types, which means that the installation of the text string can be lowered and configured on the tag object, but the tag object cannot be raised and set to the value of the text string. Choosing for you uses x: bind means your types must match.

Hope this helps you move forward.

Good luck.

+3
source

Source: https://habr.com/ru/post/1237201/


All Articles