WPF contains great things like Bindings and interface INotifyPropertyChanged. This two things allow forget the hell from WinForms and almost all previous UI technologies, where you spend 90% of your time for developing and maintenancing UI code (like updates in UI fields, hide or show some controls etc.) If you still don’t know what is it (hmmm, you develop in WPF and don’t know what is “binding”?), you can read about them, for example, here. And I want talk about problem named “magic-strings”.
How it was
So, how usually properties’ implementations look when you use INotifyPropertyChanged? Something like this:public class Author
: INotifyPropertyChanged
{
#region Properties
#region FirstName
private string _FirstName;
public string FirstName
{
get { return _FirstName; }
set
{
if (_FirstName == value)
return;
_FirstName = value;
RaisePropertyChanged("FirstName");
}
}
#endregion
#endregion
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public class AuthorViewModel
{
public AuthorViewModel()
{
Author = new Author();
Author.PropertyChanged += Author_PropertyChanged;
}
public Author Author { get; private set; }
void Author_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "FirstName":
MessageBox.Show("Author.FirstName was changed");
break;
}
}
}
The problem is the same – you can make typo error or you can change name of this property later. Also, I don’t like how “switch” code looks like. So, what to do?
The answer is – Lambdas!
How it is
I want to warn all optimizators and other perfectionists. Yes, I know that my code below works slower than “magic-strings”. But I prefer more robust code than faster. If your code is fast but it works wrong – do you really need it?I saw a lot of articles where another guys describe the same decision with RaisePropertyChanged() method, and Prism also uses this approach. But I never saw before usage of the same principle in PropertyChanged event’s handler.
OK, sorry for a lot of words, here is how the same code looks after.
public class Author
: NotificationObject
{
#region Properties
#region FirstName
private string _FirstName;
public string FirstName
{
get { return _FirstName; }
set
{
if (_FirstName == value)
return;
_FirstName = value;
RaisePropertyChanged(() => FirstName);
}
}
#endregion
#endregion
}
void Author_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
On.PropertyChanged(e, (Author a) => a.FirstName, () => MessageBox.Show("Author.FirstName was changed"));
}
All necessary code you can download by links in the end of this article.
Fast property implementation
It’s terrible to write every time the same code for implementation of properties. So, if you use ReSharper, you can download code template for it, and implement properties with just some keystrokes. All you should do:- Type “pcp” (without quotes) + press Tab
- Type property’s type + press Tab
- Type property’s name + press Tab
You can import that code template by VS main menu –> ReSharper –> Live Templates… –> Import…
Typical use cases
Raise property change notification after changes of another property
For example, class Author has properties FirstName, LastName and FullName, which is equal to FirstName + LastName. When FirstName or LastName is changed, of course you want to notify about FullName changes also. You can do this 2 ways:1. Add additional code to FirstName’s and LastName’s setters:
public string FirstName
{
get { return _FirstName; }
set
{
if (_FirstName == value) return;
_FirstName = value;
RaisePropertyChanges(() => FirstName);
RaisePropertyChanges(() => FullName);
}
}
public class Author
{
public Author
{
PropertyChanged += Author_PropertyChanges;
}
void Author_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
On.PropertyChanged(e, () => FirstName, () => RaisePropertyChanged(() => FullName));
On.PropertyChanged(e, () => LastName, () => RaisePropertyChanged(() => FullName));
}
}
Additional action on property’s changes
For example, you want to get from database some additional information after setting ID.public class Author
{
public Author
{
PropertyChanged += Author_PropertyChanges;
}
void Author_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
On.PropertyChanged(e, () => ID, UpdateData);
}
void UpdateData()
{
// Your code here
}
}
Additional actions in parent class
public class AuthorViewModel
{
public Author Author { get; private set; }
public AuthorViewModel
{
Author.PropertyChanged += Author_PropertyChanged;
}
void Author_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
On.PropertyChanged<Author>(e, a => a.ID, UpdateData);
// Or you can use this syntax:
// On.PropertyChanged(e, (Author a) => a.ID, UpdateData);
}
// This method invokes when Author.ID changes
void UpdateData()
{
// Your code here
}
}
No comments:
Post a Comment