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开发者_如何学Go/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.
精彩评论