INotifyPropertyChanged Event is not raising - wpf

I had a Model class named as FormModel.cs while coding WPF using C#..
Below is my code. I am clueless that even though debugger is going into setter method of Name, PropertyChanged flag is not raising.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace MVVM.Models
{
public class FormModel : INotifyPropertyChanged
{
private string _name;
public FormModel()
{
_name = "";
_bColor = "";
_fColor = "";
}
public string Name
{
get { return _name; }
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
private string _bColor;
public string BColor
{
get { return _bColor; }
set { _bColor = "RED"; }
}
private string _fColor;
public string FColor
{
get { return _fColor; }
set { _fColor = "BLUE"; }
}
public void apply(string Name, string BColor, string FColor)
{
this.Name = Name;
this.BColor = BColor;
this.FColor = FColor;
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if(this.PropertyChanged!=null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
if(propName=="Name")
{
MessageBox.Show("Hi" + Name);
}
}
}
}
}
Here are remaining Files I edited in question in quest on request
MainViewModel.cs
using MVVM.Models;
using MVVM.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System.Windows;
namespace MVVM.ViewModels
{
public class MainViewModel : ViewModelBase
{
private FormModel FModel;
private ICommand refresh;
public ICommand Refresh
{
get { return refresh; }
set { refresh = value; }
}
public MainViewModel()
{
FModel = new FormModel();
string s = "Pratik";
object o=(object)s;
refresh = new RelayCommand(new Action<object>(getGreet));
}
public void getGreet(object s1)
{
FModel.apply("dsf", "sf", "sf");
MessageBox.Show(s1.ToString());
}
private string _name;
public string Name
{
get { return _name; }
set {
_name = value;
FModel.Name = value;
}
}
}
class RelayCommand : ICommand
{
private Action<object> _action;
public RelayCommand(Action<object> action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter);
}
}
}
ViewModelBase.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace MVVM.ViewModels
{
public abstract class ViewModelBase: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler!=null)
{
if (propertyName == "Name")
{
//Command
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
}
Below are View Files
MainView.xaml
<Window x:Class="MVVM.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:MVVM.ViewModels"
Title="UI" Height="300" Width="300">
<Grid Background="RED">
<TextBox Name="TT" TextWrapping="Wrap" Text="{Binding Name}" Margin="20,15,160,225" />
<TextBox TextWrapping="Wrap" Text="Back Color" Margin="20,73,195,167" />
<TextBox TextWrapping="Wrap" Text="Font Color" Margin="20,122,195,118"/>
<Label Content="getGreet" HorizontalAlignment="Left" Margin="187,90,0,0" VerticalAlignment="Top"/>
<Button Content="Finish" Command="{Binding Refresh}" HorizontalAlignment="Left" Margin="112,163,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
MainView.xaml.cs
//using MVVM.Models;
using MVVM.ViewModels;
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.Shapes;
namespace MVVM.Views
{
/// <summary>
/// Interaction logic for UI.xaml
/// </summary>
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
}
And Main Files...
App.xaml
<Application x:Class="MVVM.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="OnStartup">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs
using MVVM.ViewModels;
using MVVM.Views;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
namespace MVVM
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public void OnStartup(object sender, StartupEventArgs e)
{
// Create the ViewModel and expose it using the View's DataContext
MainView view = new MainView();
view.DataContext = new MainViewModel();
view.Show();
}
}
}
Please help.

You are binding to the Name property of your MainViewModel not the Name property of your Model. Even thought this is essentially a passthrough to the Model.Name property, the binding is attaching to the PropertyChanged event of the MainViewModel which doesn't get raised in your setter.
Add an OnPropertyChanged call in the setter of your MainViewModel.Name property and it should work.
public string Name
{
get { return _name; }
set
{
_name = value;
FModel.Name = value;
OnPropertyChanged("Name");
}
}

Related

Validation of a column entry to a dynamically created DataGrid - compiles but displays nothing

Following many examples here, I created a dynamically created DataGrid from a collection. When I include the DataValidation component for one of the columns, the following code compiles, but displays nothing. Without the validation component, it works. I would appreciate it very much if an experienced member could point out my error.
Thank very much.
i. konuk
XAML:
<Window x:Class="SimpleGridX.MainWindow"
xmlns:local="clr-namespace:SimpleGridX"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ScrollViewer HorizontalAlignment="Left" Margin="10,10,0,0"
VerticalAlignment="Top" Width="497" Height="299"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" >
<DataGrid Name="dgSimpleX" AutoGenerateColumns="True"
SelectionUnit="FullRow" SelectionMode="Extended"
CanUserReorderColumns="False" CanUserAddRows="True">
<DataGridTextColumn Header="Age">
<DataGridTextColumn.Binding>
<Binding Path="[Age]">
<Binding.ValidationRules>
<local:MyValidationRule />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
</DataGrid>
</ScrollViewer>
</Grid>
</Window>
Window code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.ComponentModel;
namespace SimpleGridX
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
// Create an instance of the PersonCollection class
PersonCollection people =
new PersonCollection();
InitializeComponent();
dgSimpleX.SelectionUnit = DataGridSelectionUnit.CellOrRowHeader;
dgSimpleX.ItemsSource = people;
dgSimpleX.Items.SortDescriptions.Add(new SortDescription("FirstName", ListSortDirection.Descending));
}
}
}
Object Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Windows.Controls;
using System.Globalization;
namespace SimpleGridX
{
class Person : INotifyPropertyChanged
{
private string firstName;
private string lastName;
private float age;
private string occupation;
// Each property calls the OnPropertyChanged method
// when its value changed, and each property that
// affects the Person's Description, also calls the
// OnPropertyChanged method for the Description property.
public string FirstName
{
get
{
return firstName;
}
set
{
if (firstName != value)
{
firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("Description");
}
}
}
public string LastName
{
get
{
return lastName;
}
set
{
if (this.lastName != value)
{
this.lastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("Description");
}
}
}
public float Age
{
get
{
return age;
}
set
{
if (this.age != value)
{
this.age = value;
OnPropertyChanged("Age");
OnPropertyChanged("Description");
}
}
}
public string Occupation
{
get { return occupation; }
set
{
if (this.occupation != value)
{
this.occupation = value;
OnPropertyChanged("Occupation");
OnPropertyChanged("Description");
}
}
}
// The Description property is read-only,
// and is composed of the values of the
// other properties.
public string Description
{
get
{
return string.Format("{0} {1}, {2} ({3})",
firstName, lastName, age, occupation);
}
}
// The ToString returns the Description,
// so that it is displayed by default when
// the Person object is a binding source.
public override string ToString()
{
return Description;
}
#region INotifyPropertyChanged Members
/// Implement INotifyPropertyChanged to notify the binding
/// targets when the values of properties change.
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(
string propertyName)
{
if (this.PropertyChanged != null)
{
// Raise the PropertyChanged event
this.PropertyChanged(
this,
new PropertyChangedEventArgs(
propertyName));
}
}
#endregion
}
#region ValidationRule
public class MyValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
float myage = (float)0.0;
try
{
if (((string)value).Length > 0)
{
myage = (float)Decimal.Parse((string)value, NumberStyles.Any, cultureInfo);
}
}
catch
{
return new ValidationResult(false, "Illegal ccharacters");
}
if ((myage < (float)0.0) || (myage > (float)75.0))
{
return new ValidationResult(false, "Not in range");
}
else
{
return new ValidationResult(true, null);
}
}
}
#endregion
}
Collection:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
namespace SimpleGridX
{
class PersonCollection : ObservableCollection<Person>
{
public PersonCollection()
{
// Load the collection with dummy data
//
Add(new Person()
{
FirstName = "Elin",
LastName = "Binkles",
Age = 26,
Occupation = "Professional"
});
Add(new Person()
{
FirstName = "Samuel",
LastName = "Bourts",
Age = 28,
Occupation = "Engineer"
});
Add(new Person()
{
FirstName = "Alan",
LastName = "Jonesy",
Age = 37,
Occupation = "Engineer"
});
Add(new Person()
{
FirstName = "Sam",
LastName = "Nobles",
Age = 25,
Occupation = "Engineer"
});
}
}
}
The Age property should be bound without square brackets.
Also.
It'll be bound to two columns if you leave autogenerate true.
Edit:
Also.
You need any columns inside a columns collection.
What you have there produces an error when I try it, because it tries to make your age column into an item and then the code tries to set itemssource.
<DataGrid.Columns>
<DataGridTextColumn Header="Age">
……
</DataGrid.Columns>

Prism 5 and Unity - Manually resolve View with ViewModel wired up

Using: WPF, Prism 5, Unity
I am trying to write what I call a "WindowDialogService" which, when passed a type and called, will open a Window with that type as the content. My problem is I cannot get the ViewModel to instantiate and associate to the View.
I don't know if it is right but I am using AutoWireViewModel in my View and was assuming (obviously incorrectly) that the ViewModel would be "Located" when resolving using the Unity Container i.e. container.TryResolve <T> ()
So basically, I can resolve the View but the DataContext of the view is null.
I have included all relevant code below.
View
<dxc:DXWindow x:Class="ListClassList.Views.AddToClassView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:dxe="clr-namespace:DevExpress.Xpf.Editors.Settings;assembly=DevExpress.Xpf.Core.v15.2"
xmlns:dxeditors="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxgt="http://schemas.devexpress.com/winfx/2008/xaml/grid/themekeys"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:extToolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
WindowStartupLocation="CenterScreen"
dxc:ThemeManager.ThemeName="VS2010"
Title="{Binding Title}"
Width="800"
Height="500"
ResizeMode="CanResizeWithGrip"
>
<Grid>
</Grid>
</dxc:DXWindow>
ViewModel
using MyApp.Data;
using MyApp.Models.Model;
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
namespace ListClassList.ViewModels
{
public class AddToClassViewViewModel : BindableBase
{
private readonly MyAppDbContext _context;
private ObservableCollection<MasterClass> _masterClasses;
public ObservableCollection<MasterClass> MasterClasses
{
get { return _masterClasses; }
set { SetProperty(ref _masterClasses, value); }
}
private ObservableCollection<Student> _students;
public ObservableCollection<Student> Students
{
get { return _students; }
set
{
SetProperty(ref _students, value);
}
}
public DelegateCommand FinishCommand { get; set; }
public AddToClassViewViewModel(IStudentTimetableService studentTimetableService)
{
}
}
}
IWindowDialogService Interface
using System;
using System.Windows;
namespace MyApp.Infrastructure.Services.Dialogs.WindowDialog
{
public interface IWindowDialogService
{
bool? ShowDialog<T>(Action onDialogClosed) where T : Window;
}
}
WindowDialogService Implementation
using System;
using System.Collections.Generic;
using System.Windows;
using Microsoft.Practices.ServiceLocation;
using Prism.Unity;
namespace MyApp.Infrastructure.Services.Dialogs.WindowDialog
{
public sealed class WindowDialogService : IWindowDialogService
{
private readonly List<Window> _openWindows = new List<Window>();
private static volatile WindowDialogService _instance;
private static readonly object SyncRoot = new Object();
private WindowDialogService() { }
public static WindowDialogService Instance
{
get
{
if (_instance == null)
{
lock (SyncRoot)
{
if (_instance == null)
_instance = new WindowDialogService();
}
}
return _instance;
}
}
public bool? ShowDialog<T>(Action onDialogClosed) where T : Window
{
try
{
using (var container = new Microsoft.Practices.Unity.UnityContainer())
{
var dialog = (Window)container.TryResolve <T> ();
dialog.Closed += (s, e) => onDialogClosed();
var result = dialog.ShowDialog();
return result;
}
}
catch (Exception)
{
throw;
}
}
}
}
Prism 6 does this automatically, for Prism 5 you need something along the lines of
ViewModelLocationProvider.SetDefaultViewModelFactory( type => Container.Resolve( type ) );
in your bootstrapper's ConfigureContainer

DataContext in Window_Loaded

I'm very new to WPF. I'm currently doing a code to detect joints coordinate using the Kinect SDK and displaying on a simple textbox in WPF. The code to detect joints are in a private void Window_Loaded(object sender, RoutedEventArgs e) method. To display the coordinates, I used DataContext. Without further ado, let's just see the XAML code:
<Window x:Class="Prototype.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="480" Width="640">
<Grid>
<TextBox x:Name="coordinateText" Width="150" Height="20" Margin="441,409,27,12" Text="{Binding Path=xInfo}"/>
</Grid>
And this is my C# code:
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 Microsoft.Research.Kinect.Nui;
using Coding4Fun.Kinect.Wpf;
namespace Prototype
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//this.DataContext = new Coordinate { xInfo = "5" };
}
Runtime nui = new Runtime();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new Coordinate { xInfo = "5" };
nui.Initialize(RuntimeOptions.UseSkeletalTracking); //code for detecting joints
//some code for detecting joints
}
public class Coordinate
{
public string xInfo { get; set; }
public string yInfo { get; set; }
public string zInfo { get; set; }
}
}
}
The thing is the information will not be loaded in the textbox if this.DataContext = new Coordinate { xInfo = "5" }; is not placed in the MainWindow. I have to put it in the Window_Loaded method. Any solutions?
As Coder323 said When window is loaded you need tell WPF TextBox that the Value of the variable xInfo is changed so you should use INotifyPropertyChanged in your Model Class
then where ever you change theValue of your Object it will pick up the changed Value... also
Just Set the DataContext=myCordinate in the Window Constructor then, make my cordinate a variable in the window class.
public class Coordinate : INotifyPropertyChanged
{
private string xInfo;
public string XInfo {
get{retun value};
set{
xInfo=Value;
FirePropertyChanged("XInfo")
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void FirePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Do this for other properties and now you can set the value of myCordinate.XInfo="what ever you like" in any event it will notify to your view that the respective property has changed..
I am putting my complete solution here
My Coordinate class
public class Coordinates : INotifyPropertyChanged
{
private string xInfo;
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public string XInfo
{
get { return xInfo; }
set
{
xInfo = value;
InvokePropertyChanged(new PropertyChangedEventArgs("XInfo"));
}
}
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
#endregion
}
My Xaml
<Window x:Class="TestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<TextBox Text="{Binding Path=XInfo}" Height="30" Widht="100"></TextBox>
</Grid>
My Xaml.cs
namespace TestApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private Coordinates myCoordinates;
public MainWindow()
{
myCoordinates=new Coordinates();
this.DataContext = myCoordinates;
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
myCoordinates.XInfo = "Acbd";
}
}
}
And yes this test Project i made... is working
This might Help :)

WebBrowser in WPF using MVVM pattern

I am trying to open HTML file in WebBrowser window of WPF using MVVM patten.
Note: I have fixed the issues i was getting. Now this code works as desired.
ViewHTMLPageView.xaml
<Window x:Class="MyProject.ViewHTMLPageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject.Utility"
Title="HTML Report" Height="454" Width="787"
>
<Grid Name="grid1">
<WebBrowser local:WebBrowserUtility.BindableSource="{Binding ReportPage}" />
</Grid>
</Window>
ViewHTMLPageViewModel.cs
namespace MyProject
{
public class ViewHTMLPageViewModel: ViewModelBase
{
public ViewHTMLPageView()
{
//Testing html page on load
_reportPage = "<table border='5'><tr><td> This is sample <b> bold </b></td></tr></table>";
OnPropertyChanged("ReportPage");
}
private string _reportPage;
public string ReportPage
{
get
{
return _reportPage;
}
set
{
if (_reportPage != value)
{
_reportPage = value;
OnPropertyChanged("ReportPage");
}
}
}
}
WebBrowserUtility.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
namespace MyProject.Utility
{
public static class WebBrowserUtility
{
public static readonly DependencyProperty BindableSourceProperty =
DependencyProperty.RegisterAttached("BindableSource", typeof(string),
typeof(WebBrowserUtility), new UIPropertyMetadata(null,
BindableSourcePropertyChanged));
public static string GetBindableSource(DependencyObject obj)
{
return (string)obj.GetValue(BindableSourceProperty);
}
public static void SetBindableSource(DependencyObject obj, string value)
{
obj.SetValue(BindableSourceProperty, value);
}
public static void BindableSourcePropertyChanged(DependencyObject o,
DependencyPropertyChangedEventArgs e)
{
var webBrowser = (WebBrowser)o;
webBrowser.NavigateToString((string)e.NewValue);
}
}
}
In WebBrowserUtility.cs, change the following statement:
using System.Windows.Forms;
to
using System.Windows.Controls;
Now, for +1 on your question, can you tell me why this fixes your problem?

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.

Resources