2013-03-04 1 views
12

Sto cercando di utilizzare il collegamento dati per associare una ObservableCollection all'elemento ItemsSource di un DataGrid, mentre imparo su WPF e altre cose.Perché il binding del datacontext MainWindow in XAML non funziona come il binding nel codebehind con this.datacontext = this?

Nel codice sottostante posso impostare DataContext con this.DataContext = this; o bloopDataGrid.DataContext = this;. Va bene e dandy.

ho pensato che avrei potuto provare qualcosa di simile

<Window.DataContext> 
    <local:MainWindow/> 
</Window.DataContext> 

nella mia finestra principale, ma questo causa un'eccezione Stack Overflow come spiegato nel this question. Bene, questo ha un senso.

Dopo aver letto this e ad altre domande/risposte che dicono di provare DataContext="{Binding RelativeSource={RelativeSource Self}}" nel codice XAML della finestra, ho pensato che avrei potuto effettivamente fare questo. Apparentemente I non può. O almeno, l'IDE me lo consente ed è sintatticamente corretto, ma non fa quello che voglio (cioè, esattamente ciò che fa this.DataContext = this;).

poi ho letto this sull'utilizzo "{Binding ElementName=, Path=}" e ha cercato di usarla in questo modo:

<DataGrid 
    Name="bloopDataGrid" 
    Grid.Row="1" 
    ItemsSource="{Binding ElementName=testWin, Path=OutputCollection}"> 
</DataGrid> 

che inoltre non funziona. Forse non per lo stesso motivo, ma non riesco a capire il problema con esso.

Stranamente, non riesco a replicare l'esempio di rebinding mostrato in Rachel Lim's blog post.

XAML:

<Window 
    x:Class="DataBinding.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" 
    Height="350" 
    Width="525" 
    x:Name="testWin"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition /> 
      <RowDefinition /> 
     </Grid.RowDefinitions> 

     <Label Grid.Row="0" Content="{Binding text}"> 
     </Label> 

     <DataGrid 
      Name="bloopDataGrid" 
      Grid.Row="1" 
      ItemsSource="{Binding Path=OutputCollection}"> 
     </DataGrid> 
    </Grid> 
</Window> 

C#:

using System; 
using System.Collections.ObjectModel; //For ObservableCollection<T> 
using System.Windows; 

namespace DataBinding 
{ 
    public partial class MainWindow : Window 
    { 
     public String text { get; set; } 
     public ObservableCollection<testStruct> OutputCollection { get; set; } 

     public struct testStruct 
     { 
      public testStruct(String x, String y) : this() 
      { 
       Col1 = x; 
       Col2 = y; 
      } 
      public String Col1 { get; set; } 
      public String Col2 { get; set; } 
     } 

     public MainWindow() 
     { 
      InitializeComponent(); 

      testA t1 = new testA(); 
      this.DataContext = this; 
      //this.DataContext = t1; 
      //bloopDataGrid.DataContext = this; 
      text = "bound \"this\""; 
      t1.text = "bound a class"; 

      OutputCollection = new ObservableCollection<testStruct>(); 
      OutputCollection.Add(new testStruct("1", "2")); 
      OutputCollection.Add(new testStruct("3", "4")); 
     } 

     public class testA 
     { 
      public String text { get; set; } 
     } 

    } 
} 

Il codice di cui sopra è quello che sto usando per testare questo, ed è attualmente utilizzando il code-behind versione che correttamente mi dà

In codebehind

Che cosa sto facendo male, che mi impedisce di ottenere gli stessi risultati dell'immagine sopra ma utilizzando XAML per la gestione di DataContext? Non sto collegando i punti correttamente? ... mi mancano alcuni punti?

risposta

25
<Window.DataContext> 
    <local:MainWindow/> 
</Window.DataContext> 

non è la stessa

this.DataContext = this; 

La prima è la creazione di una nuova istanza della classe MainWindow e assegnando che al DataContext proprietà del Window, mentre la seconda consiste nell'assegnare la stessa istanza dello Window alla sua proprietà DataContext.

Al fine di raggiungere questo in XAML, è necessario utilizzare un RelativeSource vincolante:

<Window DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
</Window> 

Edit:

La differenza di comportamento tra la definizione del DataContext in XAML e nel codice dietro è causato dal fatto che XAML viene effettivamente analizzato quando il costruttore termina l'esecuzione, poiché Dispatcher attende che il codice utente (nel costruttore della finestra) termini prima di eseguire le sue operazioni in sospeso.

Questo fa sì che i valori delle proprietà effettive siano diversi in questi diversi momenti, e poiché non c'è INotifyPropertyChanged, WPF non ha modo di aggiornare l'IU per riflettere i nuovi valori.

È could implementare INotifyPropertyChanged nel Window sé, ma vi suggerisco di creare un ViewModel per questo, come non mi piace il fatto di mescolare INotifyPropertyChanged (che è più di un concetto ViewModel) con DependencyObject classi -derived (elementi dell'interfaccia utente).

+0

Ho detto che questo non ha funzionato per me e che ho capito qual è il problema con MainWindow guardando l'altra domanda a riguardo. – Hydronium

+0

Il tuo problema è che stai mettendo insieme i tuoi dati all'interno della classe 'MainWindow'. Dovresti creare un ViewModel e implementare 'INotifyPropertyChanged'. –

+0

Non sono sicuro di aver capito. Sono limitato a seguire il pattern MVVM per tutti i miei programmi in WPF? Come farà una nuova classe a risolvere il problema che ho? Preferisco ripararlo o capire perché non funziona, non evitarlo rigorosamente solo perché non lo capisco. – Hydronium