2017-06-01 3 views
0

Ich erstelle ein benutzerdefiniertes Steuerelement "CustomAutoCompleteBox" (das von AutoCompleteBox erbt) mit einer Abhängigkeitseigenschaft "CurrentItem".Abhängigkeitseigenschaft wird nicht von ViewModel festgelegt

public static readonly DependencyProperty CurrentItemProperty = 
     DependencyProperty.Register("CurrentItem", typeof(CityEntity), typeof(CustomAutoCompleteBox), 
      new FrameworkPropertyMetadata(
       null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 

    public CityEntity CurrentItem 
    { 
     get { return (CityEntity)GetValue(CurrentItemProperty); } 
     set { SetValue(CurrentItemProperty, value); } 
    } 

Dieses benutzerdefinierte Steuerelement hat auch eine Eigenschaft "InternalCurrentItem".

public CityEntity InternalCurrentItem 
    { 
     get { return _internalCurrentCity; } 

     set 
     { 
      if (_internalCurrentCity == value) return; 

      _internalCurrentCity = value; 
      OnPropertyChanged(); 

      CurrentItem = value; 
     } 
    } 

Der Datacontext ist sich im Konstruktor definieren:

public VilleAutoCompleteBox() 
    { 
     DataContext = this; 

     ... 
    } 

und der Stil gesetzt Itemssource und SelectedItem wie folgt aus:

<Style TargetType="{x:Type infrastructure_controls:CustomAutoCompleteBox}" BasedOn="{StaticResource AutoCompleteBoxFormStyle}"> 
    <Setter Property="ItemsSource" Value="{Binding InternalItems, Mode=OneWay}" /> 
    <Setter Property="SelectedItem" Value="{Binding InternalCurrentItem, Mode=TwoWay}" /> 
    ... 
</Style> 

Zusammengefasst ist Itemssource binden an interne Eigenschaft "InternalItems" und SelectedItem sind an die interne Eigenschaft "InternalCurrentItem" gebunden.

Für die Verwendung es, erkläre ich diese CustomAutoCompleteBox wie folgt aus:

<infrastructure_usercontrols:CustomAutoCompleteBox Width="200" CurrentItem="{Binding DataContext.VmCurrentItem, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Mode=TwoWay}" /> 

Ich habe die Abhängigkeitseigenschaft „CurrentItem“ binden an die Ansichtsmodell Eigentum „VmCurrentItem“.

Alles funktioniert gut bis auf eine Sache.

Wenn ich Text in das Steuerelement einfüge, ändert sich die InternalCurrentItem-Eigenschaft ordnungsgemäß. Gleiches gilt für die CurrentItem-Eigenschaft in meinem ViewModel.

Konkret wird InternalCurrentItem korrekt geändert (Set). Diese Eigenschaft legt die CurrentItem-Abhängigkeitseigenschaft fest, und diese Abhängigkeitseigenschaft legt VmCurrentItem fest.

Das Gegenteil ist nicht wahr. Wenn ich den Wert der VmCurrentItem-Eigenschaft in dem ViewModel direkt ändere, wird die CurrentItem-Eigenschaft nicht geändert. Ich verstehe nicht warum.

+1

Als Hinweis, sollten Sie in der Regel festgelegt kein Datacontext die Kontrolle an sich selbst, weil sie verhindert, dass die Steuerung übernimmt die Datacontext sein Elternsteuerelement oder Fenster. Sie können leicht einen Beweis für diese Regel finden, wenn Sie sich die Komplexität Ihrer CurrentItem-Bindung ansehen. Schreiben Sie einfach die "internen" Bindungen des Controls mit RelativeSource. Siehe z.B. diese Antwort: https://stackoverflow.com/a/28982771/1136211 – Clemens

+0

Ich habe meinen Code nach Ihren Empfehlungen aktualisiert. Dies ist sauberer, behebt das Problem jedoch nicht. – StevenPF

Antwort

1

Der erste Fall verursacht die folgende Kette von Ereignissen:

  • SelectedItem
  • geändert wird InternalCurrentItem aktualisiert durch den Rahmen aufgrund der Bindung
  • Sie CurrentItem in dermanuell aktualisierenSetter
  • VmCurrentItem wird durch den Rahmen aktualisiert aufgrund der

In der entgegengesetzten Richtung dieser Bindung ist, was passiert:

  • VmCurrentItem
  • geändert wird
  • CurrentItem von Rahmen aktualisiert wird aufgrund die Bindung

... und das ist es.Es gibt keine Bindung und kein Stück Code, der InternalCurrentItem aktualisieren würde, wenn sich CurrentItem ändert. Also, was Sie tun müssen, ist eine PropertyChangedCallback für Ihre CurrentItemProperty zu registrieren, welche InternalCurrentItem aktualisieren:

public static readonly DependencyProperty CurrentItemProperty = 
    DependencyProperty.Register(
     "CurrentItem", 
     typeof(CityEntity), 
     typeof(CustomAutoCompleteBox), 
     new FrameworkPropertyMetadata 
     { 
      BindsTwoWayByDefault = true, 
      PropertyChangedCallback = CurrentItemPropertyChanged 
     }); 

private static void CurrentItemPropertyChanged(
    DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 
    var control = (CustomAutoCompleteBox)d; 
    control.InternalCurrentItem = (CityEntity)e.NewValue; 
} 
+0

Vielen Dank für Ihre sehr klare und präzise Antwort. Aber ich bringe ein wenig Korrektur. In der entgegengesetzten Richtung wird CurrentItem nicht aktualisiert :( Das ist der Punkt, den ich nicht verstehe. Wenn CurrentItem richtig geändert wurde, müsste ich InternalCurrentItem wie in Ihrem Beispiel manuell ändern. – StevenPF

+0

Ja, das habe ich gerade bemerkt. Erhöht Ihr View-Modell "PropertyChanged"? Und erhalten Sie verbindliche Nachrichten im Ausgabefenster? Wenn Sie eine Art von View-Model-Injection verwenden, stellen Sie sicher, dass Sie dieselbe View-Model-Instanz bearbeiten, die auch Ihr Steuerelement enthält . gebunden ist – Grx70

+0

Ja, erhöhen das Ansichtsmodell Property: öffentliche CityEntity VmCurrentItem { get {_vmCurrentItem zurückkehren;} gesetzt { if (_vmCurrentItem == Wert) return; _vmCurrentItem = Wert; OnPropertyChanged (() => VmCurrentItem); } } – StevenPF

0

Sie müssen die Eigenschaft die gleiche Art und Weise wie die erste erklären:

public static readonly DependencyProperty InternalCurrentItemProperty = 
     DependencyProperty.Register("InternalCurrentItem", typeof(CityEntity), typeof(CustomAutoCompleteBox), 
      new FrameworkPropertyMetadata(
       null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 

public CityEntity InternalCurrentItem 
{ 
    get{ return (CityEntity)GetValue(InternalCurrentItemProperty); } 

    set 
    { 
     SetValue(InternalCurrentItemProperty, value); 
    } 
} 
+0

Kleine zusätzliche Genauigkeit: Wenn ich den Wert der VmCurrentItem-Eigenschaft im ViewModel direkt ändere, wird die CurrentItem-Eigenschaft nicht geändert. Doch das Gegenteil ist der Fall. Wenn ich CurrentItem ändere, wird VmCurrentItem geändert. Das Problem stammt nicht von InternalCurrentItem. – StevenPF

Verwandte Themen