4

I want to simulate data changing in the model and having that data be reflected in XAML. As I understand I need to implement INotifyPropertyChanged.

However, in the following code example, XAML displays the data from the customer only once but the date and time never changes.

What do I have to change in the following example to make XAML continually display the current date and time as it changes in the model?

In particular, I don't understand how the Binding would know when to check the model, every second? 10 times a second? There is no event that it is responding to. What am I missing here?

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 Behind:

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));
            }
        }
    }
}
Edward Tanguay
  • 189,012
  • 314
  • 712
  • 1,047

5 Answers5

4

Maybe I'm not getting what you wish this code to do.

You have created a Customer Object which implements INotifyPropertyChanged. You have another class which is a factory for the XAML to get to the instance. Now you create a Customer instance with predefined properties. The View displays them. Unless you change the properties somehow, the View will not update.

I added a button to your WPF View

<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;
}

I also changed Customer type to create a single instance for Current Customer

private static Customer _CurrentCustomer;

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

Now each time I click the button, the DateTime Property is modified and the view auto-updates due to the INotifyPropertyChanged mechanism. The code seems to be working AFAISee.

Gishu
  • 134,492
  • 47
  • 225
  • 308
  • Also CustomerViewModel doesnt need to implement INotifyPropertyChanged. – Gishu May 12 '09 at 08:27
  • Thanks, this really helped me move forward, I posted the example I was able to create based on your suggestions here: http://stackoverflow.com/questions/852441/fat-models-skinny-viewmodels-and-dumb-views-the-best-mvvm-approach – Edward Tanguay May 12 '09 at 12:19
3

Your code is working correctly. The current date and time will not update automatically by magic. You have to implement some timer to update it.

For exemple you could add this to your Customer class:

private Timer _timer;

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

private void UpdateDateTime(object state)
{
    TimeOfMostRecentActivity = DateTime.Now;
}
Sacha Bruttin
  • 4,083
  • 2
  • 21
  • 17
  • Wouldn't you have to snychronize the property change with the Dispatcher since you are causing a UI change from a thread different than the UI thread? – flq May 12 '09 at 08:39
  • As far as i know, WPF Databinding gets the value on the UI thread anyway after PropertyChanged has been raised. – Botz3000 May 12 '09 at 09:25
  • The business object is not aware that he is used on an WPF application. So no, we should not synch with the WPF Dispatcher. – Sacha Bruttin May 12 '09 at 10:03
  • Thanks, the timer is a cool addition to what I was trying to do, I posted a complete example of using it here: http://stackoverflow.com/questions/852441/fat-models-skinny-viewmodels-and-dumb-views-the-best-mvvm-approach – Edward Tanguay May 12 '09 at 12:20
0

You seem to be binding to Customer, and Customer doesn't implement INotifyPropertyChanged. Try implementing INotifyPropertyChanged on Customer, and see if that fixes it. This will require moving to manual properties (rather than automatically implemented properties) in order to raise the event.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
0

Your customer class needs to implement INotifyPropertyChanged and you need to invoke the event during the setter methods of the properties of Customers.

Edit: Have looked at you code again, I wonder whether your ShowCustomerViewModel class needs to listen to the _currentCustomer PropertyChanged event and forward it as its own PropertyChangedEvent. Worth a shot.

AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306
  • Thanks, but I changed the code above so that the Customer class implements INotifyChanged as well but it still doesn't work. What else do I have to do? – Edward Tanguay May 12 '09 at 07:48
  • But how does the Binding object know that the "time as changed" in this example? Does it constantly reinstantiate and observe the model? – Edward Tanguay May 12 '09 at 07:57
0

Can you check how many times GetCurrentCustomer() gets called? Maybe it just re-creates new instances all the time.

Vlagged
  • 503
  • 5
  • 17
  • I put a Console.Write in the GetCurrentCustomer() of the ViewModel and it gets called once. Which is what I would expect actually, but everywhere you read about XAML binding, MVVM, the great feature is that the XAML will update itself based on changes in the model, that is what I am trying to recreate here so I can build on it. – Edward Tanguay May 12 '09 at 08:00