2015-12-01 38 views
5

Se uso {x:Bind {RelativeSource Self}} in un modello di dati, ottengo il seguente errore durante la compilazione:Perché non posso usare {x: Bind {RelativeSource Self}} in un modello dati?

riferimento oggetto non impostato a un'istanza di un oggetto.

L'idea è di passare l'oggetto con modello a una proprietà come un parametro di comando. Ecco un esempio 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> 

un semplice modello è definito nel file 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(); 
     } 
    } 
} 
+0

Se ti chiedi perché ho passato 'command' come' StaticRessource': non ho trovato un modo per fare riferimento a _outer_ proprietà nel modello dati tramite 'X: Bind'. L'uso di una risorsa statica non funziona neanche (crea un'altra NullReferenceException). – ventiseis

+0

Scusa, hai ragione. Dai un'occhiata a questo: http://stackoverflow.com/questions/32372073/is-it-possible-to-use-compiled-binding-xbind-with-reletive-source-templated –

+0

Oh, hai ragione. La risposta dice: _RelativeSource (con x: Bind) non è supportato_. Quindi dovrò cambiare il mio modello .. Ho cercato solo "datatemplate" quindi ho perso questa risposta. – ventiseis

risposta

9

Anche se sembra aver risolto il problema, voglio ancora fare alcuni chiarimenti per evitare confusione e renderlo chiaramente per i futuri lettori.

Come ha accennato @Peter Duniho, {x:Bind} non può funzionare con DataContext proprietà e {x:Bind} non dispone di una proprietà Source, quindi non è possibile utilizzare StaticResource come contesto dati in {x:Bind}, ma è possibile utilizzare una proprietà o di un percorso statico invece. Durante l'utilizzo di {x:Bind}, utilizza la classe di background come contesto dati. Ad esempio, quando si imposta ItemsSource="{x:Bind NewsItems, Mode=OneWay}", utilizza la classe XBindTest5.MainPage come contesto dati e associa la proprietà NewsItems di questa classe a ItemsSource. E mentre all'interno di un DataTemplate, {x:Bind} utilizza la classe dichiarata in x:DataType come contesto dati. Si prega di notare seguente spiegazione in DataTemplate and x:DataType:

All'interno di un DataTemplate (se usato come un modello di elemento, un modello di contenuti, o un modello di intestazione), il valore di percorso non è interpretato nel contesto della pagina, ma nel contesto dell'oggetto dati oggetto di template. Affinché i binding possano essere convalidati (e generato un codice efficiente per loro) in fase di compilazione, un DataTemplate DataTemplate deve dichiarare il tipo del relativo oggetto dati utilizzando x: DataType.

Nel tuo caso, si utilizza il Command in DataTemplate, in modo da poter aggiungere una proprietà OpenCommand in NewsItem e si legano questa proprietà per Command di usarlo.

Nel code-behind:

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> 

anche {x:Bind} non supporta {RelativeSource}, di solito è possibile il nome dell'elemento e utilizzare il suo nome in Path come alternativa. Per ulteriori informazioni, vedere {x:Bind} and {Binding} feature comparison.

Ma questo non può essere utilizzato in DataTemplate poiché tutte le Path devono essere una proprietà di NewsItem.E nel tuo caso, penso che quello che vuoi passare sia lo NewsItem non lo Button, quindi puoi usare CommandParameter="{x:Bind}" per passare lo NewsItem come CommandParameter.

PS: C'è un piccolo bug nella finestra di progettazione XAML, è comunque possibile ottenere un errore Object reference not set to an instance of an object.. È possibile aggiungere uno spazio dopo Bind come {x:Bind } come soluzione alternativa.

+0

Grazie per la spiegazione molto dettagliata, ma un piccolo commento: utilizzando il 'RelativeSource ...'per il parametro di comando ho provato a fare riferimento al corrente' NewsItem', non al pulsante. Quindi ho capito anche l'estensione del markup. – ventiseis

3

Consentitemi di rispondere più specificatamente. Esiste un solo contesto dati possibile per x: bind e questa è la classe sottostante. In una pagina, è la pagina (o il code-behind). In un modello di dati, è la classe di supporto specificata nella proprietà targettype del modello di dati. Per inciso, in un modello di controllo, x: bind non è supportato affatto - anche se è solo una questione di tempo.

Tutto ciò per dire che il contesto dati di x: bind è fisso e, a seconda di dove viene utilizzato, posso dirvi il contesto dei dati senza guardare il vostro XAML. Perché così rigido? In parte per semplificare la generazione del codice. Inoltre, per semplificare anche l'implementazione. In entrambi i casi, questa è una regola fissa, RelativeSource, ElementName e Source e non supportata in x: bind.

Ciò non significa che non è possibile fare riferimento all'auto parente, è sufficiente farlo con una x: nome specificata. Faresti qualcosa di simile a questo <Tag x:Name="Jerry" Tag="Nixon" Text="{x:Bind Jerry.Tag}" />.

Perché questo particolare campione fallisce? A differenza di {binding}, {x:bind} richiede tipi di corrispondenza, il che significa che l'impostazione della stringa di testo può essere sottoposta a down-cast e impostata sull'oggetto Tag, ma l'oggetto Tag non può essere richiamato e impostato sul valore stringa di Testo. Il take-away per te sta usando x: bind significa che i tuoi tipi devono corrispondere.

Spero che questo ti aiuti ad andare oltre.

Buona fortuna.