views:

645

answers:

3

I finally got a Silverlight MVVM example to work so that when I change the values of first name and last name text boxes, the full names change automatically.

However, and strangely, my model which inherits from INotifyPropertyChanged is only notified if I change at least 2 characters of either the first or last name.

  • if I change "Smith" to "Smith1", then no event is fired
  • if I change "Smith" to "Smith12" then the event is fired, as expected

Has anyone run in to this before in Silverlight/XAML/INotifyPropertyChanged? What could it be? Is there a setting somewhere that indicates how much of a textbox needs to change before it notifies itself as "changed"?

Here are the main parts of the code I'm using:

Customer.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace TestMvvm345.Model
{
    public class Customer : INotifyPropertyChanged
    {
        public int ID { get; set; }
        public int NumberOfContracts { get; set; }

        private string firstName;
        private string lastName;

        public string FirstName
        {
            get { return firstName; }
            set
            {
                firstName = value;
                RaisePropertyChanged("FirstName");
                RaisePropertyChanged("FullName");
            }
        }

        public string LastName
        {
            get { return lastName; }
            set
            {
                lastName = value;
                RaisePropertyChanged("LastName");
                RaisePropertyChanged("FullName");
            }
        }

        public string FullName
        {
            get { return firstName + " " + lastName; }
        }

        #region INotify
        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        } 
        #endregion

    }
}

CustomerHeaderView.xaml:

<UserControl x:Class="TestMvvm345.Views.CustomerHeaderView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel HorizontalAlignment="Left">
            <ItemsControl ItemsSource="{Binding}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBox x:Name="FirstName" 
                                Text="{Binding Path=FirstName, Mode=TwoWay}" 
                                Width="150" 
                                Margin="3 5 3 5"/>
                            <TextBox x:Name="LastName" 
                                Text="{Binding Path=LastName, Mode=TwoWay}" 
                                Width="150"
                                Margin="0 5 3 5"/>
                            <TextBlock x:Name="FullName" 
                                Text="{Binding Path=FullName, Mode=TwoWay}" 
                                Margin="0 5 3 5"/>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </Grid>
</UserControl>

CustomerViewModel.cs:

using System.ComponentModel;
using System.Collections.ObjectModel;
using TestMvvm345.Model;

namespace TestMvvm345
{
    public class CustomerViewModel
    {
        public ObservableCollection<Customer> Customers { get; set; }

        public void LoadCustomers()
        {
            ObservableCollection<Customer> customers = new ObservableCollection<Customer>();

            //this is where you would actually call your service
            customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23 });
            customers.Add(new Customer { FirstName = "Jane", LastName = "Smith", NumberOfContracts = 22 });
            customers.Add(new Customer { FirstName = "John", LastName = "Tester", NumberOfContracts = 33 });
            customers.Add(new Customer { FirstName = "Robert", LastName = "Smith", NumberOfContracts = 2 });
            customers.Add(new Customer { FirstName = "Hank", LastName = "Jobs", NumberOfContracts = 5 });

            Customers = customers;
        }

    }
}

MainPage.xaml.cs:

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    CustomerViewModel customerViewModel = new CustomerViewModel();
    customerViewModel.LoadCustomers();
    CustomerHeaderView.DataContext = customerViewModel.Customers;
}

UPDATE:

I remade this project in WPF and it works fine. Perhaps it's a Silverlight 3 issue.

+2  A: 

Using your example code it works perfectly for me. I make a single character change and then move focus and the FullName update correctly happens

As an aside your usage of NotifyPropertyChanged has a flaw

    public string FirstName
    {
        get { return firstName; }
        set
        {
            firstName = value;
            RaisePropertyChanged("FirstName");
            RaisePropertyChanged("FullName");
        }
    }

should be:

    public string FirstName
    {
        get { return firstName; }
        set
        {
            if (firstName != value)
            {
                firstName = value;
                RaisePropertyChanged("FirstName");
                RaisePropertyChanged("FullName");
            }
        }
    }

You want to avoid causing the event to fire and associated rebinding from happening on non-changes.

Graeme Bradbury
ok good to know, I'm using silverlight 3 so will try it on another machine with silverlight 2 when I get a chance.
Edward Tanguay
A: 

You might also want to include 'UpdateSourceTrigger=PropertyChanged' to the Binding statement. i.e.

Text="{Binding Path=FirstName,UpdateSourceTrigger=PropertyChanged}"

This would ensure that the value is updated everything time you make a change in the textbox.

Martin Randall
when I put that in for either FirstName or LastName, everything is blank, odd. I'm using Silverlight 3 beta 1, perhaps that is the problem, couldn't reinstall Silverlight 2 tools so will check it on another machine later, thanks!
Edward Tanguay
internet explorer tells me more: "invalid attribute value: UpdateSourceTrigger=PropertyChanged"
Edward Tanguay
UpdateSourceTrigger is a WPF only thingFor a Silverlight workaround http://silverlight.net/forums/t/11547.aspx
Graeme Bradbury
A: 

I've notice the same problem in Silverlight 2. I think this is a bug. Here's my bug report (currently unanswered):

http://silverlight.net/forums/t/59207.aspx

Michael L Perry