2013-05-09 15 views
7

Sono in difficoltà con il databinding sui miei controlli utente personalizzati. Ho creato un progetto di esempio per evidenziare il mio problema. Sono completamente nuovo a WPF ed essenzialmente MVVM, quindi portami con me ...Binding on DependencyProperty of Custom User Control non si aggiorna sulle modifiche

Ho creato una vista semplice che utilizza l'associazione di dati in due modi. L'associazione dati sul controllo integrato funziona bene. Il mio controllo personalizzato non ... Ho inserito un punto di interruzione nel PropertyChangedCallback del mio controllo. Viene colpito una volta all'avvio, ma poi mai più. Nel frattempo, l'etichetta che ho legato allo stesso valore sta contando felicemente.

Cosa mi manca? Il mio progetto di esempio segue:

La finestra principale:

<Window x:Class="WpfMVVMApp.MainWindow" 
     xmlns:local="clr-namespace:WpfMVVMApp" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Grid.DataContext> 
      <local:CountdownViewModel /> 
     </Grid.DataContext> 
     <Label Name="custName" Content="{Binding Path=Countdown.ChargeTimeRemaining_Mins}" Height="45" VerticalAlignment="Top"></Label> 
     <local:UserControl1 MinutesRemaining="{Binding Path=Countdown.ChargeTimeRemaining_Mins}" Height="45"></local:UserControl1> 
    </Grid> 
</Window> 

Ecco il mio modello:

namespace WpfMVVMApp 
{ 

    public class CountdownModel : INotifyPropertyChanged 
    { 
     private int chargeTimeRemaining_Mins; 
     public int ChargeTimeRemaining_Mins 
     { 
      get 
      { 
       return chargeTimeRemaining_Mins; 
      } 
      set 
      { 
       chargeTimeRemaining_Mins = value; 
       OnPropertyChanged("ChargeTimeRemaining_Mins"); 
      } 
     } 

     #region INotifyPropertyChanged Members 
     public event PropertyChangedEventHandler PropertyChanged; 
     private void OnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
     #endregion 
    } 
} 

Il ViewModel:

namespace WpfMVVMApp 
{ 
    public class CountdownViewModel 
    { 
     public CountdownModel Countdown { get; set; } 

     DispatcherTimer timer; 
     private const int maxMins = 360; 

     public CountdownViewModel() 
     { 
      Countdown = new CountdownModel { ChargeTimeRemaining_Mins = 60 }; 

      // Setup timers 
      timer = new DispatcherTimer(); 
      timer.Tick += new EventHandler(this.SystemChargeTimerService); 
      timer.Interval = new TimeSpan(0, 0, 1); 
      timer.Start(); 
     } 

     private void SystemChargeTimerService(object sender, EventArgs e) 
     { 
      //convert to minutes remaining 
      // DEMO CODE - TODO: Remove 
      this.Countdown.ChargeTimeRemaining_Mins -= 1; 
     } 
    } 
} 

Ecco il codice XAML per il mio controllo utente:

<UserControl x:Class="WpfMVVMApp.UserControl1" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"> 
    <Grid> 
     <Label Name="Readout"></Label> 
    </Grid> 
</UserControl> 

Ed ecco il codice dietro il controllo utente:

namespace WpfMVVMApp 
{ 
    public partial class UserControl1 : UserControl 
    { 
     #region Dependency Properties 
     public static readonly DependencyProperty MinutesRemainingProperty = 
        DependencyProperty.Register 
        (
         "MinutesRemaining", typeof(int), typeof(UserControl1), 
         new UIPropertyMetadata(10, new PropertyChangedCallback(minutesRemainChangedCallBack)) 
        ); 
     #endregion 

     public int MinutesRemaining 
     { 
      get 
      { 
       return (int)GetValue(MinutesRemainingProperty); 
      } 
      set 
      { 
       SetValue(MinutesRemainingProperty, value); 
      } 
     } 

     static void minutesRemainChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args) 
     { 
      UserControl1 _readout = (UserControl1)property; 
      _readout.MinutesRemaining = (int)args.NewValue; 

      _readout.Readout.Content = _readout.MinutesRemaining; 
     } 

     public UserControl1() 
     { 
      InitializeComponent(); 
     } 
    } 
} 

risposta

10

La modifica di richiamata è rompere il legame.

Come uno scheletro: nella tua finestra hai UC.X="{Binding A}" e poi in quella modifica proprietà (in UC) hai X=B;. Questo interrompe l'associazione poiché in entrambi i casi è stato impostato X.

per rettificare, rimuovere il cambiamento di callback e aggiungere questo all'etichetta:

Content="{Binding MinutesRemaining, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" 
+0

Grazie per la risposta. Vincolare quello era lavoro. Tuttavia, il motivo per cui ho creato la proprietà di dipendenza è perché voglio fare di più che impostare un'etichetta nel mio programma attuale (questo era solo un semplice esempio). Ora vedo che stavo assegnando un valore a MinutesRemaining nel mio callback di modifica, che interrompe il binding originale. Ho commentato quella frase e ora funziona come mi aspettavo. – user2367999

2

Ho cercato il tuo codice funziona bene l'unico cambiamento che ho fatto è stato quello di rimuovere il codice dietro PropertyChangedCallback hai e DataBind l'etichetta (lettura) alla proprietà di dipendenza.

UserControl (XAML)

<UserControl x:Class="WpfApplication1.UserControl" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300"> 
    <Grid> 
     <Label Name="Readout" Content="{Binding RelativeSource={RelativeSource 
          AncestorType=UserControl}, Path=MinutesRemaining}"/> 
    </Grid> 
</UserControl> 

UserControl (codice dietro)

public partial class UserControl1 : UserControl 
{ 
    #region Dependency Properties 
    public static readonly DependencyProperty MinutesRemainingProperty = 
       DependencyProperty.Register 
       (
        "MinutesRemaining", typeof(int), typeof(UserControl1), 
        new UIPropertyMetadata(10) 
       ); 
    #endregion 

    public int MinutesRemaining 
    { 
     get 
     { 
      return (int)GetValue(MinutesRemainingProperty); 
     } 
     set 
     { 
      SetValue(MinutesRemainingProperty, value); 
     } 
    } 

    public UserControl1() 
    { 
     InitializeComponent(); 
    } 
}