When working with WPF and the observable properties a good UI needs, you need to enter class properties as full properties (not automatic properties). You need to do so this so you can call INotifyPropertyChanged
's PropertyChanged event handler in the property's setter.
Full class properties are a pain to enter manually. There is a built-in Visual Studio snippet for full properties (type propfull
and press the tab
key twice), but it adds a plain vanilla setter. However, a custom snippet eliminates needing to manually add the PropertyChanged
handler to the every view model manually, and it provides a SetField
method to call the PropertyChanged
changed handler.
Visual Studio's built-in snippets are located in this folder (this location varies slightly depending on which version of Visual Studio you're using). Looking at some of these snippets and their output helps make a little sense out of the XML needed to create a snippet. The code to create an observable property snippet is below.
1 |
C:\Program Files\Microsoft Visual Studio\2022\Community\VC#\Snippets\1033\Visual C# |
1 2 3 4 5 6 |
private bool? _tester; public bool? tester { get { return _tester; } set { SetField(ref _tester, value); } } |
Snippet as text:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?xml version="1.0" encoding="utf-8" ?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>Observable property</Title> <Shortcut>propobs</Shortcut> <Description>Code snippet for observable property</Description> <Author>Roger Pence</Author> <SnippetTypes> <SnippetType>Expansion</SnippetType> </SnippetTypes> </Header> <Snippet> <Declarations> <Literal> <ID>type</ID> <ToolTip>Data type</ToolTip> <Default>string</Default> </Literal> <Literal> <ID>var</ID> <ToolTip>Variable name</ToolTip> <Default>myVar</Default> </Literal> </Declarations> <Code Language="csharp"><![CDATA[private $type$? _$var$; public $type$? $var$ { get {return _$var$;} set {SetField(ref _$var$, value);} }]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; namespace LibrettoUI_2.Model { public class ObservableObject : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged([CallerMemberName] string? propertyname = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname)); } protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = "") { if (EqualityComparer<T>.Default.Equals(field, value)) { return false; } field = value; OnPropertyChanged(propertyName); return true; } } } |