2016-05-02 7 views
0

EDIT: Die Frage war nicht klar genug. Tatsächlich gibt es zwei von ihnen.WPF-Bindung instanziiert Objekt zu Datenkontext

Q1:

ich ein Usercontrol "Custom" haben, die mit einer Vorlage dynamisch erstellt wird:

<Window.Resources> 
    <DataTemplate DataType="{x:Type my:CustomViewModel}"> 
     <my:CustomView/> 
    </DataTemplate> 
</Window.Resources> 

<ItemsControl ItemsSource="{Binding Path=CustomList}"/> 

Wo Custom eine Eigenschaft vom Typ ObservableCollection ist < ‚CustomViewModel> Zugehörigkeit zu MainWindowViewModel, Das ist der Window DataContext.

In CustomViews Xaml-Code sind einige Eigenschaften an CustomViewModels Eigenschaften gebunden. Alles funktioniert einwandfrei. Aber wenn ich versuche, diesen Code in der Custom zu tun hinter:

public CustomView() 
{ 
    InitializeComponents(); 
    if (this.DataContext == null) Console.WriteLine ("DataContext is null"); 
    else Console.WriteLine(this.DataContext.GetType().ToString()); 
} 

Es wird in Console geschrieben: ‚Datacontext ist null‘, auch wenn Bindungen betweeen Custom und CustomViewModel arbeiten. Weißt du, warum es funktioniert?

Q2:

nun vorstellen, dass eine andere Customusercontrol hat (IndexPicker) im Inneren. IndexPicker hat auch ein ViewModel (IndexPickerViewModel), das für den Datenzugriff zuständig ist. Ich muss eine Eigenschaft ("Index") dieses IndexPickerViewModel an die vorherige Eigenschaft des CustomViewModels "Id" binden. Ich will es in StaticResources instanziiert und binden Sie es an die CustomViewModel (was ich glaube, ist die Datacontext nach meiner vorherigen Frage):

<UserControl x:Class="MyView.CustomView" 
... 
<UserControl.Resources> 
    <DataTemplate DataType="{x:Type myPicker:IndexPickerViewModel}"> 
     <myPicker:IndexPicker/> 
    </DataTemplate> 
    <myPicker:IndexPickerViewModel x:Key="pickerViewModel" Index="{Binding Path=Id}/> 
</Window.Resources/> 

<ContentControl Content={StaticResource pickerViewModel}/> 

Was ich versucht habe: Ich habe versucht, machen „IndexPickerViewModel“ erben von „DependencyObject "und make" Index "eine DependencyProperty. Aber die folgende Fehlermeldung erscheint:

"System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Id; DataItem=null; target element is 'IndexPickerViewModel' (HashCode=59604175); target property is 'Index' (type 'Nullable`1') 

Ich glaube, dies ist wegen dem, was fragte ich gerade oben. Aber ist es möglich so etwas zu tun? Wenn ja, was fehlt mir? Und: Ist das eine dumme Idee? Vielen Dank im Voraus für jede Hilfe.

+0

Warum binden Sie den Content einfach nur mit StaticResource ein? –

+0

@Kaiser Aiman ​​- Sie haben Recht, es war ein Fehler. Das Problem ist jedoch die Bindung zwischen dem Ressourcenobjekt und dem DataContext. – heliar

Antwort

0

Es scheint, dass ich zu früh gefragt habe, weil ich Antworten alleine gefunden habe.

Antwort Question1

Wenn Sie einen Benutzersteuerelement haben, das dynamisch von einem Datatemplate geschaffen, in dem es mit einem anderen Objekt zugeordnet ist (die Zugehörigkeit zu einer Ansichtsmodell oder zu einer Ressource), wird diese Aufgabe als die definierte DataContext des Benutzersteuerelements. Sie können jedoch nicht in dem Konstruktor des Usercontrol erreichen, müssen Sie warten, bis das „Loaded“ Ereignis ausgelöst wird:

public CustomUserControl() 
{ 
    InitializeComponent(); 
    Console.WriteLine(this.DataContext.ToString()); 
    // This doesn't work : DataContext is null 
} 

private void UserControl_Loaded(object sender, RoutedEventArgs e) 
{ 
    Console.WriteLine(this.DataContext.ToString()); 
    // or 
    Console.WriteLine((sender as UserControl).DataContext.ToString()); 
    // this is Ok. 
} 

Antwort Question2

Dies ist, wie Sie tun, ein Benutzersteuerelement, das zu erhalten ViewModel wird in einem übergeordneten UserControl.Resources instanziiert:

Sie tun es nicht.

Stattdessen instanziieren Sie sein ViewModel in seinem übergeordneten ViewModel. Volles Beispiel:

MainWindow.xaml:

<Window x:Class="MainWindow" 
    ... 
    xmlns:local="clr-namespace:my_project_namespace" 
    xmlns:cust="clr-namespace:CustomUserControl;assembly=CustomUserControl" 
    ...> 

    <Window.Resources> 
     <DataTemplate DataType="{x:Type cust:CustomControlViewModel}"> 
      <cust:CustomControlView> 
     </DataTemplate> 
     <!-- Here are listed all the types inheriting from CustomControlViewModel and CustomControlView.--> 
     <!-- CustomControlViewModel and CustomControlView are used as "abstract" classes--> 
    </Window.Resources> 

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

    <Grid> 
     <ItemsControl ItemsSource="{Binding Path=CustomVMList}"/> 
    </Grid> 
</Window> 

MainWindowViewModel.cs:

namespace my_project_namespace 
{ 
    public class MainWindowViewModel 
    { 
     public ObservableCollection<CustomControlViewModel> CustomVMList { get; set; } 
     public MainWindowViewModel() 
     { 
      CustomVMList = new ObservableCollection<CustomControlViewModel>(); 
      // Fill in the list... 
     } 
    } 
} 

CustomControlView.xaml

<UserControl x:class="CustomUserControl.CustomControlView" 
    ... 
    xmlns:my="clr-namespace:IndexPicker;assembly=IndexPicker" 
    ...> 

    <UserControl.Resources> 
     <DataTemplate DataType="{x:Type my:IndexPickerViewModel}"> 
      <my:IndexPickerView/> 
     </DataTemplate> 
    </UserControl.Resources> 

    <StackPanel Orientation="Horizontal"> 
     <Label Content="{Binding Name}/> 
     <ContentControl Content="{Binding Path=MyIndexPicker}"/> 
    </Grid> 
</UserControl> 

Und das ist, wo es ist interessant:

CustomControlViewModel.cs:

namespace CustomUserControl 
{ 
    public class CustomControlViewModel : INotifyPropertyChanged 
    { 
     public IndexPickerViewModel MyIndexPicker{ get; set; } 
     public string Name { get ; set; } 
     public int Id 
     { 
      get 
      { 
       return MyIndexPicker.Index; 
      } 
      set 
      { 
       if (value != MyIndexPicker.Index) 
       { 
        MyIndexPicker.Index = value; 
        NotifyPropertyChanged("Id"); 
       } 
      } 
     } 

     public CustomControlViewModel(string _name) 
     { 
      Name = _name; 
      MyIndexPicker = new IndexPickerViewModel(); 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     private void NotifyPropertyChanged(string propertyName) 
     { 
      if (this.PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName); 
     } 
    } 
} 

IndexPickerView.xaml:

<UserControl x:Class="IndexPicker.IndexPickerView" 
    ... 
    ...> 
    <Grid> 
     <Combobox ItemsSource="{Binding Path=MyTable}" 
      DisplayMemberPath="ColumnXYZ" 
      SelectedItem={Binding Path=SelectedRow}/> 
    </Grid> 
</UserControl> 

Endlich

IndexPickerViewModel.cs:

namespace IndexPicker 
{ 
    public class IndexPickerViewModel : INotifyPropertyChanged 
    { 
     private DataAccess data; 
     public DataView MyTable { get; set; } 

     private DataRowView selectedRow; 
     public DataRowView SelectedRow 
     { 
      get { return selectedRow; } 
      set 
      { 
       selectedRow = value; 
       NotifyPropertyChanged("SelectedRow"); 
      } 
     } 

     public int? Index 
     { 
      get 
      { 
       if (SelectedRow != null) return (int?)selectedRow.Row["Column_Id"]; 
       else return null; 
      } 

      set 
      { 
       SelectedRow = MyTable[MyTable.Find((int)value)]; 
       NotifyPropertyChanged("Index"); 
      } 
     } 

     public IndexPickerViewModel() 
     { 
      data = new DataAccess(); 
      MyTable = data.GetTableView("tableName"); 
      MyTable.Sort = "Column_Id"; 
     } 

     // And don't forget INotifyPropertyChanged implementation 
    } 
} 

Diese Konfiguration mit mehreren verschiedenen Usercontrols verwendet wird vererben von CustomControlView und ihre Ansichtsmodell von vererben CustomControlViewModel. Sie werden dynamisch erstellt und in CustomVMList aufgelistet. Hier ist CustomControlViewModel mit einem IndexPicker bereits eine Spezialisierung.

Konkrete Verwendung: Generischer Dialog für CRUD-Datenbank Tabellen, die UserControls in Abhängigkeit von den einzelnen Tabellenspalten dynamisch erstellen können. Die hier gezeigte Spezialisierung wird im Fall einer Spalte verwendet, die einen Fremdschlüssel enthält. Ich hoffe es ist klar.

Der oben aufgeführte Code kann Fehler enthalten. Kritik und Bemerkungen sind willkommen.

0

Stellen Sie sich vor, dass CustomView ein anderes UserControl (IndexPicker) enthält. IndexPicker hat auch ein ViewModel (IndexPickerViewModel), das für den Datenzugriff zuständig ist. Ich muss eine Eigenschaft ("Index") dieses IndexPickerViewModel an die vorherige Eigenschaft des CustomViewModels "Id" binden. Ich will es in StaticResources instanziiert und binden Sie es an die CustomViewModel (was ich glaube, dass die Datacontext nach meiner vorherigen Frage)

Wenn IndexPicker keine explizit Datacontext dann gesetzt hat IndexPickerwill inherit the datacontext von ihm übergeordnete Element ist. Sie können sich wahrscheinlich spüren

Index="{Binding Id, RelaticeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, FallbackValue={x:Null}}" 

Natürlich schon, dass diese unordentlich ist:

Allerdings, wenn IndexPicker bereits eine Datacontext hat, dann werden Sie relativ Quelle Bindung mit einem Vorfahren Suche verwenden.Die Standardeigenschaften eines UIElement oder Controls sind zwar ziemlich sicher (und üblich), aber wenn Sie anfangen, nach benutzerdefinierten Eigenschaften zu suchen, dann führen Sie Abhängigkeiten zwischen dem untergeordneten Steuerelement und seinem übergeordneten Element ein (wenn das untergeordnete Steuerelement nicht viel darüber wissen sollte) sein Elternteil), und Sie sind auch verpflichtet, zu irgendeinem Zeitpunkt verbindliche Fehler zu erhalten (daher die Verwendung eines Rückfallwertes).

+0

Vielen Dank für die Antwort. Ich habe gerade die Frage bearbeitet, weil sie nicht klar genug war ... – heliar

+0

@heliar Ich habe meine Antwort für Sie bearbeitet. – slugster

Verwandte Themen