2009-05-12 7 views
4

Je souhaite simuler le changement de données dans le modèle et que ces données soient reflétées dans XAML. Si je comprends bien, j'ai besoin d'implémenter INotifyPropertyChanged.Pourquoi INotifyPropertyChanged ne met-il pas à jour les variables en XAML?

Toutefois, dans l'exemple de code suivant, XAML affiche les données du client une seule fois mais la date et l'heure ne changent jamais.

Que dois-je modifier dans l'exemple suivant pour que XAML affiche continuellement la date et l'heure actuelles à mesure qu'elles changent dans le modèle?

En particulier, je ne comprends pas comment la reliure pourrait savoir quand vérifier le modèle, chaque seconde? 10 fois par seconde? Il n'y a aucun événement auquel il répond. Qu'est-ce que j'oublie ici?

XAML:

<Window x:Class="TestBinding99382.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:TestBinding99382" 
    Title="Window1" Height="300" Width="300"> 

    <Window.Resources> 
     <ObjectDataProvider 
       x:Key="DataSourceCustomer" 
       ObjectType="{x:Type local:ShowCustomerViewModel}" 
       MethodName="GetCurrentCustomer"/> 
    </Window.Resources> 

    <DockPanel DataContext="{StaticResource DataSourceCustomer}"> 
     <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> 
      <TextBlock Text="{Binding Path=FirstName}"/> 
      <TextBlock Text=" "/> 
      <TextBlock Text="{Binding Path=LastName}"/> 
     </StackPanel> 
     <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> 
      <TextBlock Text="{Binding Path=TimeOfMostRecentActivity}"/> 
     </StackPanel> 

    </DockPanel> 
</Window> 

code Derrière:

using System.Windows; 
using System.ComponentModel; 
using System; 

namespace TestBinding99382 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
     } 
    } 

    //view model 
    public class ShowCustomerViewModel : INotifyPropertyChanged 
    { 
     private Customer _currentCustomer; 

     public Customer CurrentCustomer 
     { 
      get 
      { 
       return _currentCustomer; 
      } 

      set 
      { 
       _currentCustomer = value; 
       this.RaisePropertyChanged("CurrentCustomer"); 
      } 
     } 

     public ShowCustomerViewModel() 
     { 
      _currentCustomer = Customer.GetCurrentCustomer(); 
     } 

     public Customer GetCurrentCustomer() 
     { 
      return _currentCustomer; 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
     private void RaisePropertyChanged(string property) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(property)); 
      } 
     } 
    } 

    //model 
    public class Customer : INotifyPropertyChanged 
    { 
     private string _firstName; 
     private string _lastName; 
     private DateTime _timeOfMostRecentActivity; 

     public string FirstName 
     { 
      get 
      { 
       return _firstName; 
      } 
      set 
      { 
       _firstName = value; 
       this.RaisePropertyChanged("FirstName"); 
      } 
     } 

     public string LastName 
     { 

      get 
      { 
       return _lastName; 
      } 
      set 
      { 
       _lastName = value; 
       this.RaisePropertyChanged("LastName"); 
      } 
     } 

     public DateTime TimeOfMostRecentActivity 
     { 

      get 
      { 
       return _timeOfMostRecentActivity; 
      } 
      set 
      { 
       _timeOfMostRecentActivity = value; 
       this.RaisePropertyChanged("TimeOfMostRecentActivity"); 
      } 
     } 

     public static Customer GetCurrentCustomer() 
     { 
      return new Customer 
        { FirstName = "Jim" 
         , LastName = "Smith" 
         , TimeOfMostRecentActivity = DateTime.Now}; 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
     private void RaisePropertyChanged(string property) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(property)); 
      } 
     } 
    } 
} 

Répondre

4

Peut-être que je ne reçois pas ce que vous voulez faire ce code.

Vous avez créé un objet client qui implémente INotifyPropertyChanged. Vous avez une autre classe qui est une fabrique pour le XAML pour accéder à l'instance. Vous créez maintenant une instance client avec des propriétés prédéfinies. La vue les affiche. Si vous ne modifiez pas les propriétés, la vue ne sera pas mise à jour.

J'ai ajouté un bouton à votre WPF Voir

<Button DockPanel.Dock="Bottom" 
     x:Name="UpdateTime" 
     Click="UpdateTime_Click"> 
       Update Activity Timestamp 
</Button> 

C# code-behind:

private void UpdateTime_Click(object sender, RoutedEventArgs e) 
{ 
    Customer.GetCurrentCustomer().TimeOfMostRecentActivity = DateTime.Now; 
} 

J'ai aussi changé le type de client pour créer une instance unique pour le client actuel

private static Customer _CurrentCustomer; 

public static Customer GetCurrentCustomer() 
{ 
    if (null == _CurrentCustomer) 
    { 
     _CurrentCustomer = new Customer 
     { FirstName = "Jim" 
      , LastName = "Smith" 
      , TimeOfMostRecentActivity = DateTime.Now 
     }; 
    } 
     return _CurrentCustomer; 
} 

Maintenant, chaque fois que je clique sur le bouton, la propriété DateTime est modifiée et la vue mises à jour automatiques en raison du mécanisme INotifyPropertyChanged. Le code semble fonctionner AFAISee.

+0

Aussi CustomerViewModel n'a pas besoin d'implémenter INotifyPropertyChanged. – Gishu

+0

Merci, cela m'a vraiment aidé à aller de l'avant, j'ai posté l'exemple que j'ai pu créer en fonction de vos suggestions ici: http://stackoverflow.com/questions/852441/fat-models-skinny-viewmodels-and-dumb-views -le meilleur-mvvm-approche –

0

Vous semblez lier à Customer et Customerne mettre en œuvre INotifyPropertyChanged. Essayez d'implémenter INotifyPropertyChanged sur Customer, et voir si cela résout. Cela nécessitera de passer aux propriétés manuelles (plutôt qu'aux propriétés implémentées automatiquement) afin de déclencher l'événement.

+0

Quelque chose comme ceci, en fait: http://stackoverflow.com/questions/839147/wpf-data-binding-to-changing-object/839164#839164 –

+0

Merci, mais j'ai changé le code ci-dessus pour que le client class implémente également INotifyChanged, mais cela ne fonctionne toujours pas. Quoi d'autre dois-je faire? –

+0

autant que je peux dire que ma classe de modèle met en œuvre INotifyPropertyChanged la même chose que la vôtre, êtes-vous peut-être lier différemment dans XAML? –

0

Votre classe de client doit implémenter INotifyPropertyChanged et vous devez appeler l'événement pendant les méthodes de définition des propriétés des clients.

Modifier: avez regardé votre code à nouveau, je me demande si votre classe ShowCustomerViewModel doit écouter l'événement _currentCustomer PropertyChanged et le transmet comme son propre PropertyChangedEvent. Ça vaut le coup.

+0

Merci, mais j'ai changé le code ci-dessus pour que la classe Customer implémente aussi INotifyChanged mais ça ne marche toujours pas. Quoi d'autre dois-je faire? –

+0

Mais comment l'objet Binding sait-il que "l'heure a changé" dans cet exemple? Est-ce qu'il réinstalle et observe constamment le modèle? –

0

Pouvez-vous vérifier combien de fois GetCurrentCustomer() est appelé? Peut-être qu'il recrée tout simplement de nouvelles instances tout le temps.

+0

Je mets un Console.Write dans le GetCurrentCustomer() du ViewModel et il est appelé une fois. Ce que j'attendrais réellement, mais partout où vous lisez sur la liaison XAML, MVVM, la grande particularité est que le XAML se mettra à jour en fonction des changements du modèle, c'est ce que j'essaye de recréer ici pour pouvoir le construire . –

3

Votre code fonctionne correctement. La date et l'heure actuelles ne seront pas mises à jour automatiquement par magie. Vous devez mettre en œuvre une minuterie pour le mettre à jour.

Pour vous pouvez ajouter exemple ceci à votre classe client:

private Timer _timer; 

public Customer() 
{ 
    _timer = new Timer(UpdateDateTime, null, 0, 1000); 
} 

private void UpdateDateTime(object state) 
{ 
    TimeOfMostRecentActivity = DateTime.Now; 
} 
+0

N'avez-vous pas besoin de synchroniser la modification de propriété avec Dispatcher puisque vous induisez une modification d'interface utilisateur à partir d'un thread différent du thread d'interface utilisateur? – flq

+0

Autant que je sache, WPF Databinding obtient la valeur sur le thread d'interface utilisateur après que PropertyChanged a été levé. – Botz3000

+0

L'objet de gestion ne sait pas qu'il est utilisé dans une application WPF. Donc non, nous ne devrions pas synchroniser avec WPF Dispatcher. – sacha

Questions connexes