observable collection not getting updated on UI change

I am trying to bind an observable collection to a user control but it is not getting updated on user change but it is getting updated when the user control is changed through code. Following is an example i tried. It might be a bit long but it is working so you can copy and paste the code as it is.

Please see my question at the end of the post.

--Customer.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace TestMVVM
{
    class Customer : INotifyPropertyChanged
    {
        private string firstName;
        private string lastName;

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

                }
            }
        }

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

        #region PropertChanged Block
        public event PropertyChangedEventHandler PropertyChanged;

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

--UCTextBox.xaml

<UserControl x:Class="TestMVVM.UCTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="40" Width="200">
<Grid>
    <TextBox Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="txtTextControl" 
             VerticalAlignment="Top" Width="120" />
</Grid>

--UCTextBox.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace TestMVVM
{
    /// 
    /// Interaction logic for UCTextBox.xaml
    /// 
    public partial class UCTextBox : UserControl, INotifyPropertyChanged
    {
        public UCTextBox()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(UCTextBox),
        new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(textChangedCallBack)));

        static void textChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
        {
            UCTextBox pasTextBox = (UCTextBox)property;
            pasTextBox.txtTextControl.Text = (string)args.NewValue;
        }

        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
                NotifyPropertyChanged("Text");
            }
        }

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}

-- Window1.xaml

<Window x:Class="TestMVVM.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestMVVM"
Title="Window1" Height="300" Width="300">
<Grid>
    <local:UCTextBox x:Name="txtUC" />
    <Button Height="23" HorizontalAlignment="Left" Margin="39,0,0,82" 
            Name="btnUpdate" VerticalAlignment="Bottom" Width="75" Click="btnUpdate_Click">Update</Button>
    <Button Height="23" Margin="120,0,83,82" Name="btnChange" VerticalAlignment="Bottom" Click="btnChange_Click">Change</Button>
</Grid>

-- Window1.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace TestMVVM
{
    /// 
    /// Interaction logic for Window1.xaml
    /// 
    public partial class Window1 : Window
    {
        CustomerHeaderViewModel customerHeaderViewModel = null;
        public Window1()
        {
            InitializeComponent();

            customerHeaderViewModel = new CustomerHeaderViewModel();
            customerHeaderViewModel.LoadCustomers();

            txtUC.DataContext = customerHeaderViewModel.Customers[0];

            Binding binding = new Binding();
            binding.Source = customerHeaderViewModel.Customers[0];
            binding.Path = new System.Windows.PropertyPath("FirstName");
            binding.Mode = BindingMode.TwoWay;

            binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

            txtUC.SetBinding(UCTextBox.TextProperty, binding);
        }

        private void btnUpdate_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(customerHeaderViewModel.Customers[0].FirstName);
        }

        private void btnChange_Click(object sender, RoutedEventArgs e)
        {
            txtUC.Text = "Tom";
        }
    }

    class CustomerHeaderViewModel
    {
        public ObservableCollection Customers { get; set; }

        public void LoadCustomers()
        {
            ObservableCollection customers = new ObservableCollection();

            customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23 });

            Customers = customers;
        }
    }
}

When i run the Window1.xaml my user control shows the data as "Jim". Now when i change the text to say "John" and click on Update, the messagebox still shows "Jim" that means the observable collection is not getting updated. When i click on Change button the user control changes the data to "Tom". Now when i click on Update button the Messagebox shows "Tom". Can anyone please tell me how to achieve the updation of observable collection by changing the data in user control rather than through code?


That's because you're not handling the txtTextControl.TextChanged event, so your Text dependency property is never updated.

Anyway, you don't need to handle that manually with a DependencyPropertyChangedCallback and an event handler, you can just bind the txtTextControl.Text to the Text dependency property :

<TextBox Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="txtTextControl" 
         VerticalAlignment="Top" Width="120"
         Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UCTextBox}}}"/>

An observable collection, observers the collection only. You will get notified when items are added or deleted not when fields of a single items did change. That is something completely different. Like Thomas Levesque said, you just need to bind the right property.

链接地址: http://www.djcxy.com/p/46570.html

上一篇: Windows服务中的System.Threading.Timer时间间隔

下一篇: 可观察的集合没有得到UI更改的更新