1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.1 Why Catel? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.2 Support for multiple platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.2.1 Caveats in WPF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.2.2 Caveats in Silverlight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.2.3 Caveats in Windows Phone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.2.4 Caveats in Windows RT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.2.5 Caveats in Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.2.6 Caveats in iOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.3 Introduction to data objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.4 Introduction to MVVM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.4.1 MVVM framework comparison sheet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.4.2 Different interpretations of MVVM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.4.3 Validation in model or view model? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.4.4 Introduction to MVVM and models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.4.5 Creating view models with Catel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.4.6 Introduction to services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.4.7 Introduction to the nested user controls problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.4.8 Introduction to unit testing in MVVM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.5 Introduction to MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.1 General . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.2 MVVM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Setup, deployment and projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.1 Getting prerelease (beta) versions via NuGet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.2 Stepping through the code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.3 Compiling from source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.4 Code snippets & templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.4.1 Using the code snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.4.2 Using the item templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.4.3 Using the project templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.5 Updating to a new version via NuGet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.6 Update guides . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.6.1 Catel 4.0.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.6.2 Catel 4.1.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.1 Quick introduction for developers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2 Getting started with WPF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2.1 Creating the project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2.2 Creating the models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2.3 Serializing data from/to disk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2.4 Creating the view models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2.5 Creating the views (user controls) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2.6 Creating the views (windows) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2.7 Hooking up everything together . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.2.8 Finalizing the application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1 WPF Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1.1 Advanced WPF example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1.2 Authentication example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1.3 Browser application example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1.4 Master/detail example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1.5 Multilingual example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1.6 MVVM communication styles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1.7 Person application WPF example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1.8 Validation example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1.9 Viewmodel lifetime example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.2 Silverlight examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.2.1 Advanced Silverlight example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.2.2 Navigation example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.2.3 Nested user controls example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.2.4 Person application Silverlight example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.3 Windows Phone examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.3.1 Bing maps example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.3.2 Sensors example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.3.3 Shopping list example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.4 Open source using Catel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.6 Problem solving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.7 Performance considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8 Catel.Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.1 ApiCop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
7
8
8
11
11
11
11
11
12
12
20
20
22
23
24
24
31
33
41
42
42
42
43
47
47
49
49
49
50
50
52
53
54
54
58
59
59
61
62
65
70
74
78
80
82
89
95
96
96
98
99
100
100
101
102
103
104
105
105
108
109
110
111
111
113
115
116
116
119
121
121
1.8.1.1 Cops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.1.2 Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.1.3 Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.2 Argument checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.3 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.4 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.5 Data handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.5.1 ObservableObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.5.2 DispatcherObservableObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.5.3 ModelBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.5.4 Using ModelBase as base for entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.5.5 Advanced property change notifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.5.6 WCF services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.6 Exception handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7 IoC (ServiceLocator and TypeFactory) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.1 Dependency injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.2 Introductions to IoC components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.2.1 Introduction to the ServiceLocator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.2.2 Introduction to the TypeFactory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.2.3 Introduction to DependencyResolver and DependencyResolverManager . . . . . . . . . . . . . . . . . . . . . .
1.8.7.2.4 Ensuring integrity of the ServiceLocator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.3 Automatic type registration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.3.1 Automatically registering types using attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.3.2 Automatically registering types using conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.4 Setting up the ServiceLocator using configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.7.5 Replacing the default components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.1 Log listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.1.1 Batch log listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.1.2 ConsoleLogListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.1.3 DebugLogListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.1.4 EventLogListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.1.5 Event Tracing for Windows (ETW) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.1.6 FileLogListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.1.7 RollingInMemoryLogListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.1.8 SeqLogListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.2 Customizing listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.3 Creating log listeners via configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.4 Integration with external loggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.4.1 Log4net . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.4.2 NLog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.8.5 Anotar.Catel.Fody . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.9 Messaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.9.1 MessageBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.9.2 Message mediator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.9.3 Messaging via attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.10 Multilingual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.11 Parallel invocation and tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.12 Preventing memory leaks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.12.1 Change notification wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.12.2 Weak events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.13 Reflection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.14 Scoping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.15 Serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.15.1 Introduction to serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.15.2 Specifying what gets serialized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.15.3 Customizing serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.15.4 Supported serializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.15.4.1 BinarySerializer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.15.4.2 JsonSerializer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.15.4.3 XmlSerializer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.16 Thread safe code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.17 Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.17.1 Validation via validate methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.17.2 Validation via data annotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.17.3 Validation via special model validators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.17.4 Validation via IValidator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.17.5 Using the validation context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.17.6 Getting a summary of validation results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.8.17.7 Deferring validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9 Catel.MVVM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.1 Auditing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
124
124
126
126
127
130
132
133
134
135
138
139
140
142
146
147
152
152
153
155
159
161
161
162
163
166
166
168
168
170
171
171
172
172
173
174
175
175
176
176
177
178
179
180
181
181
183
186
187
187
188
190
191
191
192
194
201
204
204
204
206
206
209
210
210
211
212
215
216
216
217
218
219
220
222
223
223
224
225
226
227
228
228
228
229
229
230
230
231
231
232
233
235
235
236
237
241
243
244
245
247
248
251
252
253
253
255
257
258
260
260
261
264
265
267
268
270
272
273
275
276
277
277
278
281
283
284
284
285
286
289
293
296
298
298
302
303
303
305
305
307
308
310
312
1.9.11.2 iOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.3 XAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.3.1 Window and DataWindow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.3.2 UserControl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.3.3 MVVM behaviors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.3.4 Validation controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.3.5 Using external controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.3.6 Advanced information about views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.3.7 Customizing DataContext subscription behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.4 Tips & tricks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9.11.4.1 Finding the view of a view model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.10 Catel.Mvc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.10.1 Configuring dependency injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.11 Catel.Extensions.Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.11.1 Controls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.11.1.1 StackGrid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.11.1.2 TabControl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.11.1.3 TraceOutputControl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.11.1.4 WatermarkTextBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.11.2 Pixel shaders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.11.3 StyleHelper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.11.4 Themes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.12 Catel.Extensions.CSLA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.13 Catel.Extensions.Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.13.1 Specifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.14 Catel.Extensions.DynamicObjects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.15 Catel.Extensions.EntityFramework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.15.1 Getting started with Entity Framework in Catel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.15.2 Using the DbContextManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.15.3 Using the repositories and unit of work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.15.4 Using the ModelBase as base class for code-first entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.15.5 Using stored procedures and functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.16 Catel.Extensions.FluentValidation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.17 Catel.Extensions.Interception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.17.1 Method interception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.17.2 Property interception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.18 Catel.Extensions.Memento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.18.1 Memento and collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.18.2 Memento and methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.18.3 Memento and properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.19 Catel.Extensions.Prism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.19.1 Using the bootstrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.19.2 Declaring modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.19.3 Module catalogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.19.3.1 CompositeModuleCatalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.19.3.2 DownloadingModuleCatalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.19.3.3 NuGetBasedModuleCatalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.19.3.4 SafeDirectoryModuleCatalog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.19.4 Translating or customizing the initialization task messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.20 Catel.Extensions.Wcf.Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.21 Catel.Fody . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.21.1 Weaving properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.21.2 Weaving argument checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.21.3 Exposing properties on view models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.21.4 XmlSchema generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.22 Catel.ReSharper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.22.1 Checking arguments of a method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.22.2 Converting regular properties into Catel properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.23 Reference documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
314
314
314
317
322
323
325
329
335
336
336
337
337
339
339
339
340
340
341
342
343
344
345
345
345
346
347
347
350
351
353
353
354
355
355
360
361
363
363
365
366
369
371
372
372
373
373
377
377
377
378
379
381
382
384
385
385
386
387
Supported platforms
Looking for documentation of other versions?
Articles or documentation?
Explanation about the documentation
Catel is an application development platform with the focus on MVVM (WPF, Silverlight, Windows Phone and WinRT) and MVC (ASP.NET
MVC). The goal of Catel is to provide a complete set of modular functionality for Line of Business applications written in any .NET technology,
from client to server.
Catel distinguishes itself by unique features to aid in the development of MVVM applications and server-side application development. Since Catel
focuses on Line of Business applications, it provides professional support and excellent documentation which ensures a safe bet by professional
companies and developers.
Besides the core of Catel, lots of extensions are available so developers can pick whatever they need for their applications to be built. Among
these extensions are support for Dynamic Objects, Entity Framework, Fluent Validation, Memento patterns and Prism.
Below is a visual representation of the available blocks in Catel:
Supported platforms
The following platforms are supported by Catel:
For detailed information about platform support, see Platform support explanation
Catel LATEST
Catel 4.1
Catel 4.0
Catel 3.9
Catel 3.8
Catel 3.7
Catel 3.6
Catel 3.5
Articles or documentation?
You have probably also found the articles on The Code Project. These articles are meant to be an introduction to a specific field of techniques that
can be found inside Catel. If you are looking for documentation, this is the place to be.
Are you missing documentation?
If you are missing documentation or feel that documentation is out of date, please let us know and we will will fix it!
Don't forget to take a look at the code snippets and project templates, they will make your life much easier
Introduction
FAQ
Setup, deployment and projects
Getting started
Examples
Problem solving
Performance considerations
Catel.Core
Catel.MVVM
Catel.Mvc
Catel.Extensions.Controls
Catel.Extensions.CSLA
Catel.Extensions.Data
Catel.Extensions.DynamicObjects
Catel.Extensions.EntityFramework
Catel.Extensions.FluentValidation
Catel.Extensions.Interception
Catel.Extensions.Memento
Catel.Extensions.Prism
Catel.Extensions.Wcf.Server
Catel.Fody
Catel.ReSharper
Reference documentation
Introduction
Welcome to the introduction of Catel. Catel is a framework (or enterprise library, just use whatever you like) with data handling, diagnostics,
logging, WPF controls, and an MVVM-framework. So Catel is more than "just" another MVVM-framework or some nice Extension Methods that
can be used. It's more like a library that you want to include in all the XAML applications you are going to develop in the near future.
Note that Catel is primarily meant for Line of Business (LoB) applications
It's important to realize that Catel is not just another Extension Methods library, nor only an MVVM-framework, but it is a combination of basic data
handling, useful controls, and an MVVM-framework.
Catel uses unit tests to make sure that new updates do not break existing functionality.
Catel is very well documented. Every method and property has comments, and the comments are available in a separate reference help
file. There is also a lot of documentation available, and in the future, in-depth articles will be written.
Catel is developed by a group of talented software developers, and is heavily under development. This is a great advantage because the
knowledge is not at just one person, but at a whole group. The developers of Catel all have more than three years of development
experience with WPF, and are using Catel in real life applications for at least 2 years.
Continue reading
Why Catel?
Support for multiple platforms
Introduction to data objects
Introduction to MVVM
Introduction to MVC
Why Catel?
We care a lot about the freedom you need as a software developer. Most frameworks require a developer to learn its conventions, or use the
whole framework or nothing at all. When we, the developers of Catel, use an external framework, we choose that framework for a specific reason,
and dont want to be bothered by all the other superb stuff it has to offer (maybe later, but not now).
During the development of Catel, we tried to maintain this freedom aspect which is very important to us. Therefore, all functionality is loosely
coupled. Sounds great, but everything is called loosely coupled nowadays. Catel contains a lot of different aspects, such as logging, diagnostics,
Reflection, MVVM, user controls, windows, etc. All these aspects are complementary to each other, but the great thing about Catel is that you
decide whether to use just one, some, or maybe all aspects.
As an example: you have a legacy application and want to use the DataWindow to write simple entry windows, but you are not ready for MVVM
yet. No problem, the DataWindow is complementary to MVVM, but does not require it. Therefore, you have all the freedom to use just what you
need, whenever you need it.
Most frameworks require a bootstrapper that completely decides how your application structure should look like. For example, your Views must
have this name, your controls must have that name. Again, in Catel, we wanted to give you the freedom you would expect from a framework.
The great thing about this freedom is that the different aspects of Catel can be used side-by-side with other frameworks, so you as a developer
can use the best framework for every aspect in your application.
Another nice thing is that Catel is WPF-based. Now you might be thinking: but hey, I use Silverlight! However, the upside of this approach is that
all goodies that are well known in WPF, but not in Silverlight are also brought to Silverlight. For example, in Silverlight there is no automatic
command re-evaluation. Catel does this out of the box, even in Silverlight.
Catel offers a solution in the following fields:
Data handling
MVVM
And much more which you will find out during the use of Catel!
A great example of the advantages are the services that are provided by Catel. All the platforms that support some sort of GPS functionality have
the ILocationService (which is an interface that is platform independent). Each platform (Windows Phone, WinRT, Xamarin, etc) have their own
implementation but the developer can use them in exactly the same manner without losing functionality. This means that instead of losing
functionality, Catel adds functionality by not using PCL but platform dependent assemblies instead.
Supported platforms
Xaml
WPF 4.0
WPF 4.5
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
Android 4.0+ (Xamarin)
iOS
iOS 6.0+ (Xamarin)
This page explains in detail what parts of Catel are available on specific platforms.
NET 4.0
Catel.Core
Catel.MVVM
Catel.MVC4
Catel.MVC5
Catel.Extensions.Controls
Catel.Extensions.CSLA
Catel.Extensions.Data
Catel.Extensions.DynamicObjects
Catel.Extensions.EntityFramework5
Catel.Extensions.EntityFramework6
Catel.Extensions.FluentValidation
Catel.Extensions.Interception
Catel.Extensions.Memento
NET 4.5
SL 5
WP 8.0
WP 8.1
WIN 8.0
WIN 8.1
Catel.Extensions.Prism
Fundamental differences
There are some fundamental differences between platforms (such as Xaml and Android). The table below explains the differences so it is easy to
understand for a developer what to use when. Note that all services are defined by interfaces and can be used on all the platforms in the same
manner. The platform-specific implementations are hidden as much as possible to keep the view models very re-usable.
Feature
Xaml
Android
iOS
Phone pages
PhonePage or Page
Activity
TODO
UserControl
Fragment
TODO
Caveats in WPF
Below are all caveats in WPF.
Caveats in Silverlight
Below are all caveats in Silverlight.
Caveats in Windows RT
Below are all caveats in Windows RT.
Caveats in Android
Below are all caveats in Android.
Linker settings
Know caveats? Feel free to add them!
Linker settings
When linking in release mode (or debug if you would like), the linker will remove all non-used items from the final application assembly. Since the
binding system in Catel uses reflection, it might break when the linker is too aggressive when optimizing the app. To prevent optimalization, create
a dummy file that uses the members of each type so the linker will not exclude them. Note that this class will never be instantiated, nor will its
methods be invoked. It is purely to let the static analysis be notified of the usage.
Note that this applies to both properties and bindings
Below is an example class to force the inclusion of members in Android. For each type and its members, a method is added. Then each used
property is accessed and each used event is subscribed to.
};
}
public void IncludeEditText(EditText editText)
{
editText.Text = editText.Text + string.Empty;
editText.TextChanged += (sender, e) => { };
}
public void IncludeCommand(ICatelCommand command)
{
command.CanExecuteChanged += (sender, e) => { };
}
}
Caveats in iOS
Below are all caveats in iOS.
/// <summary>
/// FirstModel class which fully supports serialization, property changed
notifications,
/// backwards compatibility and error checking.
/// </summary>
[Serializable]
public class FirstModel : ModelBase
{
#region Fields
#endregion
#region Constructors
/// <summary>
/// Initializes a new object from scratch.
/// </summary>
public FirstModel()
{ }
/// <summary>
/// Initializes a new object based on <see cref="SerializationInfo"/>.
/// </summary>
/// <param name="info"><see cref="SerializationInfo"/> that contains the
information.
/// </param>
/// <param name="context"><see cref="StreamingContext"/>.</param>
protected FirstModel(SerializationInfo info, StreamingContext context)
: base(info, context) { }
#endregion
#region Properties
// TODO: Define your custom properties here using the propdata code snippet
#endregion
#region Methods
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results
to this list.</param>
protected override void ValidateFields(List<FieldValidationResult>
validationResults)
{
}
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results
to this list.</param>
protected override void ValidateBusinessRules(List<BusinessRuleValidationResult>
validationResults)
{
}
#endregion
}
Declaring properties
The next step to learn on the ModelBase class is how to declare properties. There are several types of properties, and they will all be handled in
this part of the documentation.
The ModelBase class uses a dependency property a-like notation of properties.
Simple properties
This example shows how to declare the simplest property. In this example, a string property with a default value will be declared with the use of a
code snippet.
Code snippets
modelprop - Declares a simple property on a model
Steps
1. Open FirstModel.cs created in the previous step.
2. In the Properties region, use the code snippet modelprop, and use the following values:
Code snippet item
Value
description
type
string
name
SimpleProperty
defaultvalue
"Simple property"
Code
/// <summary>
/// Gets or sets the simple property.
/// </summary>
public string SimpleProperty
{
get { return GetValue<string>(SimplePropertyProperty); }
set { SetValue(SimplePropertyProperty, value); }
}
/// <summary>
/// Register the SimpleProperty property so it is known in the class.
/// </summary>
public static readonly PropertyDataSimplePropertyProperty =
RegisterProperty("SimpleProperty", typeof(string), "Simple property");
Value
description
type
string
name
CallbackProperty
defaultvalue
"Callback property"
Code
/// <summary>
/// Gets or sets the callback property.
/// </summary>
public string CallbackProperty
{
get { return GetValue<string>(CallbackPropertyProperty); }
set { SetValue(CallbackPropertyProperty, value); }
}
/// <summary>
/// Register the CallbackProperty property so it is known in the class.
/// </summary>
public static readonly PropertyDataCallbackPropertyProperty =
RegisterProperty("CallbackProperty", typeof(string), "Callback property",
(sender, e) => ((FirstDataObject)sender).OnCallbackPropertyChanged());
/// <summary>
/// Called when the CallbackProperty property has changed.
/// </summary>
private void OnCallbackPropertyChanged()
{
// TODO: Implement logic
}
Adding validation
It is very easy to add validation to a class (both the ModelBase and ViewModelBase). There are several ways, but this getting started guide will
handle only the most simple one.
To enable validation, you must override at least one of the following methods:
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results to
this list.</param>
protected override void ValidateFields(List<FieldValidationResult> validationResults)
{
if (string.IsNullOrEmpty(FirstName))
{
validationResults.Add(FieldValidationResult.CreateError(FirstNameProperty,
"First name cannot be empty"));
}
}
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results to
this list.</param>
protected override void ValidateBusinessRules(List<BusinessRuleValidationResult>
validationResults)
{
if (SomeBusinessErrorOccurs)
{
validationResults.Add(BusinessRuleValidationResult.CreateError("A business
error occurred"));
}
}
After the validation is implemented into the object, the validation will occur every time a property on the object changes. It is also possible to
manually validate by calling the Validate method.
There are also other ways to add validation to a data object:
Validation via data annotations - attributes such as the RequiredAttribute
Validation via IValidator - custom validation such as FluentValidation
The great thing is that Catel will gather all validation results from all different mappings and combine these into the ValidationContext. This context
can be used to query all sorts of validation info about an object.
Note that this is just an introduction, more information about validation can be found in other parts of the documentation
Saving objects
Saving and loading objects out of the box has never been so easy. SavableModelBase can automatically save/load objects in several ways, such
as memory, file in different modes (binary and XML). This example shows that making your objects savable is very easy and does not take any
time!
Code snippets
model - Declare a model based on the ModelBase class
modelprop - Declare a simple property on a model
Steps
1. Create a new class file called Person.cs.
2. Inside the namespace, use the model codesnippet and fill in the name of the class, in this case Person.
3. Change the base class from ModelBase to SavableModelBase.
4.
4. In the Properties region, use the code snippet modelprop, and use the following values:
Code snippet item
Value
description
type
string
name
Name
defaultvalue
"MyName"
Code
/// <summary>
/// Person class which fully supports serialization, property changed notifications,
/// backwards compatibility and error checking.
/// </summary>
[Serializable]
public class Person : SavableModelBase<Person>
{
#region Fields
#endregion
#region Constructors
/// <summary>
/// Initializes a new object from scratch.
/// </summary>
public Person()
{ }
/// <summary>
/// Initializes a new object based on <see cref="SerializationInfo"/>.
/// </summary>
/// <param name="info"><see
///
cref="SerializationInfo"/> that contains the information.
/// </param>
/// <param name="context"><see
//
cref="StreamingContext"/>.</param>
protected Person(SerializationInfo info, StreamingContext context)
: base(info, context) { }
#endregion
#region Properties
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name
{
get { return GetValue<string>(NameProperty); }
set { SetValue(NameProperty, value); }
}
/// <summary>
/// Register the Name property so it is known in the class.
/// </summary>
public static readonly PropertyData NameProperty = RegisterProperty("Name",
typeof(string), "MyName");
#endregion
#region Methods
#endregion
}
Loading an object
Loading an object is really simple once the class has been created. It is important to use the static method on the class:
Saving an object
To save an object, an instance is required. Then simply call the Save method.
Introduction to MVVM
This part gives an introduction to MVVM in Catel.
MVVM framework comparison sheet
Different interpretations of MVVM
Validation in model or view model?
Introduction to MVVM and models
Creating view models with Catel
Introduction to services
Introduction to the nested user controls problem
Introduction to unit testing in MVVM
Explanation
VM
View-Model
Frameworks reviewed
Framework
Description
Website
Catel
Catel is more than just an MVVM toolkit. It also includes user controls and lots of
enterprise library classes. The MVVM toolkit differs itself from other frameworks of
the simplicity and solves the "nested user control" problem with dynamic viewmodels
for user controls
http://catel.codeplex.com
Caliburn.Micro
A small, yet powerful implementation of Caliburn designed for WPF, Silverlight and
WP7. The framework implements a variety of UI patterns for solving real-world
problems. Patterns that are enabled include MVC, MVP, Presentation Model
(MVVM), and Application Controller.
http://caliburnmicro.codeplex.com
Cinch (V2)
Cinch is a MVVM framework that exposes a number of helper classes to allow the
developer to quickly get to grips with creating scalable testable MVVM frameworks
as quickly as possible.
http://cinch.codeplex.com
MVVM light
The MVVM Light Toolkit is a set of components helping people to get started in the
Model - View - ViewModel pattern in Silverlight and WPF. It is a light and pragmatic
framework that contains only the essential components needed.
http://mvvmlight.codeplex.com
Simple MVVM
toolkit
Simple MVVM Toolkit makes it easier to develop Silverlight, WPF and WP7
applications using the Model-View-ViewModel design pattern. The purpose of the
toolkit is to provide a simple framework and set of tools for getting up to speed
quickly with applications based on the MVVM design pattern. The emphasis is on
simplicity, but it contains everything you need to implement MVVM for real-world line
of business applications.
http://simplemvvmtoolkit.codeplex.com/
WAF
The WPF Application Framework (WAF) is a lightweight Framework that helps you
to create well structured WPF Applications. It supports you in applying a Layered
Architecture and the Model-View-ViewModel (aka MVVM, M-V-VM,
PresentationModel) pattern.
http://waf.codeplex.com/
Platform support
Catel
Caliburn.Micro
Cinch (V2)
MVVM light
Simple MVVM
WAF
WPF
Silverlight 4
Silverlight 5
Windows Phone 7
Windows Phone 8
WinRT
Available via NuGet
UI features
Catel
Caliburn.Micro
Cinch (V2)
MVVM light
Simple MVVM
WAF
ViewModelBase functionality
Catel
INotifyPropertyChanged
IDataErrorInfo
IDataWarningInfo
Support for canceling data
Support for confirming data
Automatic property definitions
Automatic model communications (mappings)
Generate features
Caliburn.Micro
Cinch (V2)
MVVM light
Simple MVVM
WAF
Catel
Caliburn.Micro
Cinch (V2)
MVVM light
Simple MVVM
WAF
Caliburn.Micro
Cinch (V2)
MVVM light
Simple MVVM
WAF
IMessage(Box)Service
INavigationService
IOpenFileService
IPleaseWaitService
IProcessService
ISaveFileService
IUIVisualizerservice
Unit test versions of services
Caliburn.Micro
Cinch (V2)
MVVM light
Simple MVVM
WAF
IAccelerometerService
ICameraService
ICompassService
IGyroscopeService
ILocationService
INavigationService
IVibrateService
Shortcut interpretation
This is what most people do. The view model once implements the model, and then provides the model to the view. The view then
Advantages
Easy to use
Fast since view model hardly contains any properties
Disadvantages
Always need to bind to Model.[PropertyName], but for view model properties it's just [PropertyName], might be confusing
Less control over validation (you cannot insert logic between View <=> Model where MVVM is all about
Purist interpretation
This is what the developers of Catel strongly believe in. It requires a bit more code, but gives great freedom and control and protection of the
model because all bindings go through the view model.
Advantages
Full contol and freedom, you can inject both logic and validation between view and model (what MVVM is actually about)
Everything is available on the view model, no need for "sub-bindings" (such as Model.[PropertyName])
Protection of your model from the view
Disadvantages
Needs a bit more code (but thanks to code snippets and the Expose attribute, this is not a big disadvantage)
model comes in handy. Another reason why you want to implement (a part of) the validation in the view model is the state of the model inside a
workflow. If you have a workflow that updates the model step by step, the model isnt valid after the first step in the workflow. However, you
already want to persist the model because the user might decide to execute the following steps at a later time. You dont want to implement the
state logic of a workflow in your model (and if you did that, get rid of it, as soon as possible). This is another feature where the view model
validation comes in handy.
The good news is that with Catel, it doesnt matter what you want, because its all possible. If you want your model to do all the validation, then
this is possible using the Model and ViewModelToModel attributes which map the values of the properties and the errors directly to the model so
the view model acts as a proxy between the view and the model. If you want to do all of the validation inside the view model, then you can
implement the ValidateFields and ValidateBusinessRules methods in the view model. And, if you want the best of both worlds, such as me, than
you can use a combination of the techniques described above.
Models
The Models part is one of the three major parts of MVVM. Therefore, I want to tell you a bit about what kind of Models we use to store our data.
Basically, you can use all types of objects as Models, as long as the Models implement the most commonly used interfaces required by WPF or
Silverlight.
For MVVM, it is very important that the following interfaces are implemented:
INotifyPropertyChanged
If this interface is not implemented, changes will not be reflected to the UI via bindings. In other words, your Model and View Model will
be useless in an MVVM setting.
Finally, it is strongly recommended to have your Models implement the following interfaces as well:
IDataErrorInfo
If this interface is not implemented, errors cannot be shown to the user.
IEditableObject
If this interface is not implemented, a Model cannot work with states. This means that a user cannot start editing an object and finally
cancel it (because there is no stored state that can be used to restore the values).
View Models
View models are the classes that contain the view logic. They can be seen as a container for all models in a specific view, including the behavior
to interact with these models (for example adding or removing them from a collection). It is very important that a view model also implements the I
NotifyPropertyChanged interfaces just like the models do.
The class diagram above shows how many default interfaces of the .NET Framework are supported in the ModelBase class. Since most of these
interfaces are used by WPF as well, the ViewModelBase class itself can take huge advantage of the implementation of ModelBase.
Because ViewModelBase derives from ModelBase, you can declare properties exactly the same way. Even better, you can simply use ModelBas
e (or the extended SavableModelBase) to create (and save) your Models, and use ViewModelBase as the base for all the View Models.
/// <summary>
/// $name$ view model.
/// </summary>
public class $name$ViewModel : ViewModelBase
{
#region Fields
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="$name$ViewModel"/> class.
/// </summary>
public $name$ViewModel ()
{
}
#endregion
#region Properties
/// <summary>
/// Gets the title of the view model.
/// </summary>
/// <value>The title.</value>
public override string Title { get { return "View model title"; } }
// TODO: Register models with the vmpropmodel codesnippet
// TODO: Register view model properties with the vmprop or vmpropviewmodeltomodel
codesnippets
#endregion
#region Commands
// TODO: Register commands with the vmcommand or vmcommandwithcanexecute
codesnippets
#endregion
#region Methods
#endregion
}
Declaring properties
Note that declaring properties works exactly the same as declaring properties for the ModelBase
There are several code snippets available to create View Model properties:
vmprop - Defines a simple View Model property.
vmpropmodel - Defines a View Model property with ModelAttribute. The property is also made private by default.
vmpropviewmodeltomodel - Defines a View Model property with ViewModelToModelAttribute.
When using the vmprop code snippet, this is the result:
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name
{
get { return GetValue<string>(NameProperty); }
set { SetValue(NameProperty, value); }
}
/// <summary>
/// Register the Name property so it is known in the class.
/// </summary>
public static readonly PropertyData NameProperty = RegisterProperty("Name",
typeof(string));
In the View, it is now possible to bind to the Name property of the View Model, as long as DataContext is set to an instance of the View Model.
Declaring commands
There are several code snippets available to create View Model commands:
vmcommand - Defines a command that is always executable.
vmcommandwithcanexecute - Defines a command that implements a CanExecute method to determine whether the command can be
invoked on the View Model in its current state.
When using the vmcommandwithcanexecute code snippet, this is the result:
/// <summary>
/// Gets the Add command.
/// </summary>
public Command<object, object> Add { get; private set; }
// TODO: Move code below to constructor
Add = new Command<object, object>(OnAddExecute, OnAddCanExecute);
// TODO: Move code above to constructor
/// <summary>
/// Method to check whether the Add command can be executed.
/// </summary>
/// <param name="parameter">The parameter of the command.</param>
private bool OnAddCanExecute(object parameter)
{
return true;
}
/// <summary>
/// Method to invoke when the Add command is executed.
/// </summary>
/// <param name="parameter">The parameter of the command.</param>
private void OnAddExecute(object parameter)
{
// TODO: Handle command logic here
}
The only thing left to do now is to move the creation of the command to the constructor (as the comments already instructs you to).
In the View, it is now possible to bind any Command property (such as the Command property of a Button) to the Add property of the View Model,
as long as DataContext is set to an instance of the View Model.
Adding validation
Because the ViewModelBase class derives from ModelBase, it provides the same power of validation that the ModelBase class has to offer. Mode
lBase (and thus ViewModelBase) offers the following types of validation:
Field warnings
Business warnings
Field errors
Business errors
ViewModelBase uses smart validation. This means that if the object is already validated, the object is not validated again to make sure that the
View Models don't hit too much on the performance. Only when a property on the View Model changes, validation will be invoked. Of course, if
required, it is still possible to force validation when the View Model must be validated, even when no properties have changed.
To implement field or business rule validation, you only have to override ValidateFields and/or the ValidateBusinessRules method:
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results to
this list.</param>
protected override void ValidateFields(List<FieldValidationResult> validationResults)
{
if (!string.IsNullOrEmpty(FirstName))
{
validationResults.Add(FieldValidationResult.CreateError(FirstNameProperty,
"First name cannot be empty"));
}
}
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results to
this list.</param>
protected override void ValidateBusinessRules(List<BusinessRuleValidationResult>
validationResults)
{
if (SomeBusinessErrorOccurs)
{
validationResults.Add(BusinessRuleValidationResult.CreateError("A business
error occurred"));
}
}
Note that it is also possible to re-use validation in a model using ModelToViewModel mappings or even external validation such as Flue
ntValidation
There are also other ways to add validation to a data object:
Validation via data annotations - attributes such as the RequiredAttribute
Validation via IValidator - custom validation such as FluentValidation
The great thing is that Catel will gather all validation results from all different mappings and combine these into the ValidationContext. This context
can be used to query all sorts of validation info about an object.
ModelAttribute
To be able to map values from/to a Model, it is important to know the actual Model. So, to let the View Model know what property represents the
Model, ModelAttribute can be used like shown below:
/// <summary>
/// Gets or sets the person.
/// </summary>
[Model]
public Person Person
{
get { return GetValue<Person>(PersonProperty); }
private set { SetValue(PersonProperty, value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(Person));
A Model setter is normally written as private (you normally don't want a UI to be able to change a Model), but the getter is public because you
might want to read info from it.
Note that you should use the vmpropmodel code snippet to create Model properties
Models in Catel are handled as very, very special objects. This means that as soon as a Model is set, Catel tries to call the IEditableObject.Begin
Edit method. Then, as soon as the Model is changed without being saved, or if the View Model is canceled, the Model is correctly canceled via IE
ditableObject.CancelEdit. If the Model is saved, the active Models will be committed via IEditableObject.EndEdit. I will leave the rest of the magic
out of this article, but if you have any questions about it, don't hesitate to contact us!
ViewModelToModelAttribute
Now that we know how to declare a property has a Model, it is time to learn how we can communicate with it. Normally, you would have to watch
the Model to make sure it is synchronized correctly when the Model is updated. With Catel, this is not necessary any longer. Simply use ViewMod
elToModelAttribute, and you will get the following advantages:
Models are automatically being watched for changes, thus if a mapped property changes, the View Model is updated accordingly;
When a View Model is changed, this property is automatically mapped to the Model;
When the Model changes, the View Model is initialized automatically with the values of the new Model;
When a Model has an error or warning (business or field), the warnings are mapped to the View Model so you can re-use the validation
of the Model inside your View Model.
So, you get all of this for free? No, you will have to decorate your property with ViewModelToModelAttribute, like shown below:
/// <summary>
/// Gets or sets the first name.
/// </summary>
[ViewModelToModel("Person")]
public string FirstName
{
get { return GetValue<string>(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
/// <summary>
/// Register the FirstName property so it is known in the class.
/// </summary>
public static readonly PropertyData FirstNameProperty = RegisterProperty("FirstName",
typeof(string));
The code example is the easiest usage of the attribute that is available. It only provides the name of the Model property. This is required because
it is possible (but not likely) to have multiple Models. But what if the property on your Model has a different name than your View Model? No
problem, use the overload of the attribute as shown below:
[ViewModelToModel("Person", "RealFirstName")]
public string FirstName
///... (remaining code left out for the sake of simplicity)
The code above will map the FirstName property of the View Model to the RealFirstName property of the Person model.
ExposeAttribute
The ViewModelToModelAttribute is a great way to map properties between the model and the view model. However, sometimes the mappings are
not required for manual coding and should only be exposed from inside the view model to the view. The ExposeAttribute is great way to simplify
this process. The code below is the same as declaring a model property named Person and 3 additional properties using the ViewModelToModel
Attribute:
/// <summary>
/// Gets or sets the person.
/// </summary>
[Model]
[Expose("FirstName")]
[Expose("MiddleName")]
[Expose("LastName")]
private Person Person
{
get { return GetValue<Person>(PersonProperty); }
set { SetValue(PersonProperty, value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(Person));
This feature is only available in WPF because Silverlight does not provide the ICustomTypeDescriptor or an equivalent feature
ViewModelManager
Let's start with the basics. As we have learned earlier in this article, all View Models created with the help of Catel derive from the ViewModelBase
class. One of the things that this class does is that it registers itself with the ViewModelManager class when it is being created, and it unregisters
itself again when it is closed. So, simply said, ViewModelManager is a class that holds a reference to all existing View Models at the moment.
InterestedInAttribute
Now that we know about the ViewModelManager class, and know that there is a repository that holds all of the live instances of all View Model
classes, it should be fairly easy to communicate with other View Models. It actually is; you just have to decorate a View Model with InterestedInAtt
ribute, as shown below:
[InterestedIn(typeof(FamilyViewModel))]
public class PersonViewModel : ViewModelBase
A View Model can have multiple InterestedInAttribute instances, so it is possible to subscribe to multiple View Model types at the same time. Once
a View Model is decorated with InterestedInAttribute, the View Model will receive all changes (and of course, the View Model that caused the
change) via the OnViewModelPropertyChanged method, as shown below:
/// <summary>
/// Called when a property has changed for a view model type that the current view
model is interested in. This can
/// be accomplished by decorating the view model with the <see
cref="InterestedInAttribute"/>.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="propertyName">Name of the property.</param>
protected override void OnViewModelPropertyChanged(IViewModel viewModel, string
propertyName)
{
// You can now do something with the changed property
}
Note that it is also possible to get notified of commands that are being executed on other view models
MessageMediator
Catel also offers a solution to the message mediator pattern in the form of the MessageMediator class. This is all described in the next section
"Message mediator".
Introduction to services
Services are very important in MVVM. They define a way to interact with the user without using fixed controls such as MessageBox or SaveFileDi
alog. The interfaces defined in Catel only define generic functionality of what to expect from a specific service. Using services is a great way to
abstract away all specific functionality from a view model into a service that can be mocked during unit testing and can be used by other view
models as well.
ServiceLocator
The key behind services is the ServiceLocator. The ServiceLocator is the IoC (Inversion of Control) container that Catel provides. This is a
container that contains all registrations and service instances available throughout the application. Retrieving services from the default ServiceLoc
ator in Catel is very simple:
It is also possible to get services injected into the constructor, which is the recommended approach
Dependency injection
A slightly better way to manage dependencies is to use dependency injection. The reason is that to instantiate a class, you always have to
provide all the dependencies. This way, all dependencies are always known to the caller which gives is a bit easier to control than having to know
what services are being used by a component (such as a view model).
Catel fully supports dependency on view models. This means that a view model can have a constructor with several services. Catel will
automatically inject the services via the constructor. An example is below:
Overview of services
The services below are available in Catel:
Name
Description
IAccelerometerService
ICameraService
ICompassService
IGyroscopeService
ILocationService
IMessageService
INavigationService
Allows a developer to navigate to other pages inside an application using view models only.
IOpenFileService
Allows a developer to let the user choose a file from inside a view model.
IPleaseWaitService
Allows a developer to show a please wait message (a.k.a. busy indicator) from a view model.
IProcessService
ISaveFileService
Allows a developer to let the user choose a file from inside a view model.
IUIVisualizerService
Allows a developer to show (modal) windows or dialogs without actually referencing a specific view.
IVibrateService
Allows a developer to start and stop vibration of the device via a service.
Note that this section is not always fully up-to-date, Catel might provide more services than listed here
As the images above show, the method that Catel uses to solve the problem is much more professional. Below are a few reasons:
Separation of concerns (each control has a view model only containing the information for itself, not for children);
User controls are built so they can be re-used. Without the user controls to be able to have their own view models, how should one
actually use user controls with MVVM?
The idea behind the user control is pretty complex, especially because WPF (and thus Silverlight and WP7) isnt very good at runtime data
context type changing. However, with a few workarounds (very well described in the source code of UserControl), it is possible to dynamically
construct view models. The user control constructs the view model with or without a constructor as described earlier in this article. When the view
model is constructed, the user control tries to find a (logical or visual) parent that implements the IViewModelContainer interface. Thanks to this
interface, a view model can subscribe itself to a parent view model and the validation chain is created as shown below:
As the image above shows, all children in the chain are validated, and when the last child is validated, the view model reports the result of its
children and itself back to its parent. This way, it is still possible to disable a command when one of the nested user control view models has an
error.
Saving a chain of nested view models works exactly the same as the validation. First, the view model saves all children, then itself and finally
reports back its result to the parent.
Now, lets go to some real-life example. I dont want to make it too complex, but not too easy as well, but dont want to put the focus on the
content of the data, but on the user control and view model creation. Therefore, I have chosen for the data model below:
The image shows that we have a house. In that house, we have multiple rooms. In each room, there can be several tables with chairs and beds.
This shows a complex UI tree with lots of different user controls (each object has its own representation and thus user control). Now our goal is
to create user controls that can be used in the window that shows the full house, but also in sub-parts and we want to be fully independent of the
HouseWindowViewModel (which is the only view model that would be created in a regular MVVM Framework).
The example below shows only the Room control and the corresponding view model. The full source code of this article is provided in the source
code repository of Catel, so the whole example is available if you are interested or need a more complete example.
First, we start with a simple model. For the model, we use the ModelBase class. By using the provided code snippets, this model is setup within a
minute:
/// <summary>
/// Bed class which fully supports serialization, property changed notifications,
/// backwards compatibility and error checking.
/// </summary>
[Serializable]
public class Room : ModelBase<Room>
{
#region Constructor & destructor
/// <summary>
/// Initializes a new object from scratch.
/// </summary>
public Room()
: this(NameProperty.GetDefaultValue<string>()) { }
/// <summary>
/// Initializes a new instance of the <see cref="Room"/> class.
/// </summary>
/// <param name="name">The name.</param>
public Room(string name)
{
// Create collections
Tables = new ObservableCollection<Table>();
Beds = new ObservableCollection<Bed>();
// Store values
Name = name;
}
/// <summary>
/// Initializes a new object based on <see cref="SerializationInfo"/>.
/// </summary>
/// <param name="info"><see cref="SerializationInfo"/> that contains the
information.</param>
/// <param name="context"><see cref="StreamingContext"/>.</param>
protected Room(SerializationInfo info, StreamingContext context)
: base(info, context) { }
#endregion
#region Properties
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name
{
get { return GetValue<string>(NameProperty); }
set { SetValue(NameProperty, value); }
}
/// <summary>
/// Register the Name property so it is known in the class.
/// </summary>
public static readonly PropertyData NameProperty = RegisterProperty("Name",
typeof(string), "Room");
/// <summary>
/// Gets or sets the table collection.
/// </summary>
public ObservableCollection<Table> Tables
{
get { return GetValue<ObservableCollection<Table>>(TablesProperty); }
set { SetValue(TablesProperty, value); }
}
/// <summary>
/// Register the Tables property so it is known in the class.
/// </summary>
public static readonly PropertyData TablesProperty = RegisterProperty("Tables",
typeof(ObservableCollection<Table>));
/// <summary>
/// Gets or sets the bed collection.
/// </summary>
public ObservableCollection<Bed> Beds
{
get { return GetValue<ObservableCollection<Bed>>(BedsProperty); }
set { SetValue(BedsProperty, value); }
}
/// <summary>
/// Register the Beds property so it is known in the class.
/// </summary>
public static readonly PropertyData BedsProperty = RegisterProperty("Beds",
typeof(ObservableCollection<Bed>));
#endregion
}
Next, we are going to create the view model. Again, by the use of code snippets explained earlier in this article, the view model is set up within a
few minutes:
/// <summary>
/// Room view model.
/// </summary>
public class RoomViewModel : ViewModelBase
{
#region Variables
private int _bedIndex = 1;
private int _tableIndex = 1;
#endregion
#region Constructor & destructor
/// <summary>
/// Initializes a new instance of the <see cref="RoomViewModel"/> class.
/// </summary>
public RoomViewModel(Models.Room room)
{
// Store values
Room = room;
// Create commands
AddTable = new Command(OnAddTableExecuted);
AddBed = new Command(OnAddBedExecuted);
}
#endregion
#region Properties
/// <summary>
/// Gets the title of the view model.
/// </summary>
/// <value>The title.</value>
public override string Title { get { return "Room"; } }
#region Models
/// <summary>
/// Gets or sets the room.
/// </summary>
[Model]
public Models.Room Room
{
get { return GetValue<Models.Room>(RoomProperty); }
private set { SetValue(RoomProperty, value); }
}
/// <summary>
/// Register the Room property so it is known in the class.
/// </summary>
public static readonly PropertyData RoomProperty = RegisterProperty("Room",
typeof(Models.Room));
#endregion
#region View model
/// <summary>
/// Gets or sets the name.
/// </summary>
[ViewModelToModel("Room")]
public string Name
{
get { return GetValue<string>(NameProperty); }
set { SetValue(NameProperty, value); }
}
/// <summary>
/// Register the Name property so it is known in the class.
/// </summary>
public static readonly PropertyData NameProperty = RegisterProperty("Name",
typeof(string));
/// <summary>
/// Gets or sets the table collection.
/// </summary>
[ViewModelToModel("Room")]
public ObservableCollection<Models.Table> Tables
{
get { return GetValue<ObservableCollection<Models.Table>>(TablesProperty); }
set { SetValue(TablesProperty, value); }
}
/// <summary>
/// Register the Tables property so it is known in the class.
/// </summary>
public static readonly PropertyData TablesProperty = RegisterProperty("Tables",
typeof(ObservableCollection<Models.Table>));
/// <summary>
/// Gets or sets the bed collection.
/// </summary>
[ViewModelToModel("Room")]
public ObservableCollection<Models.Bed> Beds
{
get { return GetValue<ObservableCollection<Models.Bed>>(BedsProperty); }
set { SetValue(BedsProperty, value); }
}
/// <summary>
/// Register the Beds property so it is known in the class.
/// </summary>
public static readonly PropertyData BedsProperty = RegisterProperty("Beds",
typeof(ObservableCollection<Models.Bed>));
#endregion
#endregion
#region Commands
/// <summary>
/// Gets the AddTable command.
/// </summary>
public Command AddTable { get; private set; }
/// <summary>
/// Method to invoke when the AddTable command is executed.
/// </summary>
}
#endregion
}
As you can see, the view model can only be constructed by passing a Room model object. It is very important to be aware of this construction.
The reason that there is no empty constructor is because there is no support for views that do not represent a Room model.
In the view model, the properties of the Room model are mapped by the use of the Model attribute and the ViewModelToModel attribute. Last but
not least, commands are defined to be able to add new tables and beds to the Room model.
Another way to add a new user control is to use the item templates
Now the model and the view model are fully set up, the last thing to do is to create the actual view. To accomplish this, add a new WPF user
control to the project. First thing to do is to implement the code-behind, since that is the easiest to do:
<summary>
/// Interaction logic for Room.xaml
/// </summary>
public partial class Room : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="Room"/> class.
/// </summary>
public Room()
{
// Initialize component
InitializeComponent();
}
}
The only thing we changed from the default user control template is that the user control now derives from Catel.Windows.Controls.UserControl c
ontrol instead of the default System.Windows.Controls.UserControl control. This is it for the code-behind, lets move up to the view.
The last thing to do now is the actual xaml view. For the sake of simplicity, the actual content is left out (its just a grid with a textbox and
itemscontrols for the children):
<catel:UserControl
x:Class="Catel.Articles._03___MVVM.Examples.NestedUserControls.Room"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com"
xmlns:NestedUserControls="clr-namespace:Catel.Articles._03___MVVM.Examples.NestedUserC
ontrols">
<!-- For the sake of simplicity, the content is left out -->
</catel:UserControl>
A few things are very important to notice in the xaml code shown above. The first thing to notice is that (like the code-behind), the base class is
now catel:UserControl instead of UserControl.
Thats all that can be learned about solving the nested user control problem. We have set up the model, view model and finally the view. Now,
lets take a look at how it looks in a screenshot (and notice the construction time of the view model, they are really constructed on-demand):
The red border is the control that we just created. It shows the name of the room, the view model construction time and the child objects (inside
expanders).
Testing commands
Thanks to commands (which implement the ICommand interface), testing view models and UI logic has never been so easy. Now commands can
be invoked programmatically without having to automate the UI; it is very simple to reproduce a button click during a unit test.
When testing commands, it is very important to test the state as well. The command implementation of Catel has a CanExecute and an Execute
method which can be invoked manually. Therefore, it is very easy to test a command. The code below shows a test that checks whether the Rem
ove command can be executed. At first, the command cannot be executed because there is no selected person. After the selected person is set,
the command should be able to execute:
Assert.IsFalse(mainWindowViewModel.Remove.CanExecute(null));
mainWindowViewModel.SelectedPerson = mainWindowViewModel.PersonCollection[0];
Assert.IsTrue(mainWindowViewModel.Remove.CanExecute(null));
mainWindowViewModel.Remove.Execute(null);
Testing services
For more information about unit testing services, see the unit testing services documentation.
Introduction to MVC
Note that this documentation has to be written
FAQ
Welcome to the FAQ. Please pick a section:
General
MVVM
General
I want to change the bitmap effects, what should I do?
StyleHelper.CreateStyleForwardersForDefaultStyles does not work in Silverlight
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp">
<Application.Resources>
<ResourceDictionary Source="/CWP.Shared;component/themes/generic.xaml" />
</Application.Resources>
</Application>
However, Silverlight does not allow to add resources to a ResourceDictionary where the source is set. To be able to use default styles, use this
code:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyApp.Shared;component/themes/generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
MVVM
How to support example data with view models?
How to use events with MVVM?
Silverlight throws error about type that cannot be created?
How can I add the MVVM behaviors via code (programmatically)?
How can I inject or manipulate the view model of a UserControl?
How can I prevent validation of required fields?
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Inter
activity"
xmlns:catel="http://catel.codeplex.com"
4.
<i:Interaction.Triggers>
<i:EventTrigger EventName="[YourEvent]">
<catel:EventToCommand Command="{Binding [YourCommand]}"
DisableAssociatedObjectOnCannotExecute="False" />
</i:EventTrigger>
</i:Interaction.Triggers>
<i:Interaction.Behaviors>
<catel:WindowBehavior ViewModelType="viewmodels:DemoWindowViewModel"
Save="okButton.Click" Cancel="cancelButton.Click" />
</i:Interaction.Behaviors>
This happens in Silverlight 4 when the Silverlight 5 (beta) tools are not installed. To resolve this issue, install the Silverlight 5 (beta) tools.
Silverlight has a known issue, and sometimes installing the Silverlight 5 (beta) toolkit isn't the right option to solve the issue. Luckily, it is also
possible to add one of the MVVM behaviors via code to any control or window.
Below is the code-behind of a view that adds the UserControlBehavior via code:
cref="M:System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPro
pertyChangedEventArgs)"/>.
/// </summary>
/// <param name="e">The event data that describes the property that changed, as
well as old and new values.</param>
protected override void
OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
PropertyChanged.SafeInvoke(this, new PropertyChangedEventArgs(e.Property.Name));
}
#endif
}
Using this technique, it is even possible to determine the view model of any view dynamically at runtime.
Validate(true, false);
If the validation is implemented in the models and not in the view model, set the ValidateModelsOnInitialization to false.
Update-Package Catel.Extensions.Controls
Note that Visual Studio 2012 needs to run elevated in order to download the source server files due to a bug in Visual Studio 2012
Extract the zip file to the following directory: %MyDocuments%\Visual Studio 2xxx\Code Snippets
VB.NET
Extract the zip\VB.NET\ItemTemplates to the following directory: %MyDocuments%\Visual Studio 2xxx\Templates\ItemTemplates\Visual Basic
Extract the zip\VB.NET\ProjectTemplates to the following directory: %MyDocuments%\Visual Studio 2xxx\Templates\ProjectTemplates\Visual
Basic
It's possible that you need to restart Visual Studio for it to recognize the templates
Explanation
modelobject
modelprop
modelpropchanged
vm
vmcommand
vmcommandwithcanexecute
Defines a new view model command with an executed action, but also a canexecute function.
vmprop
vmpropmodel
Defines a new property on a view model and decorates it with the ModelAttribute.
vmpropviewmodeltomodel
Defines a new property on a view model and decorates it with the ViewModelToModelAttribute.
This means that the logic for MVVM is located in the view base (in this example, the UserControl).
An item template where the logic is located in a behavior uses regular controls and uses one of the available MVVM behaviors to implement the
logic. The behaviors are extremely powerful, but we still recommend the use of logic in the view base since that takes more work out of the hands
from the developer.
View model
The view model is the easiest and smallest item template available. The question is even whether a new view model should be created via an
item template or by using the vm code snippet.
User control
The user control templates create a user control deriving from UserControl. First it is very important to make a decision whether the logic should
be implemented in the view base or a behavior. Then, select the appropriate template:
The item template will assume that a view model with the same name is already created. For example, if a view with the name PersonView is
created, the template assumes that the view model name is PersonViewModel. If you prefer a different naming convention, simply change the
view model type name after the item has been created.
Unfortunately, there is a bug in the item templates system of Visual Studio so it is not possible to also set the namespace of the view
models to [ProjectRootNamespace].ViewModels, so this has to be done manually
Window
The window templates create a window deriving from DataWindow. First it is very important to make a decision whether the logic should be
implemented in the view base or a behavior. Then, select the appropriate template:
The item template will assume that a view model with the same name is already created. For example, if a view with the name PersonView is
created, the template assumes that the view model name is PersonViewModel. If you prefer a different naming convention, simply change the
view model type name after the item has been created.
Unfortunately, there is a bug in the item templates system of Visual Studio so it is not possible to also set the namespace of the view
models to [ProjectRootNamespace].ViewModels, so this has to be done manually
As soon as the project is created, the only thing left to do is add references to the Catel libraries. You can either do this manually or use NuGet.
The pitfall
Catel ships with many different extensions. This means that each extensions ships in its own NuGet package. This might cause NuGet packages
to be installed with different versions throughout the solution. Assume the solution below:
MyProject.DAL => contains Catel.Core (3.5)
MyProject => contains Catel.Core (3.5), Catel.MVVM (3.5)
When you go to the NuGet package manager, you will notice that updates are available. When you just click update on Catel.MVVM, you will end
up with a version mismatch, which is shown below:
MyProject.DAL => contains Catel.Core (3.5)
MyProject => contains Catel.Core (3.6), Catel.MVVM (3.6)
This is because NuGet will install Catel.MVVM (3.6) which has a dependency on Catel.Core (3.6). Because it notices this dependency, it will
automatically update Catel.Core, but only for the MyProject project, not for all projects. Now there is a mismatch and you won't be able to update
the MyProject.DAL anymore via the NuGet package manager because it already notices that you already installed Catel.Core 3.6 somewhere
else.
Update guides
This section of the documentation describes how to update to a new version of Catel. This guide does only cover direct version transitions (thus
3.9 => 4.0, not 3.6 => 4.0).
Catel 4.0.0
Catel 4.1.0
Catel 4.0.0
This guide describes how to update your code to be fully compatible with Catel 4.0.0.
Renaming classes
Renaming namespaces
Simplied ModelBase external interfacing
Renamed LoadTabItemsBehavior
Add additional members to custom IView implementations
Using FastViewPropertySelector by default for major performance improvement
Manually add interesting properties (recommended)
Register the ViewPropertySelector
Full support for asynchronous (async/await)
IViewModel
Updating Initialize method
Updating Save method
Updating Close method
IMessageService
IUIVisualizerService
Optimizing views (especially 3rd party)
Removed IServiceLocator.RemoveInstance methods
Changed CompositeCommand
Added time to all log calls
Behavior changes
Renaming classes
Some classes in Catel have been renamed.
Catel.Environment => CatelEnvironment
IDependencyPropertySelector => IViewPropertySelector
Renaming namespaces
Some namespaces in Catel have been changed to match the functionality. For example, all services are now in Catel.Service instead of Catel.MV
VM.Services because they can be used without MVVM.
Below is a list of changed namespaces:
Catel.MVVM.Services => Catel.Services
Catel.Windows.Data.Converters => Catel.MVVM.Converters
Catel.Windows.Controls.MVVMProviders.Logic => Catel.MVVM.Providers
Some interfaces were moved (but not all classes in the namespace):
Catel.Windows => Catel.MVVM.Views
Member type
BusinessRuleErrorCount
IModelValidation.BusinessRuleErrorCount
property
BusinessRuleWarningCount
IModelValidation.BusinessRuleWarningCount
property
Deserialized
IModelSerialization.Deserialized
event
ErrorsChanged
INotifyDataErrorInfo.ErrorsChanged
event
FieldErrorCount
IModelValidation.FieldErrorCount
property
FieldWarningCount
IModelValidation.FieldWarningCount
property
HasErrors
INotifyDataErrorInfo.HasErrors
property
HasWarnings
INotifyDataWarningInfo.HasWarnings
property
ValidationContext
IModelValidation.ValidationContext
property
Validator
IModelValidation.Validator
property
WarningsChanged
INotifyDataWarningInfo.WarningsChanged
event
Renamed LoadTabItemsBehavior
The LoadTabItemsBehavior has been refactored with new names. The old names will be removed in v5, but will error in v4. Below are the
renames:
Single => LazyLoading
SingleUnloadOthers => LazyLoadingUnloadOthers
AllOnStartup => EagerLoading
AllOnFirstUse => EagerLoadingOnFirstUse
viewPropertySelector.AddPropertyToSubscribe("MyProperty", typeof(MyView));
In most cases, the only reason to subscribe to property changes is because of the ViewToViewModel attribute. If that is the case, it is best to use
the extension method AutoDetectViewPropertiesToSubscribe in the static constructor of the view:
static MyView()
{
typeof(MyView).AutoDetectViewPropertiesToSubscribe();
}
IMessageService
The use of await or Task.ContinueWith to await the result is now necessary or use the code below:
IUIVisualizerService
The use of await or Task.ContinueWith to await the result is now necessary or use the code below:
await uiVisualizerService.ShowDialog<MyViewModel>();
// Window is closed here thanks to the await keyword
Changed CompositeCommand
The composite command will always allow execution, even when commands don't allow it. Therefore the AllowPartialExecution is now set to false
by default.
If there is a requirement to allow partial invocation, set this property to true.
Behavior changes
To improve multiple platforms support, all parameters of the following methods on BehaviorBase have been removed:
Catel 4.1.0
This guide describes how to update your code to be fully compatible with Catel 4.1.0.
This guide assumes that you are coming from Catel 4.0.0. If not, please read that guide first.
IUIVisualizerService
IUIVisualizerService
We have reverted the change to force you to use async code on the IUIVisualizerService. There are now 2 options:
1. Synchronous:
2. Asynchronous:
Getting started
The getting started series focuses on the main features of Catel.
Quick introduction for developers
Getting started with WPF
Core
This pare contains the core functionality of Catel and what you should know when using Catel.
Logging / debugging
If you ever think Catel is behaving strange or does not work as expected, make sure to enable the logging. Below is an example on how to enable
the logging:
#if DEBUG
LogManager.RegisterDebugListener();
#endif
Catel will then log everything to the output window and provide all the information about its internals.
For more information, read about logging.
Catel properties
All properties in classes deriving from ModelBase (thus also ViewModelBase) require a special property definition.
MVVM
This part is especially meant for the MVVM part.
Handling of viewmodels
In other MVVM frameworks, you are obliged to set the data context of a view manually. It will look something like this:
Catel automatically resolves the right view model based on the view. If a view is created, Catel automatically creates the view model:
It goes even further. Catel can create view models based on the data context. For more information, read nested user controls.
for this yourself. For more information, read nested user controls.
Communication between view models
There are several methods available to communicate between view models. Just make sure that you never directly reference other view model
and keep everything loosely coupled.
Note that this guide will recommend code snippets that can be found here. They are not required, just recommended to speed up
creating Catel classes and properties.
Creating the project
Creating the models
Serializing data from/to disk
Creating the view models
Creating the views (user controls)
Creating the views (windows)
Hooking up everything together
Finalizing the application
Download the final result: WPF.GettingStarted.zip
Pick a good name, in our case WPF.GettingStarted and click OK. The template will now be downloaded and the project will be created.
The ViewModels folder contains the MainWindowViewModel, which contains the logic for the interaction with the MainWindow view.
The Views folder contains the MainWindow, which represents the actual view.
Up next
Creating the models
In this step we will create models. Since this application is about families and persons inside those families, we need to create the following
models: Settings, Family and Person.
Creating the model classes
Settings class
Family class
Person class
Adding properties to the models
Settings class
Family class
Person class
Up next
Settings class
The settings class is the top container that will store all families and other settings (which might be added in the future).
namespace WPF.GettingStarted.Models
{
using Catel.Data;
public class Settings : SavableModelBase<Settings>
{
}
}
Family class
namespace WPF.GettingStarted.Models
{
using Catel.Data;
public class Family : ModelBase
{
}
}
Person class
namespace WPF.GettingStarted.Models
{
using Catel.Data;
public class Person : ModelBase
{
}
}
Settings class
Family class
Person class
Up next
Serializing data from/to disk
In this step we will create services that will serialize the models from/to disk. Services are a great way to abstract functionality that can be used in
every part of the application. This guide will also register the service in the ServiceLocator so it can be injected in view models.
Creating the service definition
Creating the service implementation
Registering the service in the ServiceLocator
Adding the service usage to the MainWindowViewModel
Injecting the service via dependency injection
Creating the Families property on the MainWindowViewModel
Loading the families at startup
Saving the families at shutdown
Up next
Then add a new interface to the Interfaces folder named IFamilyService. This will manage the families that are avaiable. Below is the interface
defined:
namespace WPF.GettingStarted.Services
{
using WPF.GettingStarted.Models;
public interface IFamilyService
{
IEnumerable<Family> LoadFamilies();
void SaveFamilies(IEnumerable<Family> families);
}
}
namespace
{
using
using
using
using
using
WPF.GettingStarted.Services
System.Collections.Generic;
System.IO;
Catel.Collections;
Catel.Data;
WPF.GettingStarted.Models;
odel which contains all the logic of the main application window.
As you can see in the code above, a new field is created to store the dependency IFamilyService. Then the constructor ensures that the argument
is not null and stores it in the field.
/// <summary>
/// Gets the families.
/// </summary>
public ObservableCollection<Family> Families
{
get { return GetValue<ObservableCollection<Family>>(FamiliesProperty); }
private set { SetValue(FamiliesProperty, value); }
}
/// <summary>
/// Register the Families property so it is known in the class.
/// </summary>
public static readonly PropertyData FamiliesProperty = RegisterProperty("Families",
typeof(ObservableCollection<Family>), null);
To save the families at shutdown, override the Close method on the view model which is automatically called as soon as the view is closed by
Catel:
After running the application once, a new file will be stored in the following directory:
C:\Users\[yourusername]\AppData\Roaming\CatenaLogic\WPF.GettingStarted
Up next
Creating the view models
namespace WPF.GettingStarted.ViewModels
{
using Catel.MVVM;
public class PersonViewModel : ViewModelBase
{
}
}
Note that the Person property is decorated with the Model attribute. This automatically makes sure that if a view model is saved, the IEd
itableObject.EndEdit is called. When the view model is canceled, the IEditableObject.CancelEdit is called and all changes on the model
will be reverted.
/// <summary>
/// Gets or sets the first name.
/// </summary>
[ViewModelToModel("Person")]
public string FirstName
{
get { return GetValue<string>(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
/// <summary>
/// Register the FirstName property so it is known in the class.
/// </summary>
public static readonly PropertyData FirstNameProperty = RegisterProperty("FirstName",
typeof(string), null);
/// <summary>
/// Gets or sets the last name.
/// </summary>
[ViewModelToModel("Person")]
public string LastName
{
get { return GetValue<string>(LastNameProperty); }
set { SetValue(LastNameProperty, value); }
}
/// <summary>
/// Register the LastName property so it is known in the class.
/// </summary>
public static readonly PropertyData LastNameProperty = RegisterProperty("LastName",
typeof(string), null);
Note that the properties are decorated with the ViewModelToModel attribute which enables the automatic mappings feature in Catel.
namespace
{
using
using
using
using
using
WPF.GettingStarted.ViewModels
System.Collections.ObjectModel;
Catel;
Catel.Data;
Catel.MVVM;
WPF.GettingStarted.Models;
/// <summary>
/// Gets the family.
/// </summary>
[Model]
public Family Family
{
get { return GetValue<Family>(FamilyProperty);
private set { SetValue(FamilyProperty, value);
}
/// <summary>
/// Register the Family property so it is known in
/// </summary>
public static readonly PropertyData FamilyProperty
RegisterProperty("Family", typeof(Family), null);
}
}
the class.
=
/// <summary>
/// Gets the family members.
/// </summary>
[ViewModelToModel("Family")]
public ObservableCollection<Person> Persons
{
get { return GetValue<ObservableCollection<Person>>(PersonsProperty); }
private set { SetValue(PersonsProperty, value); }
}
/// <summary>
/// Register the Persons property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonsProperty =
RegisterProperty("Persons", typeof(ObservableCollection<Person>), null);
/// <summary>
/// Gets or sets the family name.
/// </summary>
[ViewModelToModel("Family")]
public string FamilyName
{
get { return GetValue<string>(FamilyNameProperty); }
set { SetValue(FamilyNameProperty, value); }
}
/// <summary>
/// Register the FamilyName property so it is known in the class.
/// </summary>
public static readonly PropertyData FamilyNameProperty =
RegisterProperty("FamilyName", typeof(string));
}
}
Up next
Creating the views (user controls)
Person view
To create a new view, right-click the Views folder in the solution => Add => New item... => On-line => and search for Catel as you can see in the
screen below:
Give the new view the name PersonView. The view will be added to the Views folder.
Catel will automatically link the PersonViewModel and PersonView together by naming convention
Now we only need to modify the view itself, the code-behind can stay untouched. Since xaml isn't very interesting for this guide, simply copy/paste
the xaml below and set it as content of the view:
<catel:StackGrid>
<catel:StackGrid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</catel:StackGrid.RowDefinitions>
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</catel:StackGrid.ColumnDefinitions>
<Label Content="First name" />
<Label Content="{Binding FirstName}" />
<Label Content="Last name" />
<Label Content="{Binding LastName}" />
</catel:StackGrid>
Family view
The FamilyView must be created exactly the same way as the PersonView. Use the following xaml as content:
<catel:StackGrid>
<catel:StackGrid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</catel:StackGrid.RowDefinitions>
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</catel:StackGrid.ColumnDefinitions>
<Label Content="Family name" />
<Label Content="{Binding FamilyName}" />
<Label Grid.ColumnSpan="2" Content="Persons" />
<ItemsControl Grid.ColumnSpan="2" ItemsSource="{Binding Persons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<views:PersonView DataContext="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</catel:StackGrid>
Since this view uses the PersonView, it must be defined as a namespace at the top of the file:
xmlns:views="clr-namespace:WPF.GettingStarted.Views"
The thing that is important to notice in the FamilyView is how it uses the PersonView and injects the Person models into the PersonView data
context.
Up next
Creating the views (windows)
Person window
It is very important that the window derives from one of the Catel windows. This is required to make the binding system work (same as
UserControl). Make sure that the window definition in the xaml is either catel:Window or catel:DataWindow
To add a new DataWindow, right-click the Views folder in the solution => Add => New item... => On-line => and search for Catel as you can see
in the screen below:
Give the new view the name PersonWindow. The view will be added to the Views folder.
Note that we can use the PersonViewModel for both the PersonView (user control) and PersonWindow. Both views represent the same
models and view models, just a different context. To make sure that the IUIVisualizerService knows what view to pick first, register the
The template will also create a constructor to inject a view model into the window. Please make sure that the constructor takes a view model of
the type PersonViewModel instead of the generated PersonWindowModel. Then replace the content of the view with the xaml below:
<catel:StackGrid>
<catel:StackGrid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</catel:StackGrid.RowDefinitions>
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</catel:StackGrid.ColumnDefinitions>
<Label Content="First name" />
<TextBox Text="{Binding FirstName, ValidatesOnDataErrors=True,
NotifyOnValidationError=True}" />
<Label Content="Last name" />
<TextBox Text="{Binding LastName, ValidatesOnDataErrors=True,
NotifyOnValidationError=True}" />
</catel:StackGrid>
Family window
The FamilyWindow is a bit different because we want additional logic in this window. We want to create add / edit / remove buttons for the family
members. Therefore we need to create a separate view model which contains this logic.
<catel:StackGrid>
<catel:StackGrid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</catel:StackGrid.RowDefinitions>
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</catel:StackGrid.ColumnDefinitions>
<Label Content="Family name" />
<TextBox Text="{Binding FamilyName, NotifyOnValidationError=True,
ValidatesOnDataErrors=True}" />
<Label Grid.ColumnSpan="2" Content="Persons" />
<catel:StackGrid Grid.ColumnSpan="2">
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</catel:StackGrid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Persons}" SelectedItem="{Binding
SelectedPerson}">
<ListBox.ItemTemplate>
<DataTemplate>
<views:PersonView DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel>
<Button Command="{Binding AddPerson}" Content="Add..." />
<Button Command="{Binding EditPerson}" Content="Edit..." />
<Button Command="{Binding RemovePerson}" Content="Remove" />
</StackPanel>
</catel:StackGrid>
</catel:StackGrid>
Up next
Hooking up everything together
Don't forget to create the right backing fields _uiVisualizerService and _messageService
/// <summary>
/// Gets or sets the selected person.
/// </summary>
public Person SelectedPerson
{
get { return GetValue<Person>(SelectedPersonProperty); }
set { SetValue(SelectedPersonProperty, value); }
}
/// <summary>
/// Register the SelectedPerson property so it is known in the class.
/// </summary>
public static readonly PropertyData SelectedPersonProperty =
RegisterProperty("SelectedPerson", typeof(Person), null);
2. You must import Catel.IoC namespace since it contains ViewModelBase's GetTypeFactory() extension method used below.
using Catel.IoC;
/// <summary>
/// Gets the AddPerson command.
/// </summary>
public Command AddPerson { get; private set; }
/// <summary>
/// Method to invoke when the AddPerson command is executed.
/// </summary>
private async void OnAddPersonExecute()
{
var person = new Person();
person.LastName = FamilyName;
// Note that we use the type factory here because it will automatically take care
of any dependencies
// that the PersonViewModel will add in the future
var typeFactory = this.GetTypeFactory();
var personViewModel =
typeFactory.CreateInstanceWithParametersAndAutoCompletion<PersonViewModel>(person);
if (await _uiVisualizerService.ShowDialogAsync(personViewModel) ?? false)
{
Persons.Add(person);
}
}
/// <summary>
/// Gets the EditPerson command.
/// </summary>
public Command EditPerson { get; private set; }
/// <summary>
/// Method to check whether the EditPerson command can be executed.
/// </summary>
/// <returns><c>true</c> if the command can be executed; otherwise
<c>false</c></returns>
private bool OnEditPersonCanExecute()
{
return SelectedPerson != null;
}
/// <summary>
/// Method to invoke when the EditPerson command is executed.
/// </summary>
private async void OnEditPersonExecute()
{
// Note that we use the type factory here because it will automatically take care
of any dependencies
// that the PersonViewModel will add in the future
var typeFactory = this.GetTypeFactory();
var personViewModel =
typeFactory.CreateInstanceWithParametersAndAutoCompletion<PersonViewModel>(SelectedPer
son);
await _uiVisualizerService.ShowDialogAsync(personViewModel);
}
/// <summary>
/// Gets the RemovePerson command.
/// </summary>
public Command RemovePerson { get; private set; }
/// <summary>
/// Method to check whether the RemovePerson command can be executed.
/// </summary>
/// <returns><c>true</c> if the command can be executed; otherwise
<c>false</c></returns>
private bool OnRemovePersonCanExecute()
{
return SelectedPerson != null;
}
/// <summary>
/// Method to invoke when the RemovePerson command is executed.
/// </summary>
private async void OnRemovePersonExecute()
{
if (await _messageService.Show(string.Format("Are you sure you want to delete the
person '{0}'?", SelectedPerson),
"Are you sure?", MessageButton.YesNo, MessageImage.Question) ==
MessageResult.Yes)
{
Persons.Remove(SelectedPerson);
SelectedPerson = null;
}
}
/// <summary>
/// Gets or sets the selected family.
/// </summary>
public Family SelectedFamily
{
get { return GetValue<Family>(SelectedFamilyProperty); }
set { SetValue(SelectedFamilyProperty, value); }
}
/// <summary>
/// Register the SelectedFamily property so it is known in the class.
/// </summary>
public static readonly PropertyData SelectedFamilyProperty =
RegisterProperty("SelectedFamily", typeof(Family), null);
/// <summary>
/// Gets the AddFamily command.
/// </summary>
public Command AddFamily { get; private set; }
/// <summary>
/// Method to invoke when the AddFamily command is executed.
/// </summary>
private async void OnAddFamilyExecute()
{
var family = new Family();
// Note that we use the type factory here because it will automatically take care
of any dependencies
// that the FamilyWindowViewModel will add in the future
var typeFactory = this.GetTypeFactory();
var familyWindowViewModel =
typeFactory.CreateInstanceWithParametersAndAutoCompletion<FamilyWindowViewModel>(famil
y);
if (await _uiVisualizerService.ShowDialog(familyWindowViewModel) ?? false)
{
Families.Add(family);
}
}
/// <summary>
/// Gets the EditFamily command.
/// </summary>
public Command EditFamily { get; private set; }
/// <summary>
/// Method to check whether the EditFamily command can be executed.
/// </summary>
/// <returns><c>true</c> if the command can be executed; otherwise
<c>false</c></returns>
private bool OnEditFamilyCanExecute()
{
return SelectedFamily != null;
}
/// <summary>
/// Method to invoke when the EditFamily command is executed.
/// </summary>
private async void OnEditFamilyExecute()
{
// Note that we use the type factory here because it will automatically take care
of any dependencies
// that the PersonViewModel will add in the future
var typeFactory = this.GetTypeFactory();
var familyWindowViewModel =
typeFactory.CreateInstanceWithParametersAndAutoCompletion<FamilyWindowViewModel>(Selec
tedFamily);
await _uiVisualizerService.ShowDialogAsync(familyWindowViewModel);
}
/// <summary>
/// Gets the RemoveFamily command.
/// </summary>
public Command RemoveFamily { get; private set; }
/// <summary>
/// Method to check whether the RemoveFamily command can be executed.
/// </summary>
/// <returns><c>true</c> if the command can be executed; otherwise
<c>false</c></returns>
private bool OnRemoveFamilyCanExecute()
{
return SelectedFamily != null;
}
/// <summary>
/// Method to invoke when the RemoveFamily command is executed.
/// </summary>
private async void OnRemoveFamilyExecute()
{
if (await _messageService.Show(string.Format("Are you sure you want to delete the
family '{0}'?", SelectedFamily),
"Are you sure?", MessageButton.YesNo, MessageImage.Question) ==
MessageResult.Yes)
{
Families.Remove(SelectedFamily);
SelectedFamily = null;
}
}
<catel:StackGrid>
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</catel:StackGrid.ColumnDefinitions>
<ListBox ItemsSource="{Binding Families}" SelectedItem="{Binding SelectedFamily}">
<ListBox.ItemTemplate>
<DataTemplate>
<views:FamilyView DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel>
<Button Command="{Binding AddFamily}" Content="Add..." />
<Button Command="{Binding EditFamily}" Content="Edit..." />
<Button Command="{Binding RemoveFamily}" Content="Remove" />
</StackPanel>
</catel:StackGrid>
Now run the application and you should see your fully functional family management application.
Up next
Finalizing the application
Adding validation
Adding validation with Catel is extremely easy. There are two flavors to pick from, but they work exactly the same (since both the models and view
models internally derive from ModelBase). To add validation to the Person model, use this code:
Note that this validation code can be used in both the model and/or the view models
Note that the xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" must be added in order for the code above to
compile
/// <summary>
/// Gets the filtered families.
/// </summary>
public ObservableCollection<Family> FilteredFamilies
{
get { return GetValue<ObservableCollection<Family>>(FilteredFamiliesProperty); }
private set { SetValue(FilteredFamiliesProperty, value); }
}
/// <summary>
/// Register the FilteredFamilies property so it is known in the class.
/// </summary>
public static readonly PropertyData FilteredFamiliesProperty =
RegisterProperty("FilteredFamilies", typeof(ObservableCollection<Family>));
/// <summary>
/// Gets or sets the search filter.
/// </summary>
public string SearchFilter
{
get { return GetValue<string>(SearchFilterProperty); }
set { SetValue(SearchFilterProperty, value); }
}
/// <summary>
/// Register the SearchFilter property so it is known in the class.
/// </summary>
public static readonly PropertyData SearchFilterProperty =
RegisterProperty("SearchFilter", typeof(string), null, (sender, e) =>
((MainWindowViewModel)sender).UpdateSearchFilter());
Note that this property contains an additional change callback function which will be called when the property has changed.
Add the following import to the view model. You will needed because native ObservableCollection class does not support ReplaceRange()
using Catel.Collections;
/// <summary>
/// Updates the filtered items.
/// </summary>
private void UpdateSearchFilter()
{
if (FilteredFamilies == null)
{
FilteredFamilies = new ObservableCollection<Family>();
}
if (string.IsNullOrWhiteSpace(SearchFilter))
{
FilteredFamilies.ReplaceRange(Families);
}
else
{
var lowerSearchFilter = SearchFilter.ToLower();
FilteredFamilies.ReplaceRange(from family in Families
where
!string.IsNullOrWhiteSpace(family.FamilyName) &&
family.FamilyName.ToLower().Contains(lowerSearchFilter)
select family);
}
}
Last but not least, add this to the Initialize method after the Families is set from the IFamilyService
<catel:StackGrid>
<catel:StackGrid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</catel:StackGrid.RowDefinitions>
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</catel:StackGrid.ColumnDefinitions>
<catel:StackGrid Grid.ColumnSpan="2">
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</catel:StackGrid.ColumnDefinitions>
<Label Content="Filter:" />
<TextBox Text="{Binding SearchFilter}">
<i:Interaction.Behaviors>
<catel:UpdateBindingOnTextChanged UpdateDelay="500" />
</i:Interaction.Behaviors>
</TextBox>
</catel:StackGrid>
<ListBox x:Name="listBox" ItemsSource="{Binding FilteredFamilies}"
SelectedItem="{Binding SelectedFamily}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<i:Interaction.Behaviors>
<catel:DoubleClickToCommand Command="{Binding
ElementName=listBox, Path=DataContext.EditFamily}" />
</i:Interaction.Behaviors>
<views:FamilyView DataContext="{Binding}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel>
<Button Command="{Binding AddFamily}" Content="Add..." />
<Button Command="{Binding EditFamily}" Content="Edit..." />
<Button Command="{Binding RemoveFamily}" Content="Remove" />
</StackPanel>
</catel:StackGrid>
Examples
There are lots of examples written for Catel. Most of the examples are located in the Catel Examples repository, but there are some examples
which can are listed below:
MyMediaStuff - WPF demo application that uses media display, nested user controls and timers
If you have an example application that you want to share, let us know!
WPF Examples
Advanced WPF example
Authentication example
Browser application example
Master/detail example
Multilingual example
MVVM communication styles
Person application WPF example
Validation example
Viewmodel lifetime example
Silverlight examples
Advanced Silverlight example
Navigation example
Nested user controls example
Person application Silverlight example
Windows Phone examples
Bing maps example
Sensors example
Shopping list example
Open source using Catel
WPF Examples
Advanced WPF example
Authentication example
Browser application example
Master/detail example
Multilingual example
MVVM communication styles
Person application WPF example
Validation example
Viewmodel lifetime example
Screenshots
Authentication example
This example shows how to use authentication for views. You can either choose to run the window as read-only user or as administrator. When
running as read-only user, you will notice that the controls are disabled, hidden and collapsed. When running as administrator, the controls will all
be available.
Screenshots
Screenshots
Master/detail example
This example shows how to create a master/detail with a UserControl as detail which automatically responds to changes in the master.
Screenshots
Multilingual example
This example show how to use the multiple languages in Catel. This example is also used to test new languages that are translated for Catel.
Screenshots
Services
The best way to communicate between view models are services. These are implementations registered in the ServiceLocator that can
automatically be injected into view models. Using services has several advantages:
1.
2.
3.
4.
The service can be shared among any view models that require the logic
Easy to mock because you can inject mock implementations of the services during unit tests
Easy to change the logic since it only needs to be changed in a single location
No need for view models to be aware of each other (separation of concerns)
A service is a custom implementation of any logic. A good example is the IMessageService in Catel. It is a service that allows a view model to
show a message. If you want to change the way messages are shown to the users, you only have to register a new implementation in the Service
Locator and all will be fixed.
InterestedIn
The InterestedIn method in Catel is the easiest way to communicate between view models. This can be done by simply adding the InterestedInAtt
ribute on top of a view model and override the OnViewModelPropertyChanged and OnViewModelCommandExecuted methods.
MessageMediator
Thought more flexible, the MessageMediator technique requires more work because the developer is responsible for defining custom messages,
registering and unregistering specific messages and sending the messages via the MessageMediator.
Screenshots
Screenshots
Validation example
This example shows all the different validation techniques available in Catel. The following validation techniques are shown:
Validation via validate methods
Validation via data annotations
Validation in model
Validation in IValidator
Validation via FluentValidation Extension
Screenshots
Screenshots
Silverlight examples
Advanced Silverlight example
Navigation example
Nested user controls example
Person application Silverlight example
Screenshots
Navigation example
This example shows how to create a page navigation application using Silverlight. It uses the INavigationService to implement navigation.
Screenshots
Screenshots
Screenshots
Screenshots
Sensors example
This example shows how to access all Windows Phone sensors and how to emulate them (for example, during unit tests or in the emulator).
Screenshots
Screenshots
Problem solving
As with every framework or toolkit, developers run into problems. This can be bugs or abuse of the API. Catel has several ways for developers to
solve the problems themselves before contacting the team with the possible issue.
Enabling the log
Enabling stepping through the code
ApiCop
#if DEBUG
Catel.Logging.LogManager.AddDebugListener();
#endif
Now you can see all the log messages in the output window.
ApiCop
Catel provides a feature called ApiCop. This will give you information about the most cases where the API of Catel is abused or not used in the
right way. The ApiCop feature will create an advisory report at the end of an application with tips on how to improve the feature usage in Catel.
Below is an example report:
****************************************************************
ApiCop (r) results of 'DataWindowTest.WPF' v1.0.0.0
recorded on 2014-02-19 10:06
To ignore rules, call ApiCopManager.IgnoredRules.Add([rulename]);
For more information about ApiCop, visit the website:
https://catelproject.atlassian.net/wiki/display/CTL/ApiCop
****************************************************************
================================================================
DATAWINDOWTEST.VIEWS.AVIEW
================================================================
Cop TargetType: Catel.Windows.Controls.MVVMProviders.Logic.UserControlLogic
Rule: UserControlLogic.InfoBarMessageControl (Error)
For more information about this rule, visit
https://catelproject.atlassian.net/wiki/display/CTL/Performance+considerat
ions
****************************************************************
End of ApiCop (r) results, generation took '00:00:00.137'
****************************************************************
Performance considerations
While developer software, it is very important to keep an eye on the performance. Catel itself does perform very well, but there are some caveats
that you should be aware of. If you have the feeling the the application is laggy or slow, or if you want to make sure to squeeze the best
performance out of Catel, the checklist below is very important.
Use the ApiCop feature to get a detailed advisory report on your software
General
Disable the call to LogManager.AddDebugListener
Disable event subscriptions of child values for ModelBase
Disabling validation during activities where validation is not required
Use LeanAndMeanModel property on ModelBase
Preloading assemblies into the AppDomain
Warming up the serializers
MVVM
Set SkipSearchingForInfoBarMessageControl on UserControl to true
Use the FastObservableCollection
Specify throttling on the ViewModelBase
General
Disable the call to LogManager.AddDebugListener
The DebugListener is a very useful class while developing an application. It throws all the logging of Catel to the output window of Visual Studio
which allows you to view exactly what happens behind the scenes. However, writing all these logs to the output window is very expensive and
might cause an application to perform badly.
Therefore, it is important to disable any call to LogManager.AddDebugListener when releasing an application or while performance testing.
ModelBase.DefaultDisableEventSubscriptionsOfChildValuesValue = false;
ModelBase.SuspendValidationForAllModels = true;
When loading lots of models, it is not required to get support for validation and change notifications. Notifications and validation can be suspended
per model (using the LeanAndMeanModel property) or globally using the GlobalLeanAndMeanModel property.
WPF application
In App.xaml.cs, add the following code
ASP.NET application
In global.asax, add the following code:
MVVM
Set SkipSearchingForInfoBarMessageControl on UserControl to true
By default, Catel assumes that an InfoBarMessageControl is located on any window. However, it might be that this control is not located on a
window that contains an instance of the UserControl class. This might decrease the performance, especially when lots of user controls are used in
a hierarchical way. The cause is that the UserControlLogic searches for an InfoBarMessageControl to register the view model to.
If no InfoBarMessageControl is located on a container, make sure to set SkipSearchingForInfoBarMessageControl to true.
The FastObservableCollection does not raise events for every item, but only invokes events for the complete range of items added to or removed
from the collection.
When modifying a large collection of items, it is not required to raise change events for each added / removed value. Therefore the FastObservabl
eCollection will disable change notifications until the full collection modification is done and then raise the change events just once.
The AdvancedDemo example contains a demo that shows the impact of throttling
Catel.Core
ApiCop
Argument checking
Caching
Configuration
Data handling
Exception handling
IoC (ServiceLocator and TypeFactory)
Logging
Messaging
Multilingual
Parallel invocation and tasks
Preventing memory leaks
Reflection
Scoping
Serialization
Thread safe code
Validation
ApiCop
Introduction
Usage for end-users (developers)
Architecture
For more information, see:
Cops
Rules
Listeners
Introduction
ApiCop is a unique feature in Catel that helps developers make sure the users are using the Api of frameworks the right way. This functionality
has been evolved from experience that users want logical advice including links to documentation when and why they are not using an Api in its
full potential.
The advantage of ApiCop is that it will only execute any code to trace the application when a debugger is attached. If no debugger is attached, the
feature will be fully disabled to prevent an impact of performance when running the software without debuggers.
The code above writes the results of the ApiCop feature to the console, which will look like this:
****************************************************************
ApiCop (r) results of 'DataWindowTest.WPF' v1.0.0.0
recorded on 2014-02-19 10:06
To ignore rules, call ApiCopManager.IgnoredRules.Add([rulename]);
For more information about ApiCop, visit the website:
https://catelproject.atlassian.net/wiki/display/CTL/ApiCop
****************************************************************
================================================================
DATAWINDOWTEST.VIEWS.AVIEW
================================================================
Cop TargetType: Catel.Windows.Controls.MVVMProviders.Logic.UserControlLogic
Rule: UserControlLogic.InfoBarMessageControl (Error)
For more information about this rule, visit
https://catelproject.atlassian.net/wiki/display/CTL/Performance+considerat
ions
[DataWindowTest.Views.AView] Feature used '0' of '2' times, consider
turning it off by default
---------------------------------------------------------------Cop TargetType: Catel.Windows.Controls.MVVMProviders.Logic.UserControlLogic
Rule: UserControlLogic.CreateWarningAndErrorValidator (Error)
For more information about this rule, visit
https://catelproject.atlassian.net/wiki/display/CTL/Performance+considerat
ions
[DataWindowTest.Views.AView] Feature used '0' of '2' times, consider
turning it off by default
---------------------------------------------------------------================================================================
DATAWINDOWTEST.VIEWS.BVIEW
================================================================
Cop TargetType: Catel.Windows.Controls.MVVMProviders.Logic.UserControlLogic
Rule: UserControlLogic.InfoBarMessageControl (Error)
For more information about this rule, visit
https://catelproject.atlassian.net/wiki/display/CTL/Performance+considerat
ions
[DataWindowTest.Views.BView] Feature used '0' of '2' times, consider
turning it off by default
---------------------------------------------------------------Cop TargetType: Catel.Windows.Controls.MVVMProviders.Logic.UserControlLogic
Rule: UserControlLogic.CreateWarningAndErrorValidator (Error)
For more information about this rule, visit
https://catelproject.atlassian.net/wiki/display/CTL/Performance+considerat
ions
[DataWindowTest.Views.BView] Feature used '0' of '2' times, consider
turning it off by default
----------------------------------------------------------------
****************************************************************
End of ApiCop (r) results, generation took '00:00:00.137'
****************************************************************
Architecture
ApiCop provides a set of components that help developers of frameworks to aid developers during the usage of their frameworks. Below is a
graphical presentation of the components.
The green parts are the only components that end-users will use. The blue parts are components for Api developers.
Cops
The IApiCop is responsible for containing all the rules that are used inside a class. A class should contain only one IApiCop and one or more
rules.
Retrieving an IApiCop for a class
Rules
Rules always belong to an IApiCop instance. Rules are registered once and then updated when needed, based on the requirement of the rule.
Rules are normally custom-made, but Catel does provide a few base implementations.
Registering rules
Updating rules
Available rules
UnusedFeatureApiCopRule
Usage
Registering rules
To register a rule in the IApiCop, use the following code:
ApiCop.RegisterRule(new
UnusedFeatureApiCopRule("UserControlLogic.InfoBarMessageControl", "The
InfoBarMessageControl is not found in the visual tree. This will have
a negative impact on performance. Consider setting the
SkipSearchingForInfoBarMessageControl or
DefaultSkipSearchingForInfoBarMessageControlValue to true.",
ApiCopRuleLevel.Error,
"https://catelproject.atlassian.net/wiki/display/CTL/Performance+considerations"));
The first argument is the name of the rule. This is a unique identifier and allows users to ignore rules by this name. The next parameter is a good
extension which will explain to the end-developer what is wrong and why the rule is created in the first place. In this case it is about the InfoBarMe
ssageControl which might have a negative impact on performance.
It is good practice to use the ClassName.FeatureName for the rule names
Updating rules
Once a rule is registered, it must be updated to actually provide any useful information. How a rule must be updated depends on the rule
implementation, but the code below shows how the feature can be used. This specific rule is implemented to check if the InfoBarMessageControl i
s actually used by the software. If it is, the counter is increased as "feature being used", otherwise "feature not being used".
This code shows the regular code executed by the framework (the search in the visual tree) and the update of the rule. Note that the update
method only requires the following things: the name, a callback method and a tag. The reason a callback method is being used is that the callback
will only be executed when a debugger is attached to minimize the impact on performance in production scenarios.
It is good practice to use the final class name as as tag (especially when the rule is registered in a base class)
Available rules
Catel provides the following rules out of the box.
UnusedFeatureApiCopRule
This rule contains several counters. Each time code is executed, one must call the IncreaseCount method and specify if the feature is being used.
Usage
ApiCop.UpdateRule<UnusedFeatureApiCopRule>("ruleName",
rule => rule.IncreaseCount(isFeatureBeingUsed));
Listeners
Listeners are a way to get information about the cops and their rules in the framework. The ApiCopManager will take care of all the retrieval of the
results and the registration of the cops. The registered listeners are used by the ApiCopManager.WriteResults method to write the output to. To
add a listener and be able to see the output of the ApiCop feature, use the following code:
ApiCopManager.WriteResults();
Customizing grouping
Creating custom listeners
Customizing grouping
It is possible to group the listeners. To specify the grouping, use the following code:
Description
Cop
Rule
Tag
Sort by tag, which is dependent on the rule implementation. It is recommended though to use the final class name as tag.
Argument checking
It is best practice to always check if the input to a method is correct. If not, an exception should be thrown. Most people do not check for
exceptions correctly and lots of null reference exceptions inside a deep stacktrace are hard to solve.
Catel does check the input on every method. Normally, a check would look like this:
However, Catel extensively logs all behavior, thus all the checks started to look like this:
Handling input correctly in such a case takes a lot of space and repetitive code. Therefore the Argument class is developed. This way, it is very
simple to check for arguments:
Caching
Caching is about improve applications performance. The most expensive performance costs of the applications are related with the data
retrieving, typically when this data requires to be moved a cross the network or loaded from disk. But some data have an slow changing behavior
(a.k.a non-volatile) and doesn't requires to be re-read with the same frequency of the volatile data.
So, to improve your application performance and handling this "nonvolatile" data from a pretty clean approach, Catel comes with a CacheStorage
<TKey, TValue> class. Notice that the first generic parameter is the type of the key and the second the type of the value the will be store, just like
a Dictionary<TKey, TValue> but CacheStorage isn't it just a Dictionary. This class allows you to retrieve data and storing it into the cache with
single statement and also helps you to handle expiration policy if you need it.
When this statement is executed more than once times with the with the same key, the value will be retrieved from the cache storage instead from
the service call. The service call will be executed just the first time or if the item is removed from the cache manually or automatically due by the
expiration policy.
You can specify an specific expiration policy for an item when it's storing:
The default cache policy specified at cache storage initialization will be used if during item storing the expiration policy is not specified.
Type
Description
AbsoluteExpirationPolicy
Time-base
DurationExpirationPolicy
Time-base
ExpirationPolicy.Duration(TimeSpan.FromMinutes(5))
SlidingExpirationPolicy
CustomExpirationPolicy
CompositeExpirationPolicy
Time-base
Custom
Custom
ExpirationPolicy.Sliding(TimeSpan.FromMinutes(5))
new CompositeExpirationPolicy().Add(ExpirationPolicy.Sliding(
TimeSpan.FromMinutes(5))).Add(ExpirationPolicy.Custom(()=>...))
The base constructor has a parameter to indicate if the policy can be reset. Therefore, if you call the base constructor with false then
the OnReset method will never called.
Configuration
Catel makes it very easy to use configurations on all supported platforms.
Getting values from the configuration
Setting values to the configuration
Customizing the way values are stored
Below is a table to explain what technology is used per platform to retrieve and store configuration values.
Platform
Technology
.NET
ConfigurationManager.AppSettings
Silverlight
IsolatedStorageSettings.ApplicationSettings
Windows Phone
IsolatedStorageSettings.ApplicationSettings
WinRT
ApplicationData.Current.LocalSettings
PCL
Not supported
The code above will retrieve the values from the configuration. If the configuration value does not exist, it will return 42 as default value.
It's best to retrieve the service from the dependency resolver or let it be injected into the classes using it
It's best to retrieve the service from the dependency resolver or let it be injected into the classes using it
Data handling
This part of the documentation is all about data handling the way it should that is available via Catel. Some parts are based on the article on Code
Project, but this documentation is more up-to-date.
The first thing that is important is that lots of developers lose way too much time writing custom serializable objects. Serialization is a field of
expertise, and only a handful of developers I know really master the serialization of objects (think of version changes of the assembly, class
changes (new or removed properties), etc.). Most developers think they master serialization by creating a BinaryFormatter object like the code
belows show:
ObservableObject
The ObservableObject is a very lightweight class that only implements the INotifyPropertyChanging and INotifyPropertyChanged interfaces. This
class is ideal for simple objects that only need property notification. Below is an example:
DispatcherObservableObject
Note that the DispatcherObservableObject is located in Catel.MVVM because it uses the IDispatcherService
The DispatcherObservableObject is a class that derives from the ObservableObject class. The only difference is that the DispatcherObservableO
bject will dispatch all property change notifications to the UI thread. Below is a class that uses the DispatcherObservableObject and is thread-safe
for the change notifications.
ModelBase
Using the class
Defining properties
Default values for reference types
Functionality provided out of the box
The ModelBase (previously known as the DataObjectBase) class is a generic base class that can be used for all your data classes.
Fully serializable
It is now really easy to store objects on disk or serialize them into memory, either binary or in XML. The data object supports this out of
the box, and automatically handles the (de)serialization.
Support property changed notifications
The class supports the INotifyPropertyChanging and INotifyPropertyChanged interfaces so this class can easily be used in WPF,
Silverlight and applications to reflect changes to the user.
Backwards compatibility
When serializing your objects to binary, it is hard to maintain the right versions. When you add a new property to a binary class, or
change a namespace, the object cannot be loaded any longer. The data object base takes care of this issue and supports backwards
compatibility.
Validation
The class implements the IDataErrorInfo interface so it is possible to validate the data object and check the errors. This way, no custom
validation code needs to be written outside the data class.
Backup & revert
The class implements the IEditableObject interface which makes it possible to create a state of the object. Then all properties can be
edited, and finally, the changes can be applied or cancelled.
/// <summary>
/// MyObject class which fully supports serialization,
/// property changed notifications, backwards compatibility and error checking.
/// </summary>
#if !SILVERLIGHT
[Serializable]
#endif
public class MyObject : ModelBase<MyObject>
{
/// <summary>
/// Initializes a new object from scratch.
/// </summary>
public MyObject() { }
#if !SILVERLIGHT
/// <summary>
/// Initializes a new object based on <see cref="SerializationInfo"/>.
/// </summary>
/// <param name="info"><see cref="SerializationInfo"/>
//
that contains the information.</param>
/// <param name="context"><see cref="StreamingContext"/>.</param>
protected MyObject(SerializationInfo info, StreamingContext context)
: base(info, context) { }
#endif
}
As you can see in the code above, the MyObject class derives from ModelBase and provides an empty constructor, but also a constructor that is
used for binary deserialization. The code above might look complex, but it is created using the model code snippet, and you only have to type the
name of the class.
Defining properties
Defining properties for the class is very easy, and works the same like dependency properties. The advantages of this way of defining properties
are:
Properties defined like this are automatically included during serialization; no need to specify complex data contracts;
You can specify a default value for a property which will be used when the class is constructed or the property is not found during
deserialization (in case this property is added to an existing class);
The PropertyData object can be used to retrieve property values so the compiler checks for errors;
You can directly subscribe to change notifications, and all properties automatically support INotifyPropertyChanged out of the box.
Below is the code that defines a new property Name of type string:
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name
{
get { return GetValue<string>(NameProperty); }
set { SetValue(NameProperty, value); }
}
/// <summary>
/// Register the Name property so it is known in the class.
/// </summary>
public static readonly PropertyData NameProperty = RegisterProperty("Name",
typeof(string), string.Empty);
A registered property can be excluded from serialization if wanted. When the object is deserialized, the default value will be used for the property
in that case.
However, instead of creating a new collection for each new object with this property, only one collection will be created that will be used by all
classes that have this property registered. One solution is to pass null as default value and create the collection in the constructor. A better
solution is to use the override of RegisterProperty with the callback parameters:
This way, every time a new value is needed, the callback will be invoked to create the default value and you will have a true default value for
reference types.
2. Derive from EntityBase instead of ModelBase so the layer that loads the data can clear the IsDirty flag.
3. When loading the data from the database and setting the initial values, use this code:
Note the ClearDirtyFlag call, which is very important to make the IsDirty property behave correctly.
4. Check the IsDirty of the model, not the view model when checking whether the model is dirty inside a view model.
It is possible to ignore the default Catel properties in the models for EF code-first. To accomplish this, use the following code:
Or, if you only want to do this for classes inheriting Modelbase, use the following code:
When the values are not provided, the old and new value are set to null.
Some sidenotes
As you might have noticed, the AdvancedPropertyChangedEventArgs also provide the IsOldValueMeaningful and the IsNewValueMeaningful. Th
ese are introduced because it is not always possible to determine the old or new value (for example, when the property name is string.Empty,
there is no old value or new value). Therefore, the OldValue and NewValue properties are null, but doesn't mean that those are the actual old and
new values.
It is always required to check whether the values are meaningful before actually handing them:
WCF services
Starting with version 2.3, both the windows and Silverlight (Silverlight and WP7) libraries use the DataContractSerializer to (de)serialize xml by
default for the ModelBase. The xml serialization technique supports complex nested object graphs. This means that classes deriving from ModelB
ase can be used to transfer data using WCF services.
With the help of the Catel.Fody plugin, it is possible to dynamically weave custom GetXmlSchema methods so no custom code is
required to support WCF and DataContracts
There is no need to decorated the classes with DataMember or DataContract attributes with one single exception: when a class contains
properties that are defined using a base class, the descending (or inherited) classes must be known. Catel could, in theory, reflect the whole App
Domain to gather these classes, but that would be a major hit on performance. Therefore the inherited classes must be defined manually using
the KnownType attribute.
When adding the service reference, make sure to check the "Reuse types in all referenced assemblies" like shown in the image below:
Exception handling
With exception handling in Catel, it is possible to create an exception handling policy and execute code in a safe way without have to check all the
exception types manually. Catel exposes this technique via the IExceptionService.
Setting up the IExceptionService
Executing code using the IExceptionService
Executing an action
Executing a function
Use the retry capabilities
Retry Immediately
Retry defined
Process with retry
Subscribe to the retry events
Handling exceptions manually
Unregistering exceptions
Buffering
Define the way to buffer
Subscribe to the buffering events
Determine if an exception type are registered for handling
Get a specific exception handler
The IExceptionService checks for type hierarchy. For example, when an exception as type Exception is registered, this handler will
handle all exceptions
Executing an action
var dependencyResolver = this.GetDependencyResolver();
var exceptionService = dependencyResolver.Resolve<IExceptionService>();
exceptionService.Process(() => { throw new ArgumentOutOfRangeException(); });
Executing a function
var dependencyResolver = this.GetDependencyResolver();
var exceptionService = dependencyResolver.Resolve<IExceptionService>();
var result = exceptionService.Process<int>(() => 1 + 1);
You can process yours actions asynchronously by using the ProcessAsync method.
Firstly, you need to define how the IExceptionService will retry your action in case of error, two possibilities are provided for that.
Retry Immediately
When you setting up your exceptions on IExceptionService, you have to additionnally use the OnErrorRetryImmediately method like shown below
:
This method will say to the IExceptionService to retry the action each times this one throw an exception until it succeed and without to wait before
the next retry.
You can also specify the number of times you want the IExceptionService to retry immediately like this for example : OnErrorRetryImme
diately(5)
Retry defined
You have also the possibility to define more deeply the way you want your actions to be retried by using the OnErrorRetry method like shown
below.
Where 5 represents the nombre of times the action will be retried and TimeSpan.FromMinutes(2) the interval between the retries.
If the exception can be handled, the registered action will be executed, but your code can safely continue. If the exception (in this case DivideByZ
eroException) is not registered, the HandleException method will rethrow the exception.
Unregistering exceptions
Although it will probably hardly used, it is possible to unregister exceptions again using the code below:
Buffering
Define the way to buffer
You can want to throttle down the number of exceptions you receive when a production process goes awry for example. You can do it through the
UsingTolerance extension method as shown below :
Component that is responsible to create types. This uses the IServiceLocator to retrieve the types which are required to instantiate a
type.
DependencyResolver
Light-weight implementation of the IServiceLocator which does not expose any register methods, but only allows to resolve types.
Dependency injection
The ServiceLocator in Catel supports dependency injection.
Introduction to dependency injection
Using dependency injection in Catel
Constructor injection
Manually defining the constructor to use for dependency injection
Advanced dependency injection
Property injection
Type is automatically determined based on property type
Type is manually defined
Using tags
Disabling dependency injection
There are other ways of using dependency injection, for example via attributes. This documentation will focus on dependency injection
via the constructor only
Constructor injection
Dependency injection via the ServiceLocator in Catel is enabled by default. This means that when a type is resolved from the container, it will
automatically use dependency injection to construct the type if it is not registered as instance.
It will first search for all available constructors on the type that will be instantiated. Then, for each constructor, starting with the one with the most
parameters, it will try to retrieve all values. If one fails, it will go to the next. If all fail, it will try to use the default constructor without parameters. If
that fails as well, then the type cannot be constructed and an exception will be thrown.
To get a better understanding of what happens, see the class below:
When the MyClass will be retrieved from the ServiceLocator, this will happen:
1. Find constructor with most parameters (the one with both firstDependency and secondDependency). If both IFirstDependency and
ISecondDependency can be resolved from the ServiceLocator, the type will be constructed with the constructor. Otherwise it will proceed
with step 2.
2. Find next constructor with most parameters (the one with only firstDependency). If IFirstDependency can be resolved from the
ServiceLocator, the type will be constructed with the constructor. Otherwise it will proceed with step 3.
3. At this point, no constructor could be used. In this case, the ServiceLocator will try to use the default constructor (the one without
parameters) as last resort to instantiate the type.
With the new technology, such a constructor can be rewritten to truly support dependency injection:
This feature is initially written to support dependency injection in combination with nested user controls
The advanced dependency injection can be used by using the TypeFactory class. Below is an example on how to create a new type using
advanced dependency injection:
var personViewModel =
TypeFactory.Default.CreateInstanceWithParametersAndAutoCompletion<PersonViewModel>(new
Person());
As you can see it is only required to pass in the objects that are not registered in the IoC container. All other dependencies will be automatically
resolved from the ServiceLocator.
Note that the order of the parameters must be the same as the constructor, otherwise the TypeFactory cannot determine the right
constructor to use
Property injection
Starting with Catel 3.8, it is also possible to use property injection. The difference with constructor injection is that the TypeFactory will
automatically set up all properties that required dependency injection.
Note that the Catel team recommends using constructor injection over property injection. Property injection looks like a silver bullet, but
is very tricky because:
1) It does not allow you to check for null values and store dependencies in private fields (when)?
2) Dependency Injection is just a technique. When using a constructor, you can force a user to provide the value and check the input.
With property injection, you can only hope that the user will set them for you, there is no way to check this (unless that is some after
constructor and dependency injection initialization routine. This is never the case if a user manually creates a type though.
To use property injection, simply decorate the properties of a class with the Inject attribute. Below are several options:
Using tags
It is also possible to determine the tag of a registered dependency:
ServiceLocator.Default.SupportedDependencyInjection = false
Registering a type
Use the following code to register a specific type in the ServiceLocator:
ServiceLocator.Default.RegisterType<IPleaseWaitService, PleaseWaitService>();
If both the ImplementingInstance and ImplementingType are filled, the ImplementingIntance will be used.
Resolving a type
To retrieve the implementation of a service, use the following code:
Dependency injection
The ServiceLocator in Catel supports dependency injection.
There are other ways of using dependency injection, for example via attributes. This documentation will focus on dependency injection
via the constructor only
automatically use dependency injection to construct the type if it is not registered as instance.
It will first search for all available constructors on the type that will be instantiated. Then, for each constructor, starting with the one with the most
parameters, it will try to retrieve all values. If one fails, it will go to the next. If all fail, it will try to use the default constructor without parameters. If
that fails as well, then the type cannot be constructed and an exception will be thrown.
To get a better understanding of what happens, see the class below:
When the MyClass will be retrieved from the ServiceLocator, this will happen:
1. Find constructor with most parameters (the one with both firstDependency and secondDependency). If both IFirstDependency and
ISecondDependency can be resolved from the ServiceLocator, the type will be constructed with the constructor. Otherwise it will proceed
with step 2.
2. Find next constructor with most parameters (the one with only firstDependency). If IFirstDependency can be resolved from the
ServiceLocator, the type will be constructed with the constructor. Otherwise it will proceed with step 3.
3. At this point, no constructor could be used. In this case, the ServiceLocator will try to use the default constructor (the one without
parameters) as last resort to instantiate the type.
ServiceLocator.Default.SupportedDependencyInjection = false
Custom initialization
All types created with the TypeFactory can be initialized with custom code. This can be done by implementing the INeedCustomInitialization interf
ace. As soon as a type is created, the TypeFactory will check whether it implements the INeedCustomInitialization interface. If so, it will call the Ini
tialize method of the interface.
To prevent misuse of the Initialize method, it is best to implement the interface explicitly
In this example, a view model is created with a custom dependency scope. When writing an extension method for the view models, it is
impossible to get to this custom scope:
One option to solve this is to create a property on the view model called DependencyResolver or ServiceLocator. However, it is not the
responsibility of the view model to store this property. In fact, the view model does not know which scoping was used to create itself. The only way
to solve this is to inject the IServiceLocator into the view model, but that's not a good practice.
Below is a rewritten version of the extensions class which uses the DependencyResolverManager:
Now you have the actual IDependencyResolver that was use to create the view model and can easily provide the right logic with the right scoping.
Note that there will only be a single instance of a DependencyResolverManager. It is possible to customize the default instance, but
there is no need for different scoping of DependencyResolverManager instances so it is valid to use the static instance
Note that it is not required to register a DependencyResolver for instances created with the TypeFactory. The TypeFactory automaticall
y registers the DependencyResolver used in the DependencyResolverManager.
Below is a graph that shows how the dependency resolver of an instance is determined:
Note that these registrations are type-specific. You cannot register an interface and all classes deriving from that interface will return the
same DependencyResolver. All actual types must be registered separately.
Note that there is no use to override the DependencyResolverManager as the example, but this keeps it easy to understand
public class X
{
public X(Y y) { }
}
public class Y
{
public Y(Z z) { }
}
public class Z
{
public Z(X x) { }
}
Note how a round-trip of dependencies is created which will result in a StackOverflowException somewhere in your code. Below is a graphical
example what happens. Note that the dotted line is showing the circular dependency causing the StackOverflowException.
TypeRequestInfo
The first step for the integrity checker is to make sure that it knows what types are being requested from the ServiceLocator (which will be
instantiated by the TypeFactory if required). This class contains all the information about a type being created by the TypeFactory:
Type
Tag (optional, can be used to differentiate different instances of the same type registration)
TypeRequestPath
Now we have detailed information about the types being constructed, it is very important to keep track of the types which are being created by the
TypeFactory. During the construction of a type, the TypeFactory will request the ServiceLocator for a type, which will ask the TypeFactory to
construct the type again. Each time the TypeFactory starts constructing a type (and currently has a TypeRequestPath), it will create a new
instance of the TypeRequestInfo and add it to the TypeRequestPath. The diagram below shows how the TypeRequestPath will evolve.
Once the TypeRequestPath will contain a duplicate instance of a TypeRequestInfo, it will become invalid (which means there is a circular type
dependency).
Note that this is a very simple example, but normally a type will have several services injected which can have dependencies on their
own as well which can cause a very complex type request path
diagram below.
As you can see, there is a lot of communication between the ServiceLocator and TypeFactory. In the TypeRequestPath example we already saw
how the path will become invalid when it contains a duplicate instance of the TypeRequestInfo. The TypeRequestPath will then throw a CircularD
ependencyException with all the necessary information to solve the issue:
Now you will find the issue in no-time and save yourself a lot of your valuable time!
[ServiceLocatorRegistration(typeof(IMyClass))]
public class MyClass : IMyClass
{
}
All registration options are available in attribute based registration, such as registration type and tag, as ServiceLocatorRegistrationAttribute const
ructor arguments. The following code shows how use such options (it registers the MyClass using the IMyClass interface in a transient way (new
type every time it is resolved) using the tag MyTag:
Here, we say to the ServiceLocator to ignore all types included into the namespace which belong to the IFooService type.
You can manually specify the namespace of the types to exclude using the ExcludeAllTypesOfNamespace method like this: ExcludeAll
TypesOfNamespace("MyNamespace")
<configuration>
<configSections>
<sectionGroup name="catel">
<section name="ioc" type="Catel.IoC.IoCConfigurationSection, Catel.Core" />
</sectionGroup>
</configSections>
...
</configuration>
In the example above we also create a section group named catel to group all Catel related configuration sections.
<configuration>
<configSections>
<sectionGroup name="catel">
<section name="ioc" type="Catel.IoC.IoCConfigurationSection, Catel.Core" />
</sectionGroup>
</configSections>
<catel>
<ioc>
<serviceLocatorConfigurations>
<serviceLocatorConfiguration [name="default"]>
<register interfaceType="Catel.MVVM.Services.IUIVisualizerService"
implementationType="Catel.MVVM.Services.UIVisualizerService" />
<register interfaceType="Catel.MVVM.Services.IProcessService"
implementationType="Catel.MVVM.Services.ProcessService" />
</serviceLocatorConfiguration>
</serviceLocatorConfigurations>
</ioc>
</catel>
</configuration>
To configure a service locator from the default service locator configuration use the following code:
<configuration>
<configSections>
<sectionGroup name="catel">
<section name="ioc" type="Catel.IoC.IoCConfigurationSection, Catel.Core" />
</sectionGroup>
</configSections>
<catel>
<ioc>
<serviceLocatorConfigurations>
<serviceLocatorConfiguration>
<register interfaceType="Catel.MVVM.Services.IUIVisualizerService"
implementationType="Catel.MVVM.Services.UIVisualizerService" />
<register interfaceType="Catel.MVVM.Services.IProcessService"
implementationType="Catel.MVVM.Services.ProcessService" />
</serviceLocatorConfiguration>
<serviceLocatorConfiguration name="test"
supportDependencyInjection="false">
<register interfaceType="Catel.MVVM.Services.IUIVisualizerService"
implementationType="Catel.MVVM.Services.Test.UIVisualizerService"
registrationType="Transient"/>
<register interfaceType="Catel.MVVM.Services.IProcessService"
implementationType="Catel.MVVM.Services.Test.ProcessService" tag="test"/>
</serviceLocatorConfiguration>
</serviceLocatorConfigurations>
</ioc>
</catel>
</configuration>
To configure a service locator from a named configuration use the following code:
var serviceLocator = ServiceLocator.Default;
Configuration configuration =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var ioc = configuration.GetSection<IoCConfigurationSection>("ioc", "catel");
ioc.GetServiceLocatorConfiguration("test").Configure(serviceLocator);
You should also note the options setup if the container in order to support dependency injection and the
registration type (a.k.a instantiation style) and the tag for each registration.
Replacing the default components
By default, Catel provides very fast and functional implementations of the component interfaces. It is possible though that one needs to use a
different container than the specified ones.
Replacing default components
Creating IoC components in code
Note that when any component is replaced, it must be registered with the other instances that are already running. Catel cannot do this
automatically because it is not aware how other (customized) components interact or require registration.
To replace any component, first create a custom implementation of the specific component, for example the IServiceLocator. Then update the
factory and call UpdateDefaultComponents:
At this moment, Catel will fully replace the components (in this case the IServiceLocator and ITypeFactory), but will keep using the default
implementation of the IDependencyResolver.
Catel will automatically create the right IDependencyResolver and ITypeFactory and register them in the newly created IServiceLocator.
Logging
Starting with version 2.2, Catel uses a custom internal logging system. This way, the reference to log4net could be removed. The idea behind this
move is not to force the user to use log4net. Also, log4net seems deprecated (no new releases for a long time), and other logging systems such
as NLog seem to grow in popularity.
The new logging system only allows very basic logging. This way, the logging is kept very simple and the real logging can be implemented by the
end-developer if he/she feels the need for it.
Table of contents
Log and ILog
LogManager
Logging in code
Logging in code with additional data
Logging to the output window or console
Overriding global log level flags
Other info
Log listeners
Customizing listeners
Creating log listeners via configuration
Integration with external loggers
Anotar.Catel.Fody
LogManager
The LogManager is the class where it all comes together. This class is responsible for creating new logs for types, but also keeps track of all logs
and log listeners. To retrieve the log for a specific class, use the following code:
Logging in code
To log in code, the ILog interface implements some basic methods to log information with an option for extra data. There are however lots of
extension methods available to log exceptions, string formats and more. Below is an example of logging in code:
Then the log data will be available in the LogData of the LogEntry:
#if DEBUG
LogManager.AddDebugListener();
#endif
LogManager.IsDebugEnabled = true;
LogManager.IsDebugEnabled = null;
Log listeners
Catel provides several log listeners out of the box.
Batch log listeners
ConsoleLogListener
DebugLogListener
EventLogListener
Event Tracing for Windows (ETW)
FileLogListener
RollingInMemoryLogListener
SeqLogListener
LogManager.FlushAll();
ConsoleLogListener
The ConsoleLogListener writes messages to the console with automatic colors:
DebugLogListener
The DebugLogListener is the best debugging tool there is during development. It shows you insight in your application in the output window of
Visual Studio, even from messages generated by Catel. To use it, use this:
#if DEBUG
LogManager.AddDebugListener();
#endif
EventLogListener
The EventLogListener allow to write log data to the system event log.
This log listener is currently available only for .NET 4.0 and .NET 4.5
FileLogListener
Specifying the path
Creating a custom listener
Registering the listener
Catel also supports very lightweight listeners to allow external logging libraries to hook on. To create a listener, first create a new class that
implements the ILogListener interface. Next, register it in the LogManager using the LogManager.AddListener method.
The ILogListener has a separate method for each LogEvent, but also has a shared method that is called for each log event. For example, if a
debug message is written to the log, both the Write and Debug methods are invoked on the ILogListener.
For an example which writes to disk in batches, see the batch log event listeners
Note that Catel already contains a FileLogListener and there is no need to reproduce this class. It only acts as an example that is easy
to understand
Description
{AppData}
The application data directory based on the entry assembly. For example %AppData%\[company]\[product]\
{AppDir}
{Date}
{Time}
{ProcessId}
The process id
{AssemblyName}
{AutoLogFileName}
LogManager.AddListener(new FileLogListener("<log_file_path>"));
RollingInMemoryLogListener
Enabling the feature
Querying log messages
Customizing number of items to keep in memory
In memory logging can be very useful to be able to query the latest log messages. Catel provides this via the RollingInMemoryLogListener and Ro
llingInMemoryLogService. The RollingInMemoryLogService is a wrapper around the RollingInMemoryLogListener to provide a simple way to use
the listener.
var rollingInMemoryLogService =
ServiceLocator.Default.ResolveType<IRollingInMemoryLogService>();
SeqLogListener
Seq is a structured logs server for .NET Apps. It act like a logs repository, allow to diagnostic by query your logs using a natural syntax, react on
notifying you through email or instant messages and so on ...
<catel>
<logging>
<listeners>
<listener type="Catel.Logging.SeqLogListener"
ServerUrl="http://localhost:5341" IgnoreCatelLogging="true" IsDebugEnabled="false"
IsInfoEnabled="true" IsWarningEnabled="true" IsErrorEnabled="true" />
</listeners>
</logging>
</catel>
This log listener is currently available only for .NET 4.0 and .NET 4.5
Customizing listeners
Each listener can be customized to only receive the logs that the listener is interested in. This way, the listener does not receive events it is not
interested in. For example, to only receive errors, create a new listener and use the following customization:
<configSections>
<sectionGroup name="catel">
<section name="logging" type="Catel.Logging.LoggingConfigurationSection, Catel.Core"
/>
</sectionGroup>
</configSections>
<catel>
<logging>
<listeners>
<listener type="Catel.Logging.FileLogListener"
FilePath="{AppData}\CatelLogging.txt" IgnoreCatelLogging="true"
IsDebugEnabled="false" IsInfoEnabled="true" IsWarningEnabled="true"
IsErrorEnabled="true"/>
</listeners>
</logging>
</catel>
It is important to register the logging section as shown in the example above. Then the logging section in the bottom can contain an unlimited
number of listeners. Each listener has to provide at least the type property which contains the type and namespace of the ILogListener which
must be added:
The other properties are fully customizable and can be defined on the fly. This means that the configuration is fully customizable for every listener
that needs to be added. Below is an example to register the FileLogListener via configuration:
The ILogListener properties (IsDebugEnabled, IsInfoEnabled, IsWarningEnabled and IsErrorEnabled) are available to all listeners. All other
properties depend on the the actual listener being registered. This allows major flexibility at runtime.
Log4net
The example below provides an ILogListener for NLog, but any external logging library can be used.
LogManager.AddListener(new Log4netListener());
Configuring log4net
Note that this is just a sample configuration. Please use the log4net documentation for all options
NLog
The example below provides an ILogListener for NLog, but any external logging library can be used.
LogManager.AddListener(new NLogListener());
Anotar.Catel.Fody
Logging is a very important part of an application. It provides detailed information about the application workflow, even when an application is
already deployed to several clients. Thats the reason that logging is a first class citizen in the Catel framework.
In general, logging works by defining an ILog instance on a class:
Writing the Log definition can be boring and repetitive. Luckily Simon Cropp came up with a solution for that, namely Anotar.Catel.Fody. With the
Anotar implementation, a reference will be added to the solution. Then after compilation the assembly will be removed and all calls to the LogTo cl
ass will be replaced by actual calls to the Catel logging classes.
Using Anotar is really easy, just call the static methods on the LogTo class as you can see below:
Note that it is no longer required to define the Log field, it will be added automatically by Anotar.
Besides that it is really easy to use, another benefit is a very small performance improvement. The GetCurrentClassLogger uses reflection to
determine the current class. This is a very slight hit on performance the first time a class is used (only once, the field is static). Anotar directly
replaces the call by an implementation like this:
Additional options
Disabling method names and line numbers
By default Anotar also logs the method and line number:
If you don't want such output, add this attribute on assembly level:
[assembly: LogMinimalMessage]
[LogToDebugOnException]
public static void ExceptionalMethod()
{
throw new Exception("This will be logged automatically");
}
Messaging
MessageBase
Message mediator
Messaging via attributes
MessageBase
The MessageMediator is a very powerful class to send messages to other objects inside an application. However, it can sometimes by
cumbersome to register and create messages. Therefore the MessageBase class is a very nice convenience class to create messages and allow
easier registration.
The MessageBase provides the following additional functionality out of the box:
Send messages with data without instantiating a message
Register message handlers
Unregister message handlers
Sending messages
A user can send a message by using the following code:
DemoMessage.SendWith("hello world");
Registering to messages
A class that is interested in message can register to a message using the Register method:
DemoMessage.Register(this, OnDemoMessage);
Message mediator
Catel allows sending messages to unknown targets by implementing the mediator pattern. The mediator is assured memory leak free, and can be
used safely in any .NET environment (even ASP.NET). Below are a few usage examples of the MessageMediator class.
Registering to a message
To register a handler for a specific message type, in this case a string, use the following code:
There are two options to decorate methods with the attribute. Either with or without tag.
/// <summary>
/// Method to invoke when the command is executed.
/// </summary>
private void OnCmdExecute()
{
var dependencyResolver = this.GetDependencyResolver();
var mediator = dependencyResolver.Resolve<IMessageMediator>();
mediator.SendMessage("Test Value");
}
If a class, for example a view model, is interested in these messages, the only thing that needs to be done is to decorate a method with the
MessageRecipient attribute as shown below:
/// <summary>
/// Shows the message.
/// </summary>
/// <param name="value">The value.</param>
[MessageRecipient]
private void ShowMessage(string value)
{
var dependencyResolver = this.GetDependencyResolver();
var messageService = dependencyResolver.Resolve<IMessageService>();
messageService.Show(value);
}
/// <summary>
/// Method to invoke when the command is executed.
/// </summary>
private void OnCmdExecute()
{
var dependencyResolver = this.GetDependencyResolver();
var mediator = dependencyResolver.Resolve<IMessageMediator>();
mediator.SendMessage("Test Value", "myTag");
}
The message is now sent with the tag. The attribute has to be used as shown below:
/// <summary>
/// Shows the message.
/// </summary>
/// <param name="value">The value.</param>
[MessageRecipient(Tag = "myTag")]
private void ShowMessage(string value)
{
var dependencyResolver = this.GetDependencyResolver();
var messageService = dependencyResolver.Resolve<IMessageService>();
messageService.Show(value);
}
Multilingual
Making an application multilingual is a very common feature request nowadays. Therefore Catel provides the resources in several languages and
provides the LanguageService to give the developers full control over the translation process in their applications.
Setting up the LanguageService
Setting cultures
Registering custom language sources
Using the LanguageService
Using the LanguageService in XAML
Using the LanguageBinding in WPF and Silverlight
Using the LanguageBinding in Windows Phone
Implementing custom LanguageService (from database)
Creating a custom ILanguageSource implementation
Creating a custom DbLanguageService
Enabling the custom DbLanguageService
The LanguageService will now automatically query these sources for the translations.
Note that this implementation queries the database for each translation. It is best to read all translations into memory at once to improve
performance
To invoke an Initialize method on all types currently loaded by Catel, in batches of 2500 types per batch, use the following code:
It is really easy to tweak the number of items per batch to find the optimal performance of items per batch.
However, using this code one must be aware that if not unsubscribed, there might be a potential memory leak here. In Catel, there is a solution for
such cases that can raise change notifications using weak events called the ChangeNotificationWrapper. It allows the subscription of both the
INotifyPropertyChanged and INotifyCollectionChanged interfaces.
Note that it is not required to check whether the object implements INotifyPropertyChanged, the wrapper does it automatically
Note that it is not required to check whether the object implements INotifyCollectionChanged, the wrapper does it automatically
All subscriptions are automatically managed by the ChangeNotificationWrapper when items are added or removed from the collection.
wrapper.UnsubscribeFromAllEvents();
Weak events
Open instance delegates
Weak references
What does it support
What does it not support and what are the downsides
How to use
Instance to instance
Instance to static
Static to instance
Static to static
You have probably heard about weak events before. This documentation is not about the issue of the cause of weak events, there are lots of
articles about that. This documentation writes about the solution, which is the WeakEventListener. Shortly said, when you do this in every class
(just for the sake of explaining the problem, dont start thinking this code has no business value):
As you can see, the log is a singleton, so there is only one living instance of the Log class. It will probably live as long as the app itself. Now you
might be thinking: whats wrong with this code? Nothing, until the app starts growing and growing and your users start complaining about memory
issues.
What happens here is that you subscribe to the LogReceived event of the Log class. This subscription contains 2 things:
1. What class do I need to call (null for static, otherwise the instance of the class)
2. What method do I need to call
So, in fact now the Log class knows about the instance of the class that just subscribed to it and holds a reference to it (how else can it deliver the
event, if it doesnt know the address). Thus, the classes that subscribe to the Log and that do no unsubscribe will never be collected by the
garbage collection.
you live on that street (method), but I have not clue in which city (instance) that is. The instance can be specified later. The delegate for a regular
event handler looks like this:
The @this is nothing special, it allows us to use the this keyword so everyone knows that the target should be passed there. As you can see, it
contains 3 parameters. The first one is the target (the city), the second and third parameters are the parameters of the regular event handler.
Weak references
The weak event listener creates an open instance delegate and stores both the source and target in a WeakReference class. As soon as one of
these references are no longer valid, the class is unbound. The good side of this approach is that this weak event listener does not leak when the
event never fires.
How to use
There are 4 categories of event subscriptions, all described below.
Instance to instance
This is the situation where an instance target subscribes to an instance event. The events are unbound as soon as either the target or source are
collected.
Instance to static
This is the situation where a static target subscribes to an instance event. The events are unbound as soon as the source is collected.
Static to instance
This is the situation where an instance target subscribes to a static event. The events are unbound as soon as the target is collected.
Static to static
This is not supported because you shouldnt be using a weak event listener here. Static events with static event handlers simply cannot cause
memory leaks because both the source and the target have no instance. However, it might be possible that you subscribe to an event too many
times and the event fires too many times. But again, no memory issues here.
Reflection
Internally, Catel uses lots of reflection to implement some of its behavior. And why not make all these excellent reflection classes public?
AssemblyHelper.RegisterAssembliesFromXap(xapStream);
Catel unpacks the xap (which is basically just a zip file) and adds all the assemblies to an internal list of assemblies. This way, the
AssemblyHelper.GetLoadedAssemblies method actually returns all the loaded assemblies, also the ones in dynamically loaded xaps that are not
available by default.
or
string firstName;
PropertyHelper.TryGetValue(person, "FirstName", out firstName);
or
Scoping
Sometimes scoping is important to share an object inside a specific scope which cannot be determined upfront. A great example is the
serialization inside Catel which requires a serialization scope which can be shared over a lot of objects. Scoping in Catel is really. To create a
scope of an object with a specific tag, use the code below:
When the scope does not yet exist, it will be created and the object will be created by the TypeFactory.
Serialization
Supported serialization formats
Type
Assembly / package
Available from
version
Binary
Catel.Core
1.0.0
Json
Catel.Serialization.Json
4.2.0
Xml
Catel.Core
1.0.0
Additional information
Yaml
Catel.Serialization.Yaml
Work in progress
Introduction to serialization
Serialization modes
Serializing a model
Warming up serialization
Warming up specific types
Warming up automatically
Warming up using multiple threads
Backwards compatibility for binary serialization
Serialization modes
Depending on the target framework, several options are available as serialization modes:
Serialization
mode
WPF
Silverlight
Windows Phone
(SL)
Windows Phone
(RT)
WinRT
Xamarin.Android
Xamarin.iOS
Comments
Binary
Json
XML
Note that it is possible to implement custom serialization techniques or customize the serialization
Serializing a model
The code below shows how to save an object (which can, of course, be a complex graph of nested objects):
Looks too easy, but this really is the only thing you need to do. You can specify the serialization mode in the several available overloads of the Sa
ve method.
Loading is as easy as saving, as you can see in the following code:
Note that for a model to support the Save and Load methods, it must derive from SavableModelBase
Warming up serialization
The first time a serializer needs to serialize an object, it needs to perform some reflection to gather all the required information. This can have a
negative impact on performance for the end-user during serialization. This load cannot be prevented, but it is possible to warmup the serializer at
any time when it is convenient (for example, during startup of the application).
typeof(Settings) };
Warming up automatically
This code will warm up all types implementing the ModelBase class:
Note that warming up for all types might take a serious amount of time and might increase the memory footprint of your application
depending on the number of models
/// <summary>
/// Retrieves the actual data from the serialization info.
/// </summary>
/// <remarks>
/// This method should only be implemented if backwards compatibility should be
implemented for
/// a class that did not previously implement the ModelBase class.
/// </remarks>
protected override void GetDataFromSerializationInfo(SerializationInfo info)
{
// Check if deserialization succeeded
if (DeserializationSucceeded)
{
return;
}
// Deserialization did not succeed for any reason, so retrieve the values manually
// Luckily there is a helper class (SerializationHelper)
// that eases the deserialization of "old" style objects
FirstName = SerializationHelper.GetString(info, "FirstName",
FirstNameProperty.GetDefaultValue());
LastName = SerializationHelper.GetString(info, "LastName",
LastNameProperty.GetDefaultValue());
}
Member name
Gets serialized
_fieldValue
RegularProperty
CatelProperty
Member name
Gets serialized
_fieldValue
RegularProperty
CatelProperty
Note that private members can only be serialized in .NET and not in Silverlight, Windows Phone or the Windows Runtime
Member name
Gets serialized
_fieldValue
RegularProperty
CatelProperty
Customizing serialization
The serialization engine explained
Customizing serialization
Customizing the serialization for specific models
Creating the modifier
Registering the modifier
Customizing the serialization engines
The documentation engine has been completely rewritten for Catel 3.7. This documentation only applies to Catel 3.7 and higher.
The SerializerBase now contains all the serialization and deserialization logic. The advantage is that this logic is no longer contained by the Model
Base itself which makes the class much simpler to understand and maintain. Now the SerializerBase contains all the heavy lifting, the deriving
classes (XmlSerializer and BinarySerializer) only have to implement a few methods.
The serialization process works as shown in the diagram below:
Customizing serialization
Customizing the serialization for specific models
Catel has a default behavior for what gets serialized. It can be tweaked by including / excluding fields and properties by using the IncludeInSeriali
zation and ExcludeFromSerialization attributes. But sometimes one needs more specific customization of the serialization for a specific type. This
customization is possible via the ISerializerModifier.
[SerializerModifier(typeof(PersonSerializerModifier))]
public class Person : ModelBase
{
// .. class contents
}
Note that modifiers are inherited from base classes. When serializing, the modifiers defined on the most derived classes will be called
last. When deserializing, the modifies defined on the most derived classes will be called first.
The only thing to do now is to register this custom instance in the ServiceLocator:
ServiceLocator.Default.RegisterType<IXmlSerializer, SafeXmlSerializer>();
The following methods on the serializer classes might be of interest when customizing the serialization:
ShouldIgnoreProperty
BeforeSerialization
BeforeSerializeProperty
AfterSerializeProperty
AfterSerialization
BeforeDeserialization
BeforeDeserializeProperty
AfterDeserializeProperty
AfterDeserialization
Supported serializers
Below is a list of supported serializers. Note that the serializers are an implementation detail for Catel. You can use all the serializer features
(such as serializer modifiers) the same way on all serializers.
BinarySerializer
JsonSerializer
XmlSerializer
BinarySerializer
The binary serializer uses the .NET binary serializer formatters to serialize the objects.
JsonSerializer
Preserve references (and support circular references)
Support complex dynamic types
The JsonSerializer is implemented in a separate assembly because it uses Json.Net under the hood.
{
"$graphid":1,
"Name":"1",
"CircularModel":{
"$graphid":2,
"Name":"2",
"CircularModel":{
"$graphrefid":1
}
}
}
or
{
"$graphid": 1,
"Collection1": [1,
2,
3,
4,
5],
"$Collection1_$graphid": 2,
"Collection2": [1,
2,
3,
4,
5],
"$Collection2_$graphrefid": 2
}
To disable the support for reference preservation, use the code below:
{
"$typename":"Catel.Test.Data.IniFile",
"FileName":"MyIniFile",
"IniEntryCollection":[
{
"$typename":"Catel.Test.Data.IniEntry",
"Group":"Group 0",
"Key":"Key 0",
"Value":"Value 0",
"IniEntryType":0
},
{
"$typename":"Catel.Test.Data.IniEntry",
"Group":"Group 1",
"Key":"Key 1",
"Value":"Value 1",
"IniEntryType":1
},
{
"$typename":"Catel.Test.Data.IniEntry",
"Group":"Group 2",
"Key":"Key 2",
"Value":"Value 2",
"IniEntryType":0
}
]
}
XmlSerializer
The xml serializer uses the DataContractSerializer from .NET to serialize the object graphs.
Background information
The common solution to access the thread-sensitive resources is use the lock statement just as follow:
But some times the scenario is not quite simple, then you need to use the Monitor class in order to synchronize cross method operations. Here is
an example:
To combine the power of the simplicity of the lock statement syntax and the flexibility of the Monitor class, Catel introduces the
SynchronizationContext class, allowing you to write the code like this.
SynchronizationContext also allow you create asynchronous locking request, that could be useful in Silverlight Application where the
action of lock the main thread is not allowed.
Acquiring a lock
To acquire a lock, only a call to Acquire is required:
_synchronizationContext.Acquire();
Releasing a lock
To release a lock, only a call to Release is required:
_synchronizationContext.Release();
Validation
The ViewModelBase derives from ModelBase, thus all information here also applies to the ViewModelBase
Validation is very important for data objects. Therefore, the ModelBase supports all kinds of different validation:
Internal validation via the ValidateFields and ValidateBusinessRules methods
Validation via data annotations (attributes)
External validators using the IValidatorProvider and IValidator interfaces
The validation results are cached and only executed when a property changes (the object becomes dirty) or when the validation is forced.
Validation via validate methods
Validation via data annotations
Validation via special model validators
Validation via IValidator
Using the validation context
Getting a summary of validation results
Deferring validation
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
IValidator.BeforeValidateFields
OnValidatingFields (raises the ValidatingFields event)
IValidator.ValidateFields
ValidateFields
OnValidatedFields (raises the ValidatedFields event)
IValidator.AfterValidateFields
IValidator.BeforeValidateBusinessRules
OnValidatingBusinessRules (raises the ValidatingBusinessRules event)
IValidator.ValidateBusinessRules
ValidateBusinessRules
OnValidatedBusinessRules (raises the ValidatedBusinessRules event)
IValidator.AfterValidateBusinessRules
end if not already validated
15. OnValidated (raises the Validated event)
16. IValidator.AfterValidation
There are lots of events, and it may seem complex and confusing at first sight. However, all these events give developers the opportunity to hook
into the validation sequence at any time.
The easiest way to implement validation is to override the ValidateFields and ValidateBusinessRules methods. Below is an example of an
implementation of the ValidateFields method:
Data annotations are validation when the specific property is set. For example, when a property FirstName is set, all the data annotations on the
FirstName property are validated.
/// <summary>
/// Gets or sets the middle name.
/// </summary>
[Required]
public string MiddleName
{
get { return GetValue<string>(MiddleNameProperty); }
set { SetValue(MiddleNameProperty, value); }
}
/// <summary>
/// Register the property so it is known in the class.
/// </summary>
public readonly PropertyData MiddleNameProperty = RegisterProperty("MiddleName",
typeof(string), string.Empty);
For more information about data annotations, read the official MSDN documentation .
[ValidateModel(typeof(PersonValidator))]
public class PersonModel : ModelBase
{
public string FirstName
{
get { return GetValue<string>(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
public static readonly PropertyData FirstNameProperty = RegisterProperty("FirstName",
typeof(string), string.Empty);
public string LastName
{
get { return GetValue<string>(LastNameProperty); }
set { SetValue(LastNameProperty, value); }
}
public static readonly PropertyData LastNameProperty = RegisterProperty("LastName",
typeof(string), string.Empty);
}
The validation in Catel is extremely flexible, but sometimes it is just not enough or you are forced to use external validators. For such cases, Catel
provides the IValidatorProvider and IValidator interfaces. These allow very flexible injection or external validators into data objects and view
models of Catel.
If an IValidatorProvider instance is available, the following code can be used to allow a more generic approach. This code assumes that the
IValidatorProvider is registered in the ServiceLocator.
If the IValidatorProvider returns null (which is allowed), no custom validator will be used.
ServiceLocator.Instance.RegisterType<IValidatorProvider, MyValidatorProvider>();
The ViewModelBase will automatically retrieve the right IValidator for the view model. If no IValidatorProvider is registered in the ServiceLocator,
no validator will be set automatically. It is also possible to set the Validator property manually, but it is recommended to use an IValidatorProvider
and register it.
Sometimes detailed information about validation is required. This is possible in Catel thanks to the ValidationContext class. The ValidationContext
serves as the container for all validation results that are gathered via the available validation methods. The ValidationContext has lots of methods
that all return lists of either IFieldValidationResult or IBusinessRuleValidationResult.
The examples below are shown a starter examples, but you can gather every type of validation result by using the ValidationContext. To retrieve
the validation context of an object, use the following code:
[ValidationToViewModel(Tag = "PersonValidation")]
public IValidationSummary PersonValidationSummary { get; set; }
All validation results that have the tag PersonValidation will automatically be gathered into the PersonValidationSummary property after each
validation sequence.
Deferring validation
The opinions about validation differ from person to person. Some people think it is best practice to immediately show the errors to the users.
Others want to defer it to the moment where the user clicks the Save or OK button. Catel supports both "best practices".
Suspending validation for a batch of changes
Deferring validation in view models
SuspendValidation = true;
// change several properties here
SuspendValidation = false;
// Now force validation
Validate(true);
Validate immediately
DeferValidationUntilFirstSaveCall
false
true
ValidateModelsOnInitialization
true
false
If the DeferValidationUntilFirstSaveCall property, is used, it must be set as first property in the view model because the validation kicks
in immediately when properties change.
Catel.MVVM
The last few years, MVVM has become the number one pattern to write applications using WPF, Silverlight, and Windows Phone 7. The actual
pattern is very simple, but there are some flaws and questions lots of MVVM users have, such as:
How to show modal dialogs or message boxes inside a View-Model?
How to run processes inside a View-Model?
How to let the user select a file inside a View-Model?
In my opinion, this is where the good frameworks separate themselves from the bad ones. For example, people actually calling
MessageBox.Show inside a View-Model are using the pattern wrong. If you are one of the developers that directly call a MessageBox inside a
View-Model, ask yourself this: who is going to click the button during a unit test?
Before we actually started developing Catel, we did lots of investigations to make sure that the MVVM pattern was really useful in Line of
Business (LoB) applications and does not miss the finishing touch. Thanks to this investigation and research, we created a solid MVVM
framework which solves all the known problems of the MVVM pattern.
This part of the documentation explains all about the MVVM framework included with Catel. The MVVM framework that ships with Catel has the
following characteristics and features:
Very easy to use, a view model is created within 10 minutes
Direct pass-through of view model properties to Models
Validation mapping from model to view model and back
Solves the nested user controls problem
If you are not yet familiar with MVVM, it is advised to read a small introduction on Wikipedia.
Auditing
Behaviors & triggers
Bindings
Auditing
There are lots of lightweight MVVM frameworks out there, which work great for the basics. However, if you are writing larger enterprise
applications, notifying the UI of changed properties isn't enough. For example, did you think about Command Authentication? Or what about
sensor emulation for Windows Phone 7 (that Microsoft dont provide)?
Why auditing
Creating an auditor
Registering an auditor
Why auditing
There are many reasons why auditing should be added to an application. Most developers only add auditing to the database, but below are
several reasons to add auditing to the client as well:
Logging (what user did what on specific moments)
Gather statistics (which views (view models) are used most)
See what features of your software are being used by checking if anyone is actually invoking specific commands
Measure performance (how long does it take to update specific properties or why is a specific view-model so slow?)
With the auditing capabilities of Catel, you can create and register custom auditors that can handled changes and events of view models. This
way, you can gather a lot of statistics or any information that you want to gather about the user experience. Below is a list of events that can be
handled:
OnViewModelCreating
OnViewModelCreated
OnPropertyChanging
OnPropertyChanged
OnCommandExecuting
OnViewModelSaving
OnViewModelSaved
OnViewModelCanceling
OnViewModelCanceled
OnViewModelClosing
OnViewModelClosed
The developer has all the freedom to handle one or more methods in an auditor. Of course multiple auditors are possible as well.
Creating an auditor
Creating a new auditor is very simple. Create a new class, derive from AuditorBase and override the methods you are interested in. The class
example tracks the event to a fake analytics framework.
/// <summary>
/// Logs all commands to a custom analytics service.
/// </summary>
public class CommandAuditor : AuditorBase
{
private Analytics _analytics = new Analytics();
/// <summary>
/// Called when a command of a view model has just been executed.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="commandName">Name of the command, which is the name of the command
property.</param>
/// <param name="command">The command that has been executed.</param>
/// <param name="commandParameter">The command parameter.</param>
public override void OnCommandExecuted(IViewModel viewModel, string commandName,
ICatelCommand command, object commandParameter)
{
_analytics.TrackEvent(viewModel.GetType(), "commandName");
}
}
Registering an auditor
Registering a new auditor is extremely easy as you can see in the code below:
AuditingManager.RegisterAuditor(new CommandAuditor());
Authentication
AutoCompletionBehavior
AutoScroll
DelayBindingUpdate
DoubleClickToCommand
EventToCommand
Focus
FocusFirstControl
FocusOnKeyPress
HideUntilViewModelLoaded
KeyPressToCommand
MouseInfo
Navigate
NumericTextBox
SelectTextOnFocus
UpdateBindingOnPasswordChanged
UpdateBindingOnTextChanged
Starting with Catel 4.0, it is possible to manage interactivity classes such as behaviors from the InteractivityManager class. This allows a
developer to get notified when a behavior or trigger is loaded or unloaded.
Note that the InteractivityManager is only compatible with behaviors and triggers using one of the Catel base classes
The manager contains both events and methods to retrieve information about triggers. For example, if one is interested in all the Focus triggers,
one could do the following:
Authentication
The Authentication behavior is able to hide, collapse or disable UI elements based on the current user state. The behavior uses the registered
IAuthenticationProvider instances to determine whether the user has access to the specified UI element.
1) Creating an authentication provider:
/// <summary>
/// Example implementation of the <see cref="AuthenticationProvider"/>. This class is
not really implemented
/// like it should, because it shouldn't be this easy to set the current role.
However, for the sake of simplicity,
/// this class has a simple property with the role of the user.
/// </summary>
public class AuthenticationProvider : IAuthenticationProvider
{
/// <summary>
/// Gets or sets the role the user is currently in.
/// </summary>
/// <value>The role.</value>
public string Role { get; set; }
public bool CanCommandBeExecuted(ICatelCommand command, object commandParameter)
{
return true;
}
public bool HasAccessToUIElement(FrameworkElement element, object tag, object
authenticationTag)
{
var authenticationTagAsString = authenticationTag as string;
if (authenticationTagAsString != null)
{
if (string.Compare(authenticationTagAsString, Role, true) == 0)
{
return true;
}
}
return false;
}
}
Catel.IoC.ServiceLocator.Instance.RegisterType<IAuthenticationProvider,
AuthenticationProvider>();
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
4) Add the behavior. As you can see, it is possible to provide a custom AuthenticationTag, which is passed to the IAuthenticationProvider:
<TextBox>
<i:Interaction.Behaviors>
<catel:Authentication AuthenticationTag="Administrator" Action="Disable" />
</i:Interaction.Behaviors>
</TextBox>
5) Below are screenshots of the example applications (which can be found at http://catelexamples.codeplex.com):
Logged in as administrator:
AutoCompletionBehavior
To enable auto completion features, once can use the services and behaviors provided by Catel. There are two components required for auto
completion:
1. AutoCompletionService => takes care of the actual filtering
2. AutoCompletionBehavior => can be attached to a TextBox to support a dropdown with recommended values
The auto completion features looks like the screenshot below:
AutoCompletion service
The default implementation automatically filters the collection specified. If there is no filter yet, it will filter the top 10 occurrences from the
collection. When a filter is available, it will do the same but with the filter applied.
AutoCompletion behavior
The behavior can be used as follows:
If the PropertyName is null or whitespace, the ItemsSource will be treated as collection of strings to be filtered directly
AutoScroll
The AutoScroll behavior automatically scrolls to a specific direction when the ItemsSource of an ItemsControl changes.
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
2) Add behavior
DelayBindingUpdate
Sometimes, a binding update should be delayed for performance reasons. This is possible using the DelayBindingUpdate behavior. This behavior
modifies the binding mode to explicit and internally watches for property changes. If the bound dependency property changes, the behavior will
wait for the time to pass and then update. If the value changes again within the timeframe, the timer is reset (so you won't get "double" updates).
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
2) Use the following definition. This example will delay the update of the SelectedItem binding with 100 milliseconds:
DoubleClickToCommand
Lots of times, a developer needs to handle a double click event. However, somehow the Silverlight team thought that double click had no priority
at all. Luckily, Catel offers the solution by providing the DoubleClickToCommand trigger. This trigger allows a developer to track a double click on
any FrameworkElement and respond to that using a command.
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
2) Use the following definition. This example will invoke the Edit command of the view model when the item is double clicked):
The trigger contains an additional property AutoFixListBoxItemTemplate which is set to true by default to easily allow the addition of a double click
event to a ListBox. The item template must contain a grid as a base like shown above.
This behavior also supports a constructor that accepts an Action. This way, an anonymous delegate can be executed when the
behavior is created in code
EventToCommand
Almost every respectable MVVM framework supports the EventToCommand trigger. It is a trigger that allows a an event to be turned into a
command. This way, you never have to manually add event handlers, search for the view model in the code-behind and then call the right
command.
The usage is really simple, but requires the System.Windows.Interactivity.dll reference (ships with Catel). The example below shows how to add a
trigger for the double click of a ListBox.
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
2) Use the following definition. This example will invoke the Edit command of the view model):
If you want to use parameters (in the case of this example, get the MouseDoubleClick event args, set PassEventArgsToCommand to true:
Then, in the view model, you can even make the command "type-safe":
/// <summary>
/// Gets the Edit command.
/// </summary>
public Command<MouseEventArgs> Edit { get; private set; }
// TODO: Move code below to constructor
Edit = new Command<MouseEventArgs>(OnEditExecute, OnEditCanExecute);
// TODO: Move code above to constructor
/// <summary>
/// Method to check whether the Edit command can be executed.
/// </summary>
private bool OnEditCanExecute(MouseEventArgs e)
{
return true;
}
/// <summary>
/// Method to invoke when the Edit command is executed.
/// </summary>
private void OnEditExecute(MouseEventArgs e)
{
// TODO: Handle command logic here
}
Focus
To set the focus on a UI element, one must write code in the code-behind. With the Focus behavior, this is no longer necessary. This behavior
sets the focus only once on the first time the associated object is loaded.
Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
In Silverlight, simply calling Focus() on the associated object is not enough. Therefore, the focus is set with a timer with a default delay
of 500 milliseconds. This is customizable via the FocusDelay property
If you are using Silverlight, and you don't want the default delay of 500 milliseconds, make sure to explicitly set it to 0
If you are using Silverlight, and you don't want the default delay of 500 milliseconds, make sure to explicitly set it to 0
FocusFirstControl
The Focus behavior is very powerful, but sometimes you just need to focus the first control on a window or control. This can be done by using the
FocusFirstControl behavior instead. This behavior will focus the first control on a window or control and has only one property: FocusParentFirst.
Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
<Window ...>
<i:Interaction.Behaviors>
<catel:FocusFirstControl />
</i:Interaction.Behaviors>
</Window>
FocusOnKeyPress
Sometimes you need to handle a key press and then set the focus on an element.
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
<TextBox x:Name="textBox">
<i:Interaction.Behaviors>
<catel:FocusOnKeyPress Key="F" Modifiers="Ctrl" />
</i:Interaction.Behaviors>
</ListBox>
HideUntilViewModelLoaded
The HideUntilViewModelLoaded hides (Visibility.Collapsed) any view model container (IViewModelContainer) when it does not have a valid view
model. This is a great way to hide lazy-loaded views that should only be visible when they contain an actual view model.
Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
<local:MyUserControl ...>
<i:Interaction.Behaviors>
<catel:HideUntilViewModelLoaded />
</i:Interaction.Behaviors>
</local:MyUserControl>
KeyPressToCommand
Sometimes you need to handle a key press and convert it to a command. An excellent example is a ListBox that should respond to an Ctrl + Enter
key press.
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
MouseInfo
Silverlight is missing a lot. One of the things that is missing are decent mouse events such as IsMouseOver. To implement such behavior, the
MouseInfo behavior is created. It supports several mouse events (even in Silverlight!).
OnMouseOver
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
3) Now, it is easy to bind to the mouse information like this (textblock will become visible when the listbox is hovered):
Navigate
Note that this behavior is only available for WPF
The Hyperlink control in WPF is very powerful, but it is hard to make them work outside pages.
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
<TextBlock>
<Hyperlink NavigateUri="http://catel.codeplex.com">
<i:Interaction.Behaviors>
<catel:Navigate />
</i:Interaction.Behaviors>
<TextBlock Text="The best MVVM Framework" />
</Hyperlink>
</TextBlock>
NumericTextBox
The NumericTextBox behavior makes it easy to allow specific numeric input on a TextBox.
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
Use the properties on the behavior to customize the behavior to your needs
SelectTextOnFocus
The SelectTextOnFocus behavior makes it easy to select all text when a TextBox receives the focus.
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
UpdateBindingOnPasswordChanged
The UpdateBindingOnPasswordChanged is a very useful behavior which allows to bind the Password property of the PasswordBox Control. Use
it, it's really simple.
Usage in WPF
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
<PasswordBox>
<i:Interaction.Behaviors>
<catel:UpdateBindingOnPasswordChanged Password="{Binding Password,
Mode=TwoWay}" />
</i:Interaction.Behaviors>
</PasswordBox>
Usage in Silverlight
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
UpdateBindingOnTextChanged
The UpdateBindingOnTextChange is a very useful behavior which allows to delay a binding update on the TextChanged event of a TextBox. This
way, it is possible to implement search boxes that only start a search after a specific time when no new key presses have occurred. For example,
when a user types a new search string, and the user doesn't enter a new key for 500 ms, the binding is updated.
1) Add the following XML namespaces:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactiv
ity"
xmlns:catel="http://catel.codeplex.com"
2) Use the following definition. This example will update the binding after 500 ms where normally it would only occur when the user tabs out of the
TextBox:
Bindings
By default, all XAML technologies support binding out of the box. However other platforms (such as Xamarin.Android and Xamarin.iOS) don't.
Catel provides a binding system for these platforms, which is described in this topic.
Note that although the binding system in Catel is very powerful, it will never be as flexible / powerful as the native XAML binding
system. We do try to support as many features as possible though.
For examples, check out the following pages:
Property bindings
Command bindings
As the image shows, each view will have their own BindingContext. A BindingContext contains all the bindings currently available in the view and
allows adding / removing bindings dynamically when required. As soon as a major change occurs (such as a new view model), a new BindingCon
text will be created and the old one will be cleaned up. The views in Catel will automatically take care of the BindingContext initialization and
lifetime management.
Each Binding is a mapping from source to target. It also allows the specification of a converter like available in the XAML platforms. Each Binding
also contains several BindingParty objects. The default value for BindingMode is BindingMode.TwoWay.
A BindingParty is an object that will take care of watching the source or target of the binding and inform the binding when a value has been
changed. The binding parties are considered equal and contain the same logic for both the source and target of the binding.
All bindings must be initialized in the AddBindings method that is available on all views provided by Catel.
Property bindings
Property bindings are very important in the MVVM pattern. The binding system in Catel will automatically map properties when the binding system
is used.
Binding properties one way
Android
iOS
Binding properties two way
Android
iOS
Binding properties with a converter
Android
iOS
Android
iOS
iOS not yet documented
Android
Note that you need to use the AddTargetEvent to allow two way binding in Android
iOS
iOS not yet documented
Android
iOS
iOS not yet documented
Command bindings
Catel will automatically hook up the CanExecute of the command to the Enabled property of the element it is bound to. A command binding is
always bound to an event of a specific element as shown below.
Android
iOS
iOS not yet documented
There are some people who dont like the ICommand implementations. For example, Caliburn (Micro) uses convention and does not require the
creation of a command. There are a few downsides for that:
It requires you to make sure the name of the control is the same as the method;
It is not clear that it is actually a command if you are not fully familiar with the conventions;
The methods need to be public (otherwise, how are you going to invoke the commands during unit tests?), which make them freely
available (and thats not something we like);
You will always have to invoke CanExecute yourself again in Execute, because you have no guarantee that the source of Execute is
actually the convention mapping;
There is no way to manually refresh the CanExecute state on the bound controls.
For more information, see:
Commands
CommandManager and command containers (Application-wide commands)
Asynchronous commands
Commands authentication
Hooking a command to validation automatically
Commands
Commands are supported in Catel. The base class for commands is Command.
Code snippets
vmcommand - declare a command on a view model
vmcommandwithcanexecute - declare a command with support for CanExecute on a view model
Explanation
To implement commands, and still be able to unit test the view models, a separate command is introduced. This command allows a developer to
implement a command that can be invoked both via code (unit testing) and UI.
There is one real Command class: Command<TCanExecuteParameter, TExecuteParameter>
The TCanExecuteParameter is the parameter that is passed to the CanExecute of the command, ands saves the developer from casting the
object (as in the interface ICommand to a typed object). The same goes for TExecuteParameter which makes the Execute of the command typed.
There are also several wrappers available in case object is used as type parameter:
Command<TExecuteParameter> (wraps Command<TCanExecuteParameter, TExecuteParameter> with object for TCanExecuteParamet
er)
Command (wraps Command<TExecuteParameter> with object for TExecuteParameter)
Examples
Code:
CommandManager
There is no generic way to specify application-wide commands in WPF and Silverlight. To overcome this issue, Catel introduces the CommandMa
nager. This manager allows to create commands which are hosted by the CommandManager. The commands on the command manager can be
created with input gestures (on both WPF and Silverlight). Once a view model wants to hook into a specific command, it only has to register the
view model command with the application-wide command.
Note that application-wide commands by default are only available on the main window of an application. To support this on other
windows, add the following code in the constructor of a window:
It is recommended to put all the command creation in a single place so they are easily manageable.
<Ribbon catel:StackGrid.ColumnSpan="4">
<RibbonTab Header="Home" KeyTip="H" >
<RibbonGroup Header="Example commands">
<RibbonButton Command="{catel:CommandManagerBinding Refresh}"
LargeImageSource="..\Resources\Images\Refresh.png"
Label="Refresh" KeyTip="F5" />
</RibbonGroup>
</RibbonTab>
</Ribbon>
As the code shows, the CommandManagerBinding extension automatically resolves the Refresh command from the CommandManager.
Command containers
When implementing a ribbon or any menu structure inside an application can result in a very complex view model containing all the commands.
Catel solves this issue by implementing so-called command containers. These are containers that have only 1 purpose: contain a command so
the logic can easily be viewed / edited and the commands will be available during the whole lifetime of the app. Internally command containers
use the ICommandManager to register commands, so the ICommandManager is still responsible for the commands.
As you can see the implementation is very clean and won't pollute any other view models.
Command definitions
To make it very easy to register new commands, Catel uses naming conventions and extension methods. The name of the command (for
example, About must be a constant on the command definitions class). If the command definition also contains a <CommandName>InputGesture,
in this case AboutInputGesture, it will use that input gesture as a default to register the command with.
It is recommended to keep a well formed structure for your command definitions to keep them manageable, even in very large
applications
Once you have the command container and the command definition (command name and the input gesture), it is time to register the command
container:
This will keep the command registration very readable and maintainable when using a lot of commands:
"Open");
"Save");
"SaveAs");
"Refresh");
commandManager.CreateCommandWithGesture(typeof(AppCommands.Settings),
"ToggleTooltips");
commandManager.CreateCommandWithGesture(typeof(AppCommands.Settings),
"ToggleQuickFilters");
commandManager.CreateCommandWithGesture(typeof(ExtensibilityCommands.Application),
"Extensions");
commandManager.CreateCommandWithGesture(typeof(ExtensibilityCommands.Application),
"ExtensionsSettings");
Asynchronous commands
Commands in MVVM are a very precious good. Actually, MVVM can't exist without them because they allow a developer to bind to a method
(that's actually all an ICommand implementation is). However, sometimes it is required to create asynchronous commands. Starting with Catel
3.1, the AsynchronousCommand is introduced.
With the AsynchronousCommand, it is possible to create a command that executes a method in the background without blocking the UI thread. It
is possible to report progress to the UI thread.
Want to use async methods? Use the TaskCommand
The second parameter is the CanExecute delegate, and this example does not allow multiple executions of the same command at the same time.
2. Make sure the execute action checks the ShouldCancel property like in the example below:
Note that this example looks stupid and you should not use Thread.Sleep, but this is just for the sake of simplicity and the example
Reporting progress
During the execution of a command, it is possible to report progress back to the main thread. This can done by using the ReportProgress method.
All code inside the ReportProgress will be executed in the UI thread:
Commands authentication
One of the questions an MVVM developer faces is how to control the executation state of a command by role or user authentication method. Catel
offers an out-of-the-box solution for this problem to check the CanExecute state of the commands in the UI.
It is very important that this way of disabling commands is only used to easy the development of consistent user interfaces. It cannot
replace the actual check whether a user can or cannot modify data. The actual and final responsibility still lays at the business layer.
IAuthenticationProvider
The IAuthenticationProvider is a provider that needs to be implemented per application and must be registered in the IoC container. Below is the
interface definition:
/// <summary>
/// Interface to allow an authentication mechanism to control the CanExecute state of
a command.
/// </summary>
public interface IAuthenticationProvider
{
/// <summary>
/// Determines whether the specified <paramref name="command"/> can be executed.
The class implementing this interface
/// can use any required method to check the command.
/// <para />
/// It is recommended to use the <see cref="ICatelCommand.Tag"/> property to
identify a command.
/// </summary>
/// <param name="command">The command that is requested.</param>
/// <param name="commandParameter">The command parameter.</param>
/// <returns>
///
<c>true</c> if this instance [can command be executed] the specified
command; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// The <c>CanExecute</c> state of a command is queried a lot. The command itself
does not cache any results because
/// it is not aware of role or identity changes. If caching is required, this must
be implemented in the class implementing
/// the <see cref="ICommandAuthenticationProvider"/> interface.
/// </remarks>
bool CanCommandBeExecuted(ICatelCommand command, object commandParameter);
}
To register a custom implementation of the command authentication provider, use the code below:
Catel.IoC.ServiceLocator.Instance.RegisterType<IAuthenticationProvider,
RoleAuthenticationProvider>();
The code above registers a custom made command authentication provider that checks whether a specific role can execute the command.
Catel checks whether an IAuthenticationProvider is registered. If not, the way commands are handled is not affected in any way. If there
is an IAuthenticationProvider available, the CanExecute state is checked, even when there is no custom CanExecute implemented.
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results to
this list.</param>
protected override void
ValidateFields(System.Collections.Generic.List<IFieldValidationResult>
validationResults)
{
if (string.IsNullOrEmpty(FirstName))
{
validationResults.Add(FieldValidationResult.CreateErrorWithTag(FirstNameProperty,
"First name cannot be empty", "PersonValidation"));
}
if (string.IsNullOrEmpty(LastName))
{
validationResults.Add(FieldValidationResult.CreateErrorWithTag(LastNameProperty, "Last
name cannot be empty", "PersonValidation"));
}
}
2. Add a property to the view model containing the validation summary using the ValidationToViewModel attribute.
[ValidationToViewModel(Tag = "PersonValidation")]
public IValidationSummary PersonValidationSummary { get; set; }
/// <summary>
/// Gets the Save command.
/// </summary>
public Command Save { get; private set; }
/// <summary>
/// Method to invoke when the Save command is executed.
/// </summary>
private void OnSaveExecute()
{
// TODO: Handle command logic here
}
4. Create the command that automatically uses the validation summary using the CommandHelper class:
With this example, the Save command on the view model can only be executed when there are no errors with the PersonValidation tag.
Converters
In MVVM, there will be some point where you will need to use converters. Most of these converters are used in any project, so we have decided
to add them to Catel. Below is a list of converters and a short description what they are used for.
Note that the behavior of most converters can be inverted by using the ConverterParameter
Linking converters
Available converters
Linking converters
It is possible to link converters. To link converters, simply set the Link property in xaml:
Available converters
Name
Description
AreEqualMultiValueConverter
BooleanToCollapsingVisibilityConverter
BooleanToHidingVisibilityConverter
BooleanToGrayscaleConverter
Converts a boolean to a grayscale saturation value. If the input is false, this converter will return 0,
otherwise 1.
BooleanToOppositeBooleanConverter
BooleanToTextConverter
Converts a boolean value to text, for example "yes" and "no", or "x" and " ".
BooleanToCollapsingVisibilityConverter
Convert from bool to Visibility and back. True returns Visibility.Visible, False returns Visibility.Collapsed.
BooleanToHidingVisibilityConverter
Convert from bool to Visibility and back. True returns Visibility.Visible, False returns Visibility.Hidden.
ColorToBrushConverter
ContainsItemsConverter
Convert the count of an ICollection or IEnumerable to true or false, depending on whether the instance
contains items.
For an instance which implements ICollection, check Count > 0
For an instance which implements IEnumerable, if the instance can be Enumerated.
CountCollapsedConverter
Converts the 'count' of ICollection, string, long, int or short to Visibility.Visible or Visibility.Collapsed
Visible means: ICollection.Count > 0, String.Length > 0 or long, int, short > 0.
CountHiddenConverter
Converts the 'count' of ICollection, string, long, int or short to Visibility.Visible or Visibility.Hidden
Visible means: ICollection.Count > 0, String.Length > 0 or long, int, short > 0.
EmptyStringToCollapsingVisibilityConverter
EmptyStringToHidingVisibilityConverter
EnumToCollapsingVisibilityConverter
Converts an enum to Visibility. The allowed values must be defined inside the ConverterParameter as
shown below (element will be visible when MyEnumValue is Enum1 or Enum3):
If the ConverterParameter starts with !, the element will not be visible for the specified enum
values
EnumToHidingVisibilityConverter
Converts an enum to Visibility. The allowed values must be defined inside the ConverterParameter as
shown below (element will be visible when MyEnumValue is Enum1 or Enum3):
If the ConverterParameter starts with !, the element will not be visible for the specified enum
values
FormattingConverter
Formats the value using the format string provided in the ConverterParameter
IsSelectedConverter
IsSelectedValueConverter
GetFirstValidationErrorConverter
Converts a collection containing ValidationError objects to return the first error or an empty string in case
there are no errors.
IsSelectedConverter
Converts a selected value to either true or false. Useful whena mutually exclusive selection must be
made.
IntToStringConverter
MethodToValueConverter
Converts the result of a method to a value. This makes it possible to bind to a method. See Source
MultiplyConverter
NullableValueConverter
PlatformToBooleanConverter
Converts a value of KnownPlatforms to a boolean based on the current platform. This makes it possible
to enable / disable functionality in shared projects based on the target platform.
ReferenceToBooleanConverter
ReferenceToCollapsingVisibilityConverter
ReferenceToHidingVisibilityConverter
ShortDateFormattingConverter
StringToIntConverter
ViewModelToViewConverter
Converts a view model to a view. Great way to locate a view based on a view model inside xaml.
Designers
Lots of developers are using designers such as the built-in designer in Visual Studio 2010 or Expression Blend to design their xaml based
applications. Although you should use designers with lots of care, we strive to fully support all designers.
This part of the documentation shows the several design-time features that Catel provides.
WPF
To create design-time support for a data window, use the following steps:
1. Create a design time view model. Normally, this can easily be achieved by deriving a new class from the actual view-model and inject the
model. Below is an example of a design time version of a person view model:
/// <summary>
/// Design time version of the <see cref="PersonViewModel"/>.
/// </summary>
public class DesignPersonViewModel : PersonViewModel
{
/// <summary>
/// Initializes a new instance of the <see cref="DesignPersonViewModel"/> class.
/// </summary>
public DesignPersonViewModel()
: base(new Person { FirstName = "Geert", MiddleName = "van", LastName =
"Horrik", Gender = Gender.Male })
{
}
}
d:DataContext="{d:DesignInstance ViewModels:DesignPersonViewModel}"
If you want it to actually show demo data (instead of allowing to configure bindings), use IsDesignTimeCreatable:
d:DataContext="{d:DesignInstance ViewModels:DesignPersonViewModel,
IsDesignTimeCreatable=True}"
<catel:DataWindow x:Class="Catel.Examples.PersonApplication.UI.Windows.PersonWindow"
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"
xmlns:catel="http://catel.codeplex.com"
xmlns:ViewModels="clr-namespace:Catel.Examples.PersonApplication.ViewModels"
xmlns:Converters="clr-namespace:Catel.Examples.PersonApplication.Data.Converters"
xmlns:Models="clr-namespace:Catel.Examples.Models;assembly=Catel.Examples.Models"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance ViewModels:DesignPersonViewModel,
IsDesignTimeCreatable=True}">
1. Create a design time view model. Normally, this can easily be achieved by deriving a new class from the actual view-model and inject the
model. Below is an example of a design time version of a person view model:
/// <summary>
/// Design time version of the <see cref="PersonViewModel"/>.
/// </summary>
public class DesignPersonViewModel : PersonViewModel
{
/// <summary>
/// Initializes a new instance of the <see cref="DesignPersonViewModel"/> class.
/// </summary>
public DesignPersonViewModel()
: base(new Person { FirstName = "Geert", MiddleName = "van", LastName =
"Horrik", Gender = Gender.Male })
{
}
}
d:DataContext="{d:DesignInstance ViewModels:DesignPersonViewModel}"
If you want it to actually show demo data (instead of allowing to configure bindings), use IsDesignTimeCreatable:
d:DataContext="{d:DesignInstance ViewModels:DesignPersonViewModel,
IsDesignTimeCreatable=True}"
<catel:DataWindow x:Class="Catel.Examples.Silverlight.UI.Windows.PersonWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com"
xmlns:ViewModels="clr-namespace:Catel.Examples.PersonApplication.ViewModels"
xmlns:Converters="clr-namespace:Catel.Examples.Silverlight.Data.Converters"
xmlns:ExampleWindows="clr-namespace:Catel.Examples.Silverlight.Views"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance ViewModels:DesignPersonViewModel,
IsDesignTimeCreatable=True}">
[assembly: DesignTimeCode(typeof(WpfApplication.Catel.DesignTimeLanguageService))]
[assembly: DesignTimeCode(typeof(WpfApplication.Catel.DesignTimeServiceLocator))]
When the attribute is found by Catel, it will automatically construct the types specified in the attribute. This will allow the constructor to execute
any code during design time.
/// <summary>
/// The IStartUpInfoProvider interface.
/// </summary>
public interface IStartUpInfoProvider
{
#if SILVERLIGHT
/// <summary>
/// Gets the silverlight application initialization parameters.
/// </summary>
IDictionary<string, string> InitParms { get; }
#endif
#if NET
/// <summary>
/// Gets the application command line argument.
/// </summary>
string[] Arguments { get; }
#endif
}
We are evaluating the way to automatically map the command line array into a meaning full strong typed options and switches map or
dictionary to direct access to command line options
Process.GetCurrentProcess().StartInfo.Arguments
or
Application.Current.Host.InitParams
Such thing are actually unmockable. But now, thanks to Catel, you can do it registering fakes or mock instances into the service locator. Just like
this:
....
ServiceLocator.Default.RegisterInstance<IStartUpInfoProvider>(startUpInfoProviderMock.
Object);
ViewModelLocator
Starting with Catel 3.0, there are several ways to hook up a view model to the view. When a view is constructed, an MVVM behavior is added to
the view. Thanks to these MVVM behaviors, it is possible to use exactly the same logic on 3rd party controls.
Note that the while using the conventions, magic words such as "View", "Control", "UserControl", "Window" and "Page" will be stripped
from the view name while locating the view model type
Catel.Examples.Views.MyView
Catel.Examples.ViewModels.MyViewModel
However, it is possible to add or remove new naming conventions to support your own naming convention. For example, to add a new naming
convention for a different assembly, use this code:
ServiceLocator.Default.Register<IViewModelLocator, MyViewModelLocator>();
ViewLocator
The IViewLocator class is responsible for resolving the right views for a view model. Before Catel 3.0, the IUIVisualizerService was responsible for
resolving the view, but this responsibility is now taken over by the IViewLocator. The UIVisualizerService internally uses the IViewLocator to
resolve the views.
Catel.Examples.ViewModels.MyViewModel
Catel.Examples.Views.MyView
[UP].Pages.[VM]
[UP].Pages.[VM]Page
[UP].Windows.[VM]
[UP].Windows.[VM]Window
[AS].Views.[VM]
[AS].Views.[VM]View
[AS].Views.[VM]Control
[AS].Views.[VM]Page
[AS].Views.[VM]Window
[AS].Views.[VM]Activity
[AS].Views.[VM]Fragment
[AS].Controls.[VM]
[AS].Controls.[VM]Control
[AS].Pages.[VM]
[AS].Pages.[VM]Page
[AS].Windows.[VM]
[AS].Windows.[VM]Window
[AS].Activities.[VM]
[AS].Activities.[VM]Activity
[AS].Fragments.[VM]
[AS].Fragments.[VM]Fragment
[AS].UI.Views.[VM]
[AS].UI.Views.[VM]View
[AS].UI.Views.[VM]Control
[AS].UI.Views.[VM]Page
[AS].UI.Views.[VM]Window
[AS].UI.Views.[VM]Activity
[AS].UI.Views.[VM]Fragment
[AS].UI.Controls.[VM]
[AS].UI.Controls.[VM]Control
[AS].UI.Pages.[VM]
[AS].UI.Pages.[VM]Page
[AS].UI.Windows.[VM]
[AS].UI.Windows.[VM]Window
[AS].UI.Activities.[VM]
[AS].UI.Activities.[VM]Activity
[AS].UI.Activities.[VM]Fragment
[CURRENT].[VM]View
[CURRENT].[VM]Control
[CURRENT].[VM]Page
[CURRENT].[VM]Window
[CURRENT].[VM]Activity
[CURRENT].[VM]Fragment
However, it is possible to add or remove new naming conventions to support your own naming convention. For example, to add a new naming
convention for a different assembly, use this code:
Sometimes, a class doesn't follow a naming convention (for whatever reason possible). In such a case, it is possible to register a mapping
manually using the following code:
ServiceLocator.Default.RegisterType<IViewLocator, MyViewLocator>();
UrlLocator
The IUrlLocator class is responsible for resolving the right urls for the xaml views for a view model in navigation based applications. Before Catel
3.0, the INavigationService was responsible for resolving the url, but this responsibility is now taken over by the IUrlLocator. The NavigationServic
e internally uses the IUrlLocator to resolve the views.
Catel.Examples.ViewModels.MyViewModel
/Views/MyPage.xaml
Note that the UrlLocator checks whether the resource actually exists. If the resource does not exists, it will not be able to resolve a view
/Views/[VM]Page.xaml
/Views/[VM]Window.xaml
/Controls/[VM].xaml
/Controls/[VM]Control.xaml
/Pages/[VM].xaml
/Pages/[VM]Page.xaml
/Windows/[VM].xaml
/Windows/[VM]Window.xaml
/UI.Views/[VM].xaml
/UI.Views/[VM]View.xaml
/UI.Views/[VM]Control.xaml
/UI.Views/[VM]Page.xaml
/UI.Views/[VM]Window.xaml
/UI.Controls/[VM].xaml
/UI.Controls/[VM]Control.xaml
/UI.Pages/[VM].xaml
/UI.Pages/[VM]Page.xaml
/UI.Windows/[VM].xaml
/UI.Windows/[VM]Window.xaml
/[VM].xaml
/[VM]Control.xaml
/[VM]Page.xaml
/[VM]Window.xaml
However, it is possible to add or remove new naming conventions to support your own naming convention. For example, to add a new naming
convention for a different assembly, use this code:
ServiceLocator.Default.Register<IUrlLocator, MyUrlLocator>();
Naming conventions
Some services in Catel support naming conventions. For example, the IViewLocator and IViewModelLocator allow naming conventions to prevent
a user from having to register all views and view models. Internally, the naming conventions are resolved using the NamingConvention helper
class. This part of the documentation explains the possible constants in naming conventions.
[AS] constant
The [AS] constant will be replaced by the assembly name. For example, the following naming convention:
[AS].Views
Catel.Examples.Views
[VM] constant
The [VM] constant will be replaced by the name of the view model without the ViewModel postfix. For example, the following naming convention:
[AS].Views.[VM]View
Catel.Examples.Views.MyView
[VW] constant
The [VW] constant will be replaced by the name of the view without the View, Control, Page or Window postfixes. For example, the following
naming convention:
[AS].ViewModels.[VW]ViewModel
Catel.Examples.ViewModels.MyViewModel
[UP] constant
Sometimes it is not possible to use the [AS] constant because the assembly name is not used in the namespace. For example, for an application
called PersonApplication where the client assembly is PersonApplication.Client, the root namespace will still be PersonApplication. Therefore, it is
recommend to use the [UP] constant for this situation.
The [UP] constant will move the namespaces up by one step. It automatically detects the right separator (\ (backslash), / (slash), . (dot) and |
(pipe) are supported).
The following naming convention:
[UP].Views.[VM]View
Catel.Examples.Views.MyView
[CURRENT] constant
Some people prefer to put classes into the same namespace (such as views and view models).
The [CURRENT] constant will use the same namespace.
The following naming convention:
[CURRENT].[VM]View
Catel.Examples.MyView
Services
Catel offers lots of out-of-the-box services and implementations. Services can be used to separate the logic of external functionality in a service.
The advantages of this technique are:
Separation of Concerns
Unit testing (mocking interfaces is easy)
All services provided by Catel have both a unit test version and a real (or production version). For example, the IMessageService has a test
implementation that does not show a message box, but checks the result only.
AccelerometerService
CameraService
CompassService
GyroscopeService
LocationService
MessageService
NavigationService
OpenFileService
PleaseWaitService
ProcessService
SaveFileService
SchedulerService
SelectDirectoryService
SplashScreenService
UIVisualizerService
VibrateService
ViewExportService
AccelerometerService
The IAccelerometerService allows a developer to access the accelerometer of a Windows Phone device.
Platform info
Check if the sensor is supported by the device
Starting the service
Stopping the service
It is important that the service must be started and stopped to retrieve values
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
CameraService
The ICameraService allows a developer to use the PhotoCamera class in an MVVM manner.
Platform info
Starting and stopping the service
Capturing images
Showing a video of camera in a view
Instantiating the test implementation
Customizing camera settings for testing
It is important that the service must be started and stopped to retrieve values
There is also an article available about this service: WP7 Mango and Unit Testing the Camera
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
To stop the service, use the code below: (note: the Close method on a view model is feature of Catel):
Capturing images
To capture images, several things must be done. The first action to accomplish is to subscribe to the ICameraService.CaptureImageAvailable eve
nt. The next step is to invoke the CaptureImage method like shown below:
CameraService.CaptureImage();
The last part is very important. You will need to read the image stream from the CaptureImageAvailable event:
/// <summary>
/// Gets or sets the current photo.
/// </summary>
public BitmapImage CurrentPhoto
{
get { return GetValue<BitmapImage>(CurrentPhotoProperty); }
set { SetValue(CurrentPhotoProperty, value); }
}
/// <summary>
/// Register the CurrentPhoto property so it is known in the class.
/// </summary>
public static readonly PropertyData CurrentPhotoProperty =
RegisterProperty("CurrentPhoto", typeof(BitmapImage));
This property definition is a Catel property, but if you prefer using a different MVVM framework or your own property definition style, you are free
to do that as well.
In the view, use the Image control to show the current photo:
Last but not least, we need to update the CurrentPhoto property when a new thumbnail is available.
The animation that will be applied is the image scrolling to the right pixel by pixel.
To instantiate the test service, add an image to the Windows Phone project and set its build action to Resource. Then instantiate the service like
in the code below:
By default, the ICameraService will generate a new thumbnail image every 50 milliseconds. It is possible to customize this with a constructor
overload.
It is also possible to change the thumbnail and final resolution of the images:
CompassService
The ICompassService allows a developer to access the compass of a Windows Phone device.
Platform info
Check if the sensor is supported by the device
Starting the service
Stopping the service
Note that the Compass service is available as separate assembly to make sure Catel.MVVM does also work on older devices without a
Compass
It is important that the service must be started and stopped to retrieve values
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
GyroscopeService
The IGyroscopeService allows a developer to access the gyroscope of a Windows Phone device.
Platform info
Check if the sensor is supported by the device
Starting the service
Stopping the service
Note that the Gyroscope service is available as separate assembly to make sure Catel.MVVM does also work on older devices without
a Gyroscope
It is important that the service must be started and stopped to retrieve values
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
LocationService
The ILocationService allows a developer to use GPS devices inside a view model.
Platform info
Starting the service
Stopping the service
Emulating GPS without device
It is important that the service must be started and stopped to retrieve values
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
The service will raise the LocationChanged event when a new location becomes available.
It is also possible to enqueue lots of coordinates with a time span and emulate a path.
MessageService
The IMessageService allows a developer to show message boxes from a view model.
Platform info
Screenshot
Showing a message
Showing an error
Requesting confirmation
Asynchronous confirmation
Platform info
Framework
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Screenshot
Supported
Showing a message
To show a message from a view model, use the following code:
Showing an error
Showing a warning or error is very easy. Use the following code:
Requesting confirmation
It is also possible to request confirmation from the user. The number of possibilities depends on WPF, Silverlight or Windows Phone is used (for
example, not all platforms support YesNo).
The following code must be used to request confirmation:
Asynchronous confirmation
Sometimes you don't want to use regular message boxes, and in Silverlight this means that your call has to be asynchronous. The
implementation is very simple:
There are two possible callbacks, one with a result of type Func<MessageResult> or one without a result of type Action.
NavigationService
The INavigationService allows a developer to navigate to other pages inside an application using view models only.
All pages will have to be registered manually or following the right naming convention.
Platform info
Closing an application
Preventing an application to be closed
Navigating to a new view
Navigating with parameters
Navigating back and forward
Navigating to a custom Uri
Registering custom views
Using naming conventions to find pages
For WPF and Silverlight, the pages must inherit from Page. For WP7, the pages must inherit from PhoneApplicationPage. In WPF, the
parameters are of type Dictionary<string, object>, in Silverlight and WP7, the arguments are of type Dictionary<string, string>.
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Closing an application
It is possible to close an application using the following code:
To read the navigation parameters in the receiving view model, use the OnNavigationCompleted method.
OpenFileService
The IOpenFileService allows a developer to let the user choose a file from inside a view model.
Platform info
Opening a file
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Opening a file
To open a file, it is required to set the right properties of the service and then make a call to the DetermineFile method:
PleaseWaitService
The IPleaseWaitService allows a developer to show a please wait message (a.k.a. busy indicator) from a view model.
Platform info
Screenshot
Showing
Hiding
Showing and automatically hide
Changing the status
Showing a determinate please wait window
Push/Pop
Platform info
Framework
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Supported
Screenshot
This is the WPF service screenshot, the exact look differs per framework
Showing
using Catel.IoC;
Hiding
var dependencyResolver = this.GetDependencyResolver();
var pleaseWaitService = dependencyResolver.Resolve<IPleaseWaitService>();
pleaseWaitService.Hide();
using Catel.IoC;
The determinate version can be hidden via a call to Hide or when the currentItem argument is larger than the number of totalItems.
Push/Pop
Sometimes, multiple view models or multiple actions use the service. It's not possible to hide the window when the first action is completed,
because the user will still have to wait for the other actions to complete (without a please wait window). To implement this correctly, it is possible
to use the Push and Pop methods.
The Push method shows the window if it is not already visible and then increases an internal counter. At the start of each (asynchronous) action,
the developer can call the Push method. When the action is completed, the developer calls Pop which will internally decrease the counter. If the
counter hits zero (0), the window is automatically hidden.
It is possible to hide the window, even when the internal counter is not yet zero. A call to Hide will reset the counter to zero and thus hide the
window.
ProcessService
The IProcessService allows a developer to run processes from inside a view model.
Platform info
Starting a process with arguments
Starting a process with arguments and completed callback
Platform info
Framework
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Supported
SaveFileService
The ISaveFileService allows a developer to let the user choose a file from inside a view model.
Platform info
Choosing a file
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Choosing a file
To select a file to save, it is required to set the right properties of the service and then make a call to the DetermineFile method:
SchedulerService
The ISchedulerService allows a developer to schedule an action in the relative or absolute future. The SchedulerService will use the DispatcherTi
mer to invoke the action.
Platform info
Scheduling an action in the relative future
Scheduling an action in the absolute future
Note that the SchedulerService does not provide any persistence of actions and schedules. When the application is closed, all
schedules are lost because they are kept in memory.
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
SelectDirectoryService
The ISelectDirectoryService allows a developer to let the user choose a directory from inside a view model.
Platform info
Selecting a directory
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Selecting a directory
To select a directory, it is required to set the right properties of the service and then make a call to the DetermineDirectory method:
SplashScreenService
The ISplashScreenService allows a developer execute a batch of tasks en-queued and show the progress through a registered IPleaseWaitServi
ce or IUIVisualizerService type, from the main entry point of the application (typically from the bootstrapper) or a view model.
Platform info
Screenshot
Enqueuing tasks into the SplashScreenService batch
Committing the batch
Committing the batch asynchronously with callback
Smooth progress notification of an executing action task
Platform info
Framework
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
Supported
iOS
Test/emulation service
Screenshot
ServiceLocator.Default.RegisterTypeIfNotYetRegistered<ISplashScreenService,
SplashScreenService>();
/*...*/
var splashScreenService = ServiceLocator.Default.ResolveType<ISplashScreenService>();
splashScreenService.Commit();
To execute the batch of en-queued tasks, and show the progress through the IUIVisualizerService, use the following code:
splashScreenService.Commit<MySplashScreenViewModel>();
or:
splashScreenService.Commit(typeof(MySplashScreenViewModel));
splashScreenService.CommitAsync(OnBatchCompleted);
To execute the batch of en-queued task asynchronously, and show the progress through the IUIVisualizerService, use the following code:
splashScreenService.CommitAsync<MySplashScreenViewModel>(OnBatchCompleted);
or:
splashScreenService.CommitAsync(OnBatchCompleted, typeof(MySplashScreenViewModel));
After committing the batch is cleared, so to execute it again you should en-queue the tasks again
UIVisualizerService
The IUIVisualizerService allows a developer to show (modal) windows or dialogs without actually referencing a specific view. Internally, the UIVisu
alizerService uses the ViewLocator to resolve views.
Platform info
Screenshot
Showing a non-modal window
Showing a modal window
Showing a window with callback
Registering a window
Using naming conventions to find windows
Platform info
Framework
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.0
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Screenshot
Supported
Registering a window
To register a custom window which is not automatically detected via reflection, it is required to use the Register method:
VibrateService
The IVibrateService allows a developer to start and stop vibration of the device via a service.
Platform info
Starting vibration
Stopping the vibration earlier than initially planned
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Starting vibration
To start the vibration, use the following code (will vibrate for 250 ms). Note that the time span must be between 0 and 5 seconds.
ViewExportService
The IViewExportService allows a developer to export a specific view that belongs to a view model to the clipboard, a file or a printer.
Platform info
Exporting a view
Supported export methods
Platform info
Framework
Supported
WPF
Silverlight 5
Windows Phone 8.0
Windows Phone 8.1
Windows RT 8.0
Windows RT 8.1
Android
iOS
Test/emulation service
Exporting a view
To export a view, use the following code:
WPF
Clipboard
File
Print
View models
Silverlight
The view model is a very important part in the MVVM pattern. The view model is responsible for the actual logic that ensures separation of
concerns, but also allows unit testing on the view logic (which is implemented in the view model) without actually instantiating the views.
Like almost every other MVVM framework, the base class for all View-Models is ViewModelBase. This base class is derived from the ModelBase
class explained earlier in this article, which gives the following advantages:
Dependency property a-like property registration;
Automatic change notification;
Support for field and business errors.
Because the class derives from ModelBase, you can simply add field and business errors that are automatically being reflected to the UI. Writing
View-Models has never been so easy!
Creating a basic view model
Creating a view model that watches over other view models
Creating a view model with a model
Creating a view model with a model and mappings
Mapping properties from view to view model
Nested view models
Validation in view models
Advanced view models
Code snippets
vm - declare a view model
vmprop - declare a property on a view model
Explanation
When implementing a simple view model without a model, only one property has to be implemented that represents the checkbox that needs to
be checked. The example view model declares a single property using the vmprop code snippet. Then, a field error is set if the user has not
agreed in the ValidateFields method.
Code
C#
/// <summary>
/// Simple view model.
/// </summary>
public class SimpleViewModel : ViewModelBase
{
/// <summary>
/// Gets the title of the view model.
/// </summary>
/// <value>The title.</value>
public override string Title { get { return "Just acknowledge"; } }
/// <summary>
/// Gets or sets whether the user has agreed to continue.
/// </summary>
public bool UserAgreedToContinue
{
get { return GetValue<bool>(UserAgreedToContinueProperty); }
set { SetValue(UserAgreedToContinueProperty, value); }
}
/// <summary>
/// Register the UserAgreedToContinue property so it is known in the class.
/// </summary>
public static readonly PropertyData UserAgreedToContinueProperty =
RegisterProperty("UserAgreedToContinue", typeof(bool));
/// <summary>
/// Validates the fields.
/// </summary>
protected override void ValidateFields(List<FieldValidationResult>
validationResults)
{
// Check if the user agrees to continue
if (!UserAgreedToContinue)
{
validationResults.Add(FieldValidationResult.CreateError(UserAgreedToContinueProperty,
"User must agree to continue");
}
}
}
[InterestedIn(typeof(FamilyViewModel))]
public class PersonViewModel : ViewModelBase
Then, inside the PersonViewModel (which is interested in the changes of FamilyViewModel), you only have to override the OnViewModelProperty
Changed method:
/// <summary>
/// Called when a property has changed for a view model type
/// that the current view model is interested in. This can
/// be accomplished by decorating the view model with the <see
cref="InterestedInAttribute"/>.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="propertyName">Name of the property.</param>
protected override void OnViewModelPropertyChanged(IViewModel viewModel, string
propertyName)
{
// You can now do something with the changed property
}
It is possible to be interested in multiple view models. Since the view model is passed to the OnViewModelPropertyChanged method, it is very
easy to check the type of the view model.
Code snippets
vm - declare a view model
vmprop - declare a property on a view model
Explanation
The most important thing to know is that there is a ViewModelManager in the library. This manager keeps track of all view models that are alive. A
view model automatically registers itself to the manager, and when it is closed, the view model automatically unsubscribes itself again.
By using the InterestedInAttribute, it is possible to receive notifications of other view models. The InterestedInAttribute defines a specific type of
view model the decorated view model is interested in. Then, the decorated view model will receive all the PropertyChanged events of the live view
models of that specific type. It is possible to define multiple attributes to watch several different types of view models.
Code
Watched view model
/// <summary>
/// Watched view model.
/// </summary>
public class WatchedViewModel : ViewModelBase
{
/// <summary>
/// Gets the title of the view model.
/// </summary>
/// <value>The title.</value>
public override string Title { get { return "View model being watched"; } }
/// <summary>
/// Initializes the object by setting default values.
/// </summary>
protected override void Initialize()
{
// Not required
}
}
/// <summary>
/// Interested view model.
/// </summary>
[InterestedIn(typeof(WatchedViewModel))]
public class InterestedViewModel : ViewModelBase
{
/// <summary>
/// Gets the title of the view model.
/// </summary>
/// <value>The title.</value>
public override string Title { get { return "View model that is interested"; } }
/// <summary>
/// Initializes the object by setting default values.
/// </summary>
protected override void Initialize()
{
// Not required
}
/// <summary>
/// Called when a property has changed for a view model type that the current view
model is interested in. This can
/// be accomplished by decorating the view model with the <see
cref="InterestedInAttribute"/>.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="propertyName">Name of the property.</param>
protected override void OnViewModelPropertyChanged(IViewModel viewModel, string
propertyName)
{
// TODO: Check what property was changed
}
/// <summary>
/// Called when a command for a view model type that the current view model is
interested in has been executed. This can
/// be accomplished by decorating the view model with the <see
cref="InterestedInAttribute"/>.
/// </summary>
/// <param name="viewModel">The view model.</param>
/// <param name="command">The command that has been executed.</param>
/// <param name="commandParameter">The command parameter used during the
execution.</param>
protected override void OnViewModelCommandExecuted(IViewModel viewModel,
ICatelCommand command, object commandParameter)
{
// TODO: Check what command has been executed
}
}
Code snippets
vm - declare a view model
vmprop - declare a property on a view model
Explanation
To be in full control, the only thing required is to create a basic view model with the vm code snippet. Then, the following methods should be
implemented:
Constructor - initialize the properties on the view model
ValidateFields - check for field errors in the view model
ValidateBusinessRules - check for business rules in the view model
Save - save the view model data to the model and then save the model
Code
C#
/// <summary>
/// Classical view model.
/// </summary>
public class ClassicalViewModel : ViewModelBase
{
#region Properties
/// <summary>
/// Gets or sets the Person.
/// </summary>
private Person Person
{
get { return GetValue<Person>(PersonProperty); }
set { SetValue(PersonProperty, value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(Person));
/// <summary>
/// Gets or sets the first name.
/// </summary>
public string FirstName
{
get { return GetValue<string>(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
/// <summary>
/// Register the FirstName property so it is known in the class.
/// </summary>
public static readonly PropertyData FirstNameProperty =
RegisterProperty("FirstName", typeof(string));
/// <summary>
/// Gets or sets the last name.
/// </summary>
public string LastName
{
get { return GetValue<string>(LastNameProperty); }
set { SetValue(LastNameProperty, value); }
}
/// <summary>
/// Register the LastName property so it is known in the class.
/// </summary>
public static readonly PropertyData LastNameProperty = RegisterProperty("LastName",
typeof(string));
#endregion
#region Methods
/// <summary>
/// Initializes the object by setting default values.
/// </summary>
protected override void Initialize()
{
// Get the person from (in this case) a magical context
Person = Context.CurrentPerson;
// Load the data manually to the view model
FirstName = Person.FirstName;
LastName = Person.LastName;
}
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results
to this list.</param>
protected override void ValidateFields(List<FieldValidationResult>
validationResults)
{
if (string.IsNullOrWhiteSpace(FirstName))
{
validationResults.Add(FieldValidationResult.CreateError(FirstNameProperty,
"First name is required"));
}
if (string.IsNullOrWhiteSpace(LastName))
{
validationResults.Add(FieldValidationResult.CreateError(LastNameProperty,
"Last name is required"));
}
}
/// <summary>
/// Saves the data.
/// </summary>
/// <returns>
///
<c>true</c> if successful; otherwise <c>false</c>.
/// </returns>
protected override Task<bool> Save()
{
return Task.Factory.StartNew(() =>
{
// Save the data manually to the model
Person.FirstName = FirstName;
Person.LastName = LastName;
// Save the model
return Person.Save();
});
}
#endregion
}
Code snippets
vm - declare a view model
vmpropmodel - declare a property as model on a view model
vmpropviewmodeltomodel - declare a property as a pass-through property on a view model"
Explanation
Defining a model is very simple, you only have to decorate your property with the ModelAttribute:
/// <summary>
/// Gets or sets the person.
/// </summary>
[Model]
public IPerson Person
{
get { return GetValue<IPerson>(PersonProperty ); }
private set { SetValue(PersonProperty , value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(IPerson));
Using the ModelAttribute is very powerful. Basically, this is the extended functionality in the view model. If the model supports IEditableObject, Be
ginEdit is automatically called in the initialization of the view model. When the view model is canceled, the CancelEdit is called so the changes are
undone.
When a model is defined, it is possible to use the ViewModelToModelAttribute, as you can see in the code below:
/// <summary>
/// Gets or sets the FirstName of the person.
/// </summary>
[ViewModelToModel("Person")]
public string FirstName
{
get { return GetValue<string>(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
/// <summary>
/// Register the FirstName property so it is known in the class.
/// </summary>
public static readonly PropertyData FirstNameProperty = RegisterProperty("FirstName",
typeof(string));
/// <summary>
/// Gets or sets the LastName of the person.
/// </summary>
[ViewModelToModel("Person")]
public string LastName
{
get { return GetValue<string>(LastNameProperty); }
set { SetValue(LastNameProperty, value); }
}
/// <summary>
/// Register the LastName property so it is known in the class.
/// </summary>
public static readonly PropertyData LastNameProperty = RegisterProperty("LastName",
typeof(string));
The ViewModelToModelAttribute in the code example above automatically maps the view model FirstName and LastName properties to the Pers
on.FirstName and Person.LastName properties. This way, you dont have to manually map the values from and to the model. Another nice effect
is that the view model automatically validates all objects defined using the ModelAttribute, and all field and business errors mapped are
automatically mapped to the view model.
Sometimes you need the full name of a person, you can easily acquire it by creating a custom converter:
Now, when we created the converter we should define it in mapping like this:
/// <summary>
/// Gets or sets the FullName of the person.
/// </summary>
[ViewModelToModel("Person", "FirstName", AdditionalPropertiesToWatch = new[] {
"LastName" }, ConverterType = typeof(CollapsMappingConverter))]
public string FullName
{
get { return GetValue<string>(FullNameProperty); }
set { SetValue(FullNameProperty, value); }
}
/// <summary>
/// Register the LastName property so it is known in the class.
/// </summary>
public static readonly PropertyData FullNameProperty = RegisterProperty("FullName",
typeof(string));
The ViewModelToModelAttribute in the code example above automatically maps the view model FullName property to the Person.FirstName and
Person.LastName properties and converts them with CollapsMappingConverter. This way, you dont have to manually map the values from the
model and update FullName property when FirstName or LastName property changed.
Summarized, the Model and ViewModelToModel attributes make sure no duplicate validation and no manual mappings are required.
Example implementation
The example below shows how the MapCenter is a dependency property on the control. It automatically maps the property to the ViewModel.Map
Center property.
In most cases, the only reason to subscribe to property changes is because of the ViewToViewModel attribute. If that is the case, it is best to use
the extension method AutoDetectViewPropertiesToSubscribe in the static constructor of the view:
static MyView()
{
typeof(MyView).AutoDetectViewPropertiesToSubscribe();
}
Mapping types
Catel supports the following mapping types using the ViewToViewModelMappingType enum.
Type
Description
TwoWayDoNothing
Two way, which means that either the view or the view model will update the values of the other party as soon as
they are updated.
When this value is used, nothing happens when the view model of the view changes. This way, it might be possible
that the values of the view and the view model are different. The first one to update next will update the other.
TwoWayViewWins
Two way, which means that either the view or the view model will update the values of the other party as soon as
they are updated.
When this value is used, the value of the view is used when the view model of the view is changed, and is directly
transferred to the view model value
TwoWayViewModelWins
Two way, which means that either the view or the view model will update the values of the other party as soon as
they are updated.
When this value is used, the value of the view model is used when the view model of the view is changed, and is
directly transferred to the view value.
ViewToViewModel
ViewModelToView
/// <summary>
/// Gets or sets the person.
/// </summary>
[Model]
public Person Person
{
get { return GetValue<Person>(PersonProperty); }
private set { SetValue(PersonProperty, value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(Person));
/// <summary>
/// Gets or sets the first name.
/// </summary>
[ViewModelToModel("Person")]
public string FirstName
{
get { return GetValue<string>(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
/// <summary>
/// Register the FirstName property so it is known in the class.
/// </summary>
public static readonly PropertyData FirstNameProperty = RegisterProperty("FirstName",
typeof(string));
If the Person.FirstName provides an error via the IDataErrorInfo interface, it will automatically be exposed by the view model as well.
/// <summary>
/// Gets or sets the person.
/// </summary>
[Model]
[Expose("FirstName")]
[Expose("MiddleName")]
[Expose("LastName")]
private Person Person
{
get { return GetValue<Person>(PersonProperty); }
set { SetValue(PersonProperty, value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(Person));
Simply declare the model property and decorate it with one or multiple ExposeAttribute instances. Not only are the properties automatically
available for binding, the view model also checks for errors and automatically maps these as well.
Validating fields
To validate fields, one should override the ValidateFields method. Below is an example of field validation on a view model:
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results to
this list.</param>
protected override void ValidateFields(List<IFieldValidationResult> validationResults)
{
if (!string.IsNullOrEmpty(FirstName))
{
validationResults.Add(FieldValidationResult.CreateError(FirstNameProperty,
"First name cannot be empty"));
}
}
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results to
this list.</param>
protected override void ValidateBusinessRules(List<IBusinessRuleValidationResult>
validationResults)
{
if (SomeBusinessErrorOccurs)
{
validationResults.Add(BusinessRuleValidationResult.CreateError("A business
error occurred"));
}
}
SetFieldError(FirstName, FirstNameRequired);
All errors that are mapped from the model to the view model automatically are available in the validationResults parameter. This way, the error
can be easily translated:
/// <summary>
/// Validates the field values of this object. Override this method to enable
/// validation of field values.
/// </summary>
/// <param name="validationResults">The validation results, add additional results to
this list.</param>
protected override void ValidateFields(List<IFieldValidationResult> validationResults)
{
foreach (var validationResult in validationResults)
{
if (validationResult.Message == "FirstNameRequired")
{
validationResult.Message = Properties.Resources.FirstNameRequired;
}
}
}
Of course this is not something you want to actually do in your view model, so youll probably have to write a helper class that translates the
validation for you. You might or might not like delaying the translation of the model errors to as close as the view, but it shows how extremely
powerful the improved validation of Catel is. And if you think a bit about it, wouldn't it be a good idea to delay the translation from the server to the
actual client to as close as the view?
/// <summary>
/// Gets or sets the first name.
/// </summary>
[Required("This value is required")]
private string FirstName
{
get { return GetValue<string>(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
/// <summary>
/// Register the FirstName property so it is known in the class.
/// </summary>
public static readonly PropertyData FirstNameProperty= RegisterProperty("FirstName",
typeof(string));
Validate(true, false);
It is possible to have more control about the lifetime of view models. To keep a view model alive, even when the view is unloaded, set the CloseVi
ewModelOnUnloaded property of the UserControl to false in the constructor of the view:
CloseViewModelOnUnloaded = false;
The view model will now be re-used when the view is loaded into the visual tree again.
Keep in mind that the developer is responsible for actually closing the view model
The Catel Examples repository contains an example demonstrating controlling the lifetime of view models
/// <summary>
/// Gets or sets the person.
/// </summary>
[Model]
public Person Person
{
get { return GetValue<Person>(PersonProperty); }
private set { SetValue(PersonProperty, value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(Person));
/// <summary>
/// Gets or sets the first name.
/// </summary>
[ViewModelToModel("Person")]
public string FirstName
{
get { return GetValue<string>(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
/// <summary>
/// Register the FirstName property so it is known in the class.
/// </summary>
public static readonly PropertyData FirstNameProperty = RegisterProperty("FirstName",
typeof(string));
However, if you only define the FirstName property just to protect your model, then why should you define the whole property? This is where the E
xposeAttribute property comes in very handy. This attribute internally registers a new dynamic property on the view model, and then uses the
same technique as the ViewModelToModelAttribute.
Below is the new way you can easily expose properties of a model and protect other properties of the model from the view:
/// <summary>
/// Gets or sets the person.
/// </summary>
[Model]
[Expose("FirstName")]
[Expose("MiddleName")]
[Expose("LastName")]
private Person Person
{
get { return GetValue<Person>(PersonProperty); }
set { SetValue(PersonProperty, value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(Person));
So, this is a very cool feature that allows you to protect your model without having to re-define all the properties on the view model. Also, the
validation in the model is automatically synchronized with the view model when you use this attribute.
When the method returns null, it will fall back on the earlier determined view model type.
There is no need to set the e.ViewModelType to null because that is the default value.
Starting with Catel 3.1, it is possible to take control of the view model instantiation of a view dynamically at runtime. This feature can be used
when the construction of a view model is more complex than injecting the datacontext as model, or when the view model instance in a custom
repository should be re-used.
How to control the view model instantiation using the IViewModelFactory
How to control the view model instantiation using a UserControl
How to control the view model instantiation using a behavior
Preventing the logic to create a view model by itself
Note that this feature is available on all controls an behaviors, not only for the UserControl
ServiceLocator.Default.RegisterType<IViewModelFactory, CustomViewModelFactory>();
Controlling the instantiation of the view model dynamically when using the UserControl is extremely easy. You can override the GetViewModelInst
ance(object) method like this:
When the method returns null, the logic will try to construct the view model by itself.
Controlling the instantiation of the view model dynamically when using behaviors must be done via the DetermineViewModelInstance event like
this:
There is no need to set the e.ViewModel to null because that is the default value.
Views
Views are what the user actually sees on the screen. The views communicate with the view models. The MVVM Framework that ships with Catel
can be used on its own. However, the framework requires some conventions that you, as end-user of Catel, should not be worrying about.
Therefore, Catel ships with both a UserControl and a Window that fully support the MVVM Framework. Those can both be used as base classes
for all controls and windows developed in an application.
Android
iOS
XAML
Tips & tricks
Android
Bindings
Binding properties
Binding properties with events
Binding properties with converters
Binding commands
You might also be interested in:
Activities (pages)
Fragments (user controls)
Note that this guide is not a full Android development guide. It will cover the basics though.
Bindings
Unfortunately Android does not have a powerful binding system like XAML does. Therefore it is required to manually synchronize data from the
view to the view model and back or to use the binding system in Catel.
Binding properties
[Activity]
public class SecondActivity : Catel.Android.App.Activity
{
private PersonView _personView;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Page_Second);
_personView = FragmentManager.FindFragmentById<PersonView>(Resource.Id.PersonView);
}
protected override void AddBindings(BindingContext bindingContext, IViewModel
viewModel)
{
var vm = (SecondViewModel) viewModel;
bindingContext.AddBinding(() => vm.Title, () => Title);
bindingContext.AddBinding(() => vm.Person, () => _personView.DataContext);
}
}
Binding commands
Activities (pages)
The user controls in Android are called fragments. This means that if a user control must be created, it must derive from the Fragment class.
Catel provides a base implementation of this class to ensure full compatibility with the MVVM framework that ships with Catel.
Creating the view model
Creating the view
Designing the view
Setting up synchronization
[Activity(MainLauncher = true)]
public class MainActivity : Catel.Android.App.Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Page_Main);
}
}
Setting up synchronization
In Android it is required to manually synchronize the values between the view and view model. Below is the fully extended MainActivity class
containing these mapping functionality:
[Activity(MainLauncher = true)]
public class MainActivity : Catel.Android.App.Activity
{
private Button _testButton;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Page_Main);
// Note: at this stage the visual tree is guaranteed in Android
_testButton = FindViewById<Button>(Resource.Id.MyButton);
_testButton.Click += delegate {
GetViewModel<MainViewModel>().RunCommand.Execute(); };
}
protected override void SyncViewModel()
{
var vm = GetViewModel<MainViewModel>();
if (vm == null)
{
return;
}
Title = vm.Title;
_testButton.Text = string.Format("{0} clicks!", vm.Counter);
}
}
Setting up synchronization
In Android it is required to manually synchronize the values between the view and view model. Below is the fully extended PersonView class
containing these mapping functionality:
iOS
You might also be interested in:
XAML
This section contains the XAML related views.
Window and DataWindow
UserControl
MVVM behaviors
Validation controls
Using external controls
Advanced information about views
Customizing DataContext subscription behavior
Automatic validation
Customizing the buttons
Styling the DataWindow
<catel:DataWindow x:Class="Catel.Articles._03___MVVM.Examples.DataWindow.PersonWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com">
<!-- Content left out for the sake of simplicity -->
</catel:DataWindow>
As you can see, one thing has changed in regard to a normal window definition:
1. The type definition has changed from Window to catel:DataWindow;
The code-behind is even simpler:
/// <summary>
/// Interaction logic for PersonWindow.xaml
/// </summary>
public partial class PersonWindow : DataWindow
{
/// <summary>
/// Initializes a new instance of the <see cref="PersonWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
public PersonWindow(PersonViewModel viewModel)
: base(viewModel)
{
InitializeComponent();
}
}
The code above is everything you will need when using the MVVM Framework of Catel.
The easiest way to create a new DataWindow is to use item templates
Automatic validation
The cool thing about the DataWindow is that is automatically wraps the content that a developer defines into an InfoBarMessageControl. This
way, errors and warnings are shown at the top of the window. Another feature of the DataWindow is that is automatically creates a WarningAndEr
rorValidator control and sets the view model as source. This way, all the warnings of the view model are also shown in the InfoBarMessageContro
l. In other words: you dont have to do anything to implementation validation, except for actually setting the warnings and errors in your view
model. And if the validation takes place in the model, you can use the ViewModelToModelAttribute so you dont have to worry about that either.
/// <summary>
/// Upload window.
/// </summary>
public class UploadWindow : DataWindow
{
public UploadWindow()
: base(DataWindowMode.Custom)
{
InitializeComponent();
}
}
2. Add the custom buttons. This must be done before the call to InitializeComponent.
/// <summary>
/// Upload window.
/// </summary>
public class UploadWindow : DataWindow
{
public UploadWindow()
: base(DataWindowMode.Custom)
{
AddCustomButton(new DataWindowButton("Upload", "Upload"));
InitializeComponent();
}
}
Description
DataWindowStyle
The actual window style which can be used to decorate or customize the window itself.
DataWindowButtonContainerStyle
The container that is used for the buttons. This is a WrapPanel, so the styles must match that.
DataWindowButtonStyle
The style for the buttons. By default, the buttons are right aligned and have a fixed size.
UserControl
The UserControl is a very interesting class of Catel, and fully shows the power of the MVVM Framework that ships with Catel. The user control is
able to fully integrate MVVM on a user control level and solves the nested user control problem, which is explained in detail a bit further in this
documentation.
Automatic construction without parameter
Automatic construction with parameter
Mapping properties from/to view model
Keeping view models alive
Now comes the real power of UserControl in to play. For example, to show the company and its managers, one has to write an items control that
contains the companies and then a user control containing the details of the company. For the sake of simplicity, I will leave the employees out for
now. The usage might seem a bit complex, but once you get the hang of it, its actually quite simple. First of all, create a view model that has a
constructor of the model that you want to accept, in our case the Company class of which we will show the details:
/// <summary>
/// Initializes a new instance of the <see cref="CompanyViewModel"/> class.
/// </summary>
/// <param name="company">The company.</param>
public CompanyViewModel(Models.Company company)
: base()
{
// Store values
Company = company;
}
As you can see, the view model can only be constructed by passing a company model. This is quite normal, because how can we show details of
a non-existing (null) company? Now we have a view model, we can create our user control:
<catel:UserControl
x:Class="Catel.Articles._03___MVVM.Examples.UserControlWithParameter.Company"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com">
<!-- For the sake of simplicity, content is left out -->
</catel:UserControl>
/// <summary>
/// Interaction logic for Company.xaml
/// </summary>
public partial class Company : UserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="Company"/> class.
/// </summary>
public Company()
{
InitializeComponent();
}
}
Now the control is created (I dont want to focus on the actual control content here, since its not important), we can use the user control in our
main window that has a collection of companies. The view model also has a SelectedCompany property representing the selected company
inside the listbox. Then, we use the Company control and bind the data context to the SelectedCompany property:
As the code shows, there is a listbox containing all the companies. The data context of the user control is bound to the SelectedCompany. The
cool thing is that as soon as a company is selected, the user control will create an instance of the CompanyViewModel because it accepts a Com
pany instance in the constructor. The screenshot of the example application will (hopefully) give more insight in what change is causing the exact
view model creation:
In the image above, you see 2 controls. The first one is an items control that binds to the CompaniesViewModel because the window represents
list of companies. The second one is the CompanyControl, which dynamically constructs the CompanyViewModel as soon as a company is
selected at the left. This means that for every company selection, and new view model is constructed. This way, you can handle the saving,
canceling and closing of the view model before the next is view model is constructed.
The best thing about this is that you can actually start re-using user controls throughout your whole application. Instead of having the main view
model have to define all the properties of (sub) controls, now each control has its own view model, and you dont have to worry about the
implementation in the parent of a control. Simply set the data context of the user control to the right type instance, and the user control will handle
the rest.
The easiest way to create a new UserControl is to use item templates
[ViewToViewModel]
public bool MyDependencyProperty
{
get { return (bool)GetValue(MyDependencyPropertyProperty); }
set { SetValue(MyDependencyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyDependencyProperty.
enables animation, styling, binding, etc...
public static readonly DependencyProperty MyDependencyPropertyProperty =
DependencyProperty.Register("MyDependencyProperty", typeof(bool),
typeof(MyControl), new UIPropertyMetadata(true));
This
By default, the attribute assumes that the name of the property on the view model is the same as the property on the user control. To specify a
different name, use the overload of the attribute constructor as shown in the following example:
[ViewToViewModel("MyViewModelProperty")]
public bool MyDependencyProperty
... (remaining code left out for the sake of simplicity)
In the first place, all of this looks fine enough. However, what should happen when the current view model of the control is replaced by another
instance? Or what if the developer only wants to map values from the control to the view model, but not back? By default, the view model will take
the lead when this attribute is used. This means that as soon as the view model is changed, the values of the control will be overwritten by the
values of the view model. If another behavior is required, the MappingType property of the attribute should be used:
[ViewToViewModel("MyViewModelProperty", MappingType =
ViewToViewModelMappingType.TwoWayControlWins)]
public bool MyDependencyProperty
... (remaining code left out for the sake of simplicity)
Description
TwoWayDoNothing
Two way, which means that either the control or the view model will update the values of the other party as soon as
they are updated.
When this value is used, nothing happens when the view model of the user control changes. This way, it might be
possible that the values of the control and the view model are different. The first one to update next will update the
other.
TwoWayViewWins
Two way, which means that either the control or the view model will update the values of the other party as soon as
they are updated.
When this value is used, the value of the control is used when the view model of the user control is changed, and is
directly transferred to the view model value.
TwoWayViewModelWins
Two way, which means that either the control or the view model will update the values of the other party as soon as
they are updated.
When this value is used, the value of the view model is used when the view model of the user control is changed,
and is directly transferred to the control value.
ViewToViewModel
ViewModelToView
The UserControl automatically closes view models in the Unloaded event. Reason for this is that there is no guarantee that the control will be
loaded again. However, this can have some negative side effects. On of this side effects is a user control shown as a tab in a tab control. One of
the behaviors of a tab control is that it unloads all non-active tabs from the visual tree. Therefore, the UserControl cancels and closes the view
model. However, the state of the tab is lost then as well.
To prevent this behavior, it is possible to keep view models alive when a user control is unloaded. This can be done by setting CloseViewModelO
nUnloaded to false. This way, the view model is not closed and will be re-used when the control is loaded again. The downside of this is that the
responsibility of closing and disposing the view model is now in the hands of the developer. A great way to make a difference between unloading
(tab switch) and closing is to create a close button on the tabs that will explicitly call ViewModel.CloseViewModel.
MVVM behaviors
Starting with Catel 2.0, it is possible to use the logic of the following controls as a behavior:
DataWindow => WindowBehavior
UserControl => UserControlBehavior
Page => NavigationPageBehavior
This means that you no longer have to derive user controls from the UserControl to use the ability to solve the nested user controls problem. Or, if
you are not happy with the endless possibilities of the DataWindow, why not just creating a custom one without having to think about the MVVM
integration.
WindowBehavior
UserControlBehavior
NavigationPageBehavior
WindowBehavior
The WindowBehavior class takes care of all the MVVM integrations of a window and a view model. So, where you previously had to derive a
Window implementation from DataWindow, you can now create a new Window like any application and then add this:
The Save and Cancel properties are not obligate, and need the format of [controlname].[event]. By default, the Click event is used, so if a button
(or another control that should respond using the Click event), the [controlname] is sufficient.
<i:Interaction.Behaviors>
<catel:WindowBehavior ViewModelType="viewmodels:DemoWindowViewModel"
Save="okButton.Click" Cancel="cancelButton.Click" />
</i:Interaction.Behaviors>
Seems too easy right? Well, it is really all you have to do.
This looks great, but why is there still a DataWindow with this terrific solution?
First of all, we have to think about all the people that are already using Catel. We don't want to break their code and provide backward
compatibility. Updates to the logic will be applied to both the behavior and the view base because the logic is located in a separate class. Also, the
DataWindow is truly a terrific class, which supports lots of customization and takes care of dumb generation of buttons and the InfoBarMessageC
ontrol.
For more information, check out the the Catel.Examples.WPF.AdvancedDemo which shows the differences
UserControlBehavior
The UserControlBehavior class takes care of all the MVVM integrations of a user control and a view model. So, where you previously had to
derive a UserControl implementation from UserControl, you can now create a new UserControl like any application and then add this:
<i:Interaction.Behaviors>
<catel:UserControlBehavior ViewModelType="viewmodels:DemoWindowViewModel" />
</i:Interaction.Behaviors>
This looks great, but why is there still a UserControl with this terrific solution?
First of all, we have to think about all the people that are already using Catel. We don't want to break their code and provide backward
compatibility. Also, the UserControl implements the IViewModelContainer interface which allows chained view models in a hierarchy. If you don't
need this, just go for the behavior. If you need hierarchy chains, either let the custom UserControl implement it or use the UserControl.
To support nested user controls and their validation, it is important to chain views together using the IViewModelContainer interface. You can
choose not to do this, but then it is important to disable SupportParentViewModelContainers for performance reasons (otherwise, the behavior will
keep searching the visual tree for the parent view model).
For more information, check out the the Catel.Examples.WPF.AdvancedDemo which shows the differences
NavigationPageBehavior
The NavigationPageBehavior class takes care of all the MVVM integrations of a page (used in navigation or browser based applications) and a
view model. So, where you previously had to derive a Page implementation from Page, you can now create a new Page like any application and
then add this:
<i:Interaction.Behaviors>
<catel:NavigationPageBehavior ViewModelType="viewmodels:DemoWindowViewModel" />
</i:Interaction.Behaviors>
This looks great, but why is there still a Page with this terrific solution?
First of all, we have to think about all the people that are already using Catel. We don't want to break their code and provide backward
compatibility. Also, the Page class comes with more default functionality that you might be interested in.
For more information, check out the the Catel.Examples.WPF.BrowserApplication which shows the differences
Validation controls
There are some very important controls in Catel which help with visualizing the validation results.
InfoBarMessageControl
WarningAndErrorValidator
Styling in DataWindow
InfoBarMessageControl
Ever wanted to show the details of error messages to your end-users? Then, the InfoBarMessageControl is the control to use! The control shows
a summary of all business and field errors provided by bindings on objects that implement the IDataErrorInfo interface.
In combination with the WarningAndErrorValidator control, the InfoBarMessageControl can even show field and business warnings for objects that
implement the IDataWarningInfo interface that ships with Catel.
<catel:InfoBarMessageControl>
<!-- Actual content here -->
</catel:InfoBarMessageControl>
The InfoBarMessageControl subscribes to the Validation class. This class is responsible for showing the red border around the controls that WPF
shows by default. Then, it requests the actual field error property of the data item. This is added to an internal collection of error messages, and
therefore the control is able to show the errors of all bindings.
When the WarningAndErrorValidator control is found as a child control, the InfoBarMessageControl also subscribes to the events exposed by the
WarningAndErrorValidator. The internal working of that control is explained later in this article. When a data object is subscribed via the WarningA
ndErrorValidator, the InfoBarMessageControl will also handle the warnings and business errors of that data object.
WarningAndErrorValidator
The WarningAndErrorValidator control is not visible to the end user. The only thing this control takes care of is to forward business errors and
warnings to controls that are interested in them. The only control that ships with Catel is the InfoBarMessageControl. Thanks to the WarningAndE
rrorValidator, the InfoBarMessageControl is able to show business errors and warnings to the end user.
The WarningAndErrorValidator needs to be placed inside an InfoBarMessageControl. The control then subscribes to all property changed events
to make sure it receives all change notifications. Then, on every property change, the control checks whether the sender either implements the ID
ataErrorInfo or IDataWarningInfo interfaces.
When an error or warning is found on the changed property, the control invokes the corresponding events so the InfoBarMessageControl can
show the right information. When an error or warning no longer exists in a model, a Removed event is invoked so the InfoBarMessageControl kno
ws that the error or warning should be removed from the summary.
Styling in DataWindow
A InfoBarMessageControl is automatically added to the DataWindow, if you want to use a different style for this InfoBarMessageControl, you
must override the default style, add your own InfoBarMessageControl and disable the default InfoBarMessageControl from the DataWindow.
1. Create a custom style based on the default style.
2. Change the x:Key from x:Key="{x:Type local:InfoBarMessageControl}" to x:Key="yourCustomStyleKey"
3. Set the InfoBarMessageControlGenerationMode to None
/// <summary>
/// Initializes a new instance of the <see cref="DataWindow"/> class.
/// </summary>
/// <param name="viewModel">The view model to inject.</param>
/// <remarks>
/// This constructor can be used to use view-model injection.
/// </remarks>
public DataWindow(DataWindowViewModel viewModel)
: base(viewModel, DataWindowMode.Custom, null,
DataWindowDefaultButton.None, true, InfoBarMessageControlGenerationMode.None)
{
InitializeComponent();
}
4. Add a new InfoBarMessageControl as root control to your DataWindow and set the style.
/// <summary>
/// Base class for a control with the Catel mvvm behavior.
/// </summary>
public class TabItem : RadTabItem, IUserControl
{
private readonly UserControlLogic _logic;
private event EventHandler<EventArgs> _viewLoaded;
private event EventHandler<EventArgs> _viewUnloaded;
private event EventHandler<Catel.MVVM.Views.DataContextChangedEventArgs>
_viewDataContextChanged;
/// <summary>
/// Initializes a new instance of the <see cref="TabItem"/> class.
/// </summary>
public TabItem()
{
_logic = new UserControlLogic(this, viewModelType);
_logic.PropertyChanged += (sender, e) => PropertyChanged.SafeInvoke(this, e);
Loaded += (sender, e) => _viewLoaded.SafeInvoke(this);
Unloaded += (sender, e) => _viewUnloaded.SafeInvoke(this);
this.AddDataContextChangedHandler((sender, e) =>
_viewDataContextChanged.SafeInvoke(this, new
Catel.MVVM.Views.DataContextChangedEventArgs(e.OldValue, e.NewValue)));
You would expect an abstract class here, but the designers (both Visual Studio and Expression Blend) can't handle abstract base
classes
/// <summary>
/// Base class for a window with the Catel mvvm behavior.
/// </summary>
public class Window : RadWindow, IDataWindow
{
private readonly WindowLogic _logic;
private event EventHandler<EventArgs> _viewLoaded;
private event EventHandler<EventArgs> _viewUnloaded;
private event EventHandler<Catel.MVVM.Views.DataContextChangedEventArgs>
_viewDataContextChanged;
public Window(IViewModel viewModel)
{
_logic = new WindowLogic(this, null, viewModel);
_logic.ViewModelChanged += (sender, e) => ViewModelChanged.SafeInvoke(this,
e);
_logic.PropertyChanged += (sender, e) => PropertyChanged.SafeInvoke(this, e);
Loaded += (sender, e) => _viewLoaded.SafeInvoke(this);
Unloaded += (sender, e) => _viewUnloaded.SafeInvoke(this);
this.AddDataContextChangedHandler((sender, e) =>
_viewDataContextChanged.SafeInvoke(this, new
Catel.MVVM.Views.DataContextChangedEventArgs(e.OldValue, e.NewValue)));
// Because the RadWindow does not close when DialogResult is set, the following code
is required
ViewModelChanged += (sender, e) => OnViewModelChanged();
// Call manually the first time (for injected view models)
OnViewModelChanged();
WindowStartupLocation = WindowStartupLocation.CenterScreen;
SetBinding(RadWindow.HeaderProperty, new Binding("Title"));
}
public IViewModel ViewModel
{
get { return _logic.ViewModel; }
}
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<EventArgs> ViewModelChanged;
event EventHandler<EventArgs> IView.Loaded
{
add { _viewLoaded += value; }
remove { _viewLoaded -= value; }
}
event EventHandler<EventArgs> IView.Unloaded
{
add { _viewUnloaded += value; }
remove { _viewUnloaded -= value; }
}
event EventHandler<Catel.MVVM.Views.DataContextChangedEventArgs>
IView.DataContextChanged
{
add { _viewDataContextChanged += value; }
remove { _viewDataContextChanged -= value; }
}
private void OnViewModelChanged()
{
if (ViewModel != null && !ViewModel.IsClosed)
{
ViewModel.Closed += ViewModelClosed;
}
}
private void ViewModelClosed(object sender, ViewModelClosedEventArgs e)
{
Close();
}
}
You would expect an abstract class here, but the designers (both Visual Studio and Expression Blend) can't handle abstract base
classes
Main flow
The following flowchart shows what happens with a user control in the main flow (the startup). First, it checks whether the user control is loaded
(which is not in a normal case). If the control is loaded, it goes directly to determining the datacontext. Otherwise, it will postpone the action until
the Loaded event.
Loaded
When the control is loaded, it starts checking for the first time whether the current datacontext can be used to create a view model. But, before it
does this, it checks whether it should (and can) re-use an existing view model. To control whether view models should be re-used, use the CloseV
iewModelOnUnloaded property.
If a view model can and should be re-used, it sets the view model as data context and that's it. If there is no view model, or the previous view
model should not be re-used, the control continues to determine the datacontext.
Unloaded
Another event that is very important is the Unloaded event. In this event, the control either cleans up the view model or stores it so it can be
re-used later. Then, it also restores the old datacontext so it never breaks existing application bindings. This way, the control won't leave any
traces behind.
DataContextChanged
The DataContextChanged event is used to react to changes of the datacontext. You might be thinking: "there is no DataContextChanged event in
Silverlight". We use the DataContextHelper class for that. If the new datacontext is new (thus not a view model that the control just set itself), it it
continues to determine the datacontext. Otherwise, it will not take any action.
DetermineDataContext
All other flowcharts eventually led to this flowchart, the determination of the datacontext. The determination of the datacontext is very important,
because this is the moment where the user control transforms the datacontext into a new view model if possible. First it tries is to construct the
view model with the datacontext. So, if the datacontext is an object of type Person, and the view model of the user control has a constructor that
accepts a Person object, it injects the datacontext into the constructor of the view model. If that fails, or there is simply no constructor, the control
checks whether the view model has an empty constructor. If so, it constructs the view model and sets it as the new datacontext. If not, it will leave
the datacontext untouched.
Basically, this is all that happens on a higher level to transform a datacontext into a view model. Under the hood, it's a bit more complicated but
again, on a higher level this is what happens.
Note that it is possible that multiple views are linked to the same view model
Catel.Mvc
The MVC library provided by Catel provides code to easily combine the power of Catel with ASP.NET MVC.
Configuring dependency injection
Catel.Mvc.DependencyInjectionConfig.RegisterServiceLocatorAsDependencyResolver();
GlobalConfiguration.Configuration.DependencyResolver = new
CatelWebApiDependencyResolver();
Catel.Extensions.Controls
Controls
Pixel shaders
StyleHelper
Themes
Controls
There are several controls available. See the child pages.
StackGrid
TabControl
TraceOutputControl
WatermarkTextBox
StackGrid
Although the example looks crappy (I am not a designer), it shows the power of the StackGrid. You don't have to specify the Grid.Row and
Grid.Column attached properties. Remember the times that you want to insert a grid and you had to increase all the numbers? From now on, use
the StackGrid!
<catel:StackGrid>
<!-- Row definitions -->
<catel:StackGrid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" MinHeight="15" />
<RowDefinition Height="Auto" />
</catel:StackGrid.RowDefinitions>
<!-- Column definitions -->
<catel:StackGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</catel:StackGrid.ColumnDefinitions>
<!-- Name, will be set to row 0, column 1 and 2 -->
<Label Content="Name" />
<TextBox Text="Geert van Horrik" />
<!-- Empty row -->
<catel:EmptyRow />
<!-- Wrappanel, will span 2 columns -->
<WrapPanel Grid.ColumnSpan="2">
<Button Command="ApplicationCommands.Close" />
</WrapPanel>
</catel:StackGrid>
The Grid is an excellent control to show several controls in a nice layout on the screen. However, it happens a lot that a grid consists of only 2 or
3 columns, and the first column is for all the labels, and the second one is for controls such as textboxes. You correctly implement all the windows
and controls of your application based on user requirements, and then the user decides that he/she wants a row inserted into a grid containing
about 20 rows. When this happens, you need to re-define all the row attributes of the grid.
With the StackGrid, it is no longer required to define the row and column definitions. The StackGrid can smartly interpret the location of the
controls and therefore fill in the Grid.Row and Grid.Column attached properties for you. You need an empty row? No problem, you can use the
EmptyRow class to fill up a row for you. You want a column span? No problem, just use the existing Grid.Column attached property and the
StackGrid will automatically handle this for you.
The StackGrid internally uses a Grid to measure the layout. However, it dynamically loops through its children, and then assigns the Grid.Row
and Grid.Column attached properties for the user.
TabControl
A custom implementation of the TabControl which allows the customization of the way tab items are loaded.
The following options are available:
LazyLoading => Load all tabs using lazy loading, but keeps the tabs in memory afterwards.
LazyLoadingUnloadOthers => Load all tabs using lazy loading. As soon as a tab is loaded, all other loaded tabs will be unloaded.
EagerLoading => Load all tabs as soon as the tab control is loaded.
EagerLoadingOnFirstUse => Load all tabs when any of the tabs is used for the first time.
TraceOutputControl
The TraceOutputControl will be removed in Catel v5.0. It is deprecated and replaced by the LogViewerControl in Orc.Controls.
TraceOutputControl is a debugging convenience control. It shows all the trace and logging output in a filterable control. This way, you can easily
view all the binding errors, etc., in your app instead of the non-colored output window in Visual Studio.
XAML sample
<Controls:TraceOutputControl />
C# sample
Many times, developers are inside an application viewing the result of what they have created. But, they also want to know what is happening in
the background and view the traces they have written. The output window of Visual Studio is a solution, but it doesnt show errors very well (black,
just as the normal output). Also, it doesnt allow run-time filtering of the results.
The TraceOutputControl allows a developer to embed a control inside a window or control in the actual application and view the information when
the application is actually running. The TraceOutputControl is also available as a separate window in case it cant be embedded into the software
itself (for example, when a plug-in is being developed for a 3rd party application).
The TraceOutputControl subscribes a custom TraceListener to the Trace.Listeners collection. Then, it filters out the messages that the user
actually wants to see and stores these messages into an internal collection so the user can still filter the messages at a later time.
Starting with Catel 4.0, the TraceOutputControl uses the logging system in Catel to display both log and trace messages
WatermarkTextBox
The WatermarkTextBox will be removed in Catel v5.0. It is deprecated and replaced by the WatermarkTextBox in Orc.Controls.
The WatermarkTextBox allows to set a watermark on textboxes that do not yet have a value.
<catel:WatermarkTextBox>
<catel:WatermarkTextBox.Watermark>
<StackPanel Orientation="Horizontal">
<Image Source="/Images/Address.png" />
<TextBlock Text="Enter the e-mail" />
</StackPanel>
</catel:WatermarkTextBox.Watermark>
</catel:WatermarkTextBox>
Pixel shaders
This documentation only applies to WPF
Catel also uses pixel shaders to apply effects to controls via themes and styles. One of the pixel shaders is, for example, the GrayscaleEffect.
This effect automatically converts an image on a button to gray scale when the button is disabled. Below is an example of the shader effect:
If there are a lot of buttons used on the screen, it might be possible that the video card does not support so many shaders, and then WPF will start
throwing exceptions. In that case, first try to set the shader mode of Catel to ShaderRenderMode.Software. If that doesnt work, you can turn the
shaders off by using ShaderRenderMode.Off.
StyleHelper
The StyleHelper class has a few static members that will create style forwarders. Style forwarders are styles that are defined on the application
level, not on the theme level. This allows you to create forwarders with the same key as the control name, but that will forward to the
DefaultxxxStyle. Since the new styles are defined at the application level, you will not get any circular references because the style defined in the
theme cannot access the application level resources.
This is accomplished by simply calling StyleHelper.CreateStyleForwardersForDefaultStyles() in the OnStartup of an application.
Catel currently ships with a several theme files. This example theme file is based on the Aero" theme that is included in the WPF libraries. The
theme of Catel corrects the margins, since the default Aero" theme sets the margin of all controls to 0, which will result in all the user controls
being stuck together, as shown in the figure below:
We see too many developers fixing the margins on the control directly, while this can also be accomplished by using the Catel theme and the
included StyleHelper class. See the image below for the final result:
Themes
Using the themes is pretty simple. First, the right theme has to be added to the application resource dictionary:
<Application x:Class="OverrideStyles.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Set custom theme -->
<ResourceDictionary
Source="/Catel.Extensions.Controls;component/themes/generic.xaml"
/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
The next step is optional. If the margins should automatically be corrected by the stylehelper, it is required to call the following code somewhere in
the application (application startup is recommended):
namespace OverrideStyles
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// Create style forwarders
Catel.Windows.Helpers.StyleHelper.CreateStyleForwardersForDefaultStyles();
// Call base
base.OnStartup(e);
}
}
}
Catel.Extensions.CSLA
Catel supports CSLA. All MVVM features in Catel (such as nested user controls) can be used with in combination with the CSLA ViewModelBase.
For more information about CSLA, see http://www.lhotka.net/cslanet/
Catel.Extensions.Data
The data extension provides an implementation of the specification pattern.
Specifications
Specifications
Catel provides an implementation of the specification pattern.
A specification pattern outlines a business rule that is combinable with other business rules. In this pattern, a unit of business logic inherits its
functionality from the abstract aggregate Composite Specification class. The Composite Specification class has one method called IsSatisfiedBy
that returns a boolean value.
After instantiation, the specification is "chained" with other specifications, making new specifications easily maintainable, yet highly customizable
business logic. Furthermore upon instantiation the business logic may, through method invocation or inversion of control, have its state altered in
order to become a delegate of other classes such as a persistence repository.
The big advantage is that commonly used queries can be converted to a specification or a combination of specifications. Then if the query should
change (for example, IsDeleted is introduced), only the specification needs to be changed.
The cool thing about the specification implementation in Catel is that it can be used for Entity Framework queries, but also provides implicit
casting to a Func<TEntity, bool>. This way a specification can be passed to any method accepting a method and thus also works with all linq
queries on normal collections.
Catel.Extensions.DynamicObjects
In .NET, it is possible to create fully dynamic objects. This makes it possible to create types of which the members are not yet known at compile
time. Starting with Catel 3.7, the DynamicModelBase is fully dynamic and still provides the features such as serialization. Catel supports dynamic
objects by implementing the IDynamicMetaObjectProvider which is available in WPF, Silverlight and Windows RT.
For more information about dynamic programming, see MSDN
It is important to know that you must use the keyword dynamic to instantiate the type.
The DynamicModelBase class derives from ModelBase. However it must be preceded by the dynamic keyword. To use the ModelBase functionali
ty, cast it to the right type:
Catel.Extensions.EntityFramework
Starting with Catel 3.5, support for Entity Framework was added. For more information about Entity Framework, see the official documentation by
Microsoft.
Getting started with Entity Framework in Catel
Using the DbContextManager
Using the repositories and unit of work
Using the ModelBase as base class for code-first entities
Using stored procedures and functions
Context
Creating the code-first models
Create a new context with code-first models right-clicking on the solution => Add => New item... Then search for ADO.NET Entity Data Model and
give it a name and follow the wizard.
[Table("person")]
public partial class Person : ModelBase
{
[StringLength(50)]
public string FirstName { get; set; }
[StringLength(50)]
public string lastname { get; set; }
public long PersonId { get; set; }
public long FamilyId { get; set; }
public virtual Family Family { get; set; }
}
When the classes are derived from ModelBase, EF will try to serialize all the default Catel properties as well. To make sure that EF ignores the
Catel properties, go to the Context class and search for the OnModelCreating method. Then add the IgnoreCatelProperties extension method to
all entity definitions as shown in the example below:
Repositories
Creating repositories
Once the DAL is correctly set up, it's time to create the repositories. Creating repositories with Catel is super easy. Just create (or generate with
T4) a class and interface for each entity:
Registering repositories
In order for the unit of work to find the repositories, they need to be registered in the ServiceLocator. This can be done as follows:
Unit of Work
Retrieving data
To retrieve data inside a transaction, you can use the UnitOfWork. Inside a method / service call / view model, use the following code:
Catel will automatically take care of scoping of the DbContext when using unit of work and repositories
Saving data
To save data, the entities must be updated in the repositories and then saved in the unit of work. Below is an example, assuming the list of
families contains updated entities:
Obtaining a DbContext
Obtaining a DbContext is very simple by using the DbContextManager.
It is very important to wrap the DbContextManager in a using state because it must be disposed
Scoping is all done automatically because when a DbContextManager is instantiated, a reference counter is increased. Every time an instance of
the DbContextManager is disposed, the reference counter is decreased. When the reference count reaches zero (0), it will dispose the DbContext
that it manages.
DbContextManagerHelper.CreateDbContextForHttpContext<MyEntities>();
When a request is ended, the context can be disposed by using this code:
DbContextManagerHelper.DisposeDbContextForHttpContext<MyEntities>();
Note that repositories and UoW should not be used to abstract away the ORM tool because that is just another abstraction layer which
is not required. Use it for the advantages mentioned above
A Unit of Work (UoW) is a a combination of several actions that will be grouped into a transaction. This means that either all actions inside a UoW
are committed or rolled back. The advantage of using a UoW is that multiple save actions to multiple Repositories can be grouped as a unit.
A repository is a class or service responsible for providing objects and allowing end-developers to query data. Instead of querying the DbContext
directly, the DbContext can be abstracted away to provide default queries and force required functionality to all end-developers of the DbContext.
The image above shows that the Unit of Work is the top-level component to be used. Each UoW contains its own DbContext instance. The
DbContext can either be injected or will be created on the fly. Then the UoW also contains repositories which always get the DbContext injected.
This way, all repositories inside a UoW share the same DbContext.
Creating a repository
A repository can be created very easily by deriving from the EntityRepositoryBase class. Below is an example of a customer repository:
[Table("person")]
public partial class Person : ModelBase
{
[StringLength(50)]
public string FirstName { get; set; }
[StringLength(50)]
public string lastname { get; set; }
public long PersonId { get; set; }
public long FamilyId { get; set; }
public virtual Family Family { get; set; }
}
In the context, add additional initialization code to ignore the Catel properties on the ModelBase class:
Now the entities are ready to be used and can contain validation.
Catel.Extensions.FluentValidation
The validation in Catel is extremely flexible, at this point you must already know it, but sometimes it is just not enough or you are forced to use
external validators.
Note that the FluentValidation extension can be used in combination with Catel.Core only, so it is not required to combine it with the
MVVM framework
FluentValidation is a small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules for your
business objects. Catel provides an extension in order to use FluentValidation as a validation library.
The only thing you have to do is install an isolate package named Catel.Extensions.FluentValidation, available via NuGet, then you will be able to
write your view models validations using FluentValidation approach.
In order to retrieve the right validators, you must register the FluentValidatorProvider:
ServiceLocator.Default.RegisterType<IValidatorProvider, FluentValidatorProvider>();
The FluentValidatorProvider will automatically retrieve the right validators associated with the view models.
How handle all Catel validation concepts with fluent validation classes?
Catel handle concepts like field or business rules errors and warnings . So, it's necessary map the fluent validation class to the specific Catel
validation using ValidationDescriptionAttribute.
Catel.Extensions.Interception
Interception Extensions enables you to write code that is executed each time a matching method is invoked using a fluent API. It's suited for cross
cutting concerns, such as transactions, security and logging.
Method interception
Property interception
Interception provides the following advantages:
Strongly-typed syntax
Interception semantics are based on strongly-typed method definitions, which permit to develop aspects taking advantage of features like:
auto-completion, refactoring, compile-time errors, etc ;
No configuration
We tried to simplify AOP implementation by favoring convention over configuration. As a result, no configuration of any kind is ever
required to build aspects. However, some conventions take place to make this possible ;
Minimum learning-curve
In order to get started, no previous experience with this or any other AOP implementation is required. No need to know or understand
AOP terminology, which is not always very intuitive. By looking at some examples developers can figure out how to intercept calls and
modularize their own applications ;
Methods as first-class elements
Utilizing dynamic proxies to implement AOP typically results in having to model aspects as interceptors. Such interceptors are commonly
associated with objects no with methods. Therefore, the developer is responsible for providing the logic to break down object interception
into method interception;
Usage in combination with IoC
The interception mechanism in Catel is based on the registration of a type in the ServiceLocator and in addition of the interception
configuration, this way, you can fully use the advantages of IoC.
Initially, Interception extension support Methods and Properties members for the types you register in the ServiceLocator (internally, we create an
class proxy instance which is actually registered in ServiceLocator).
This extension is based on FluentAop
Method interception
OnBefore callback
It always runs before the target method execution.
Called Before
The actual call
OnAfter callback
It runs after the target method execution, but only if it does not throw an exception.
OnInvoke callback
It runs instead of the target method execution.
OnReturn callback
It runs right before the proxy method execution is over. OnFinally callback runs first if it exist.
Called On Before
The actual call
Called On after
Called On Before
Called On After
Called On Before
Called On After
Called On Before
The actual call
Called On after
Called On Before
Called On After
Called On Before
Called On After
Exclude members
If you want to exclude some members using InterceptAll* methods, you can decorate your membre with the DoNotInterceptAttribute
in the interface type.
Intercepted
45
1,2,3...
The actual call
Execution Succeeded
Exception occurs: 'Exception has been thrown by the target of an invocation.'
Dispose Resources Here
Called before
The actual call
Called after
Property interception
Intercept setters
Intercept getters
Intercept setters
Use the following code:
Intercept getters
Use the following code:
Catel.Extensions.Memento
Lots of real world applications need to implement undo/redo. However, most applications written in MVVM lack this feature because it is very hard
to implement. Luckily, Catel solves this issue by introducing the IMementoService. The IMementoService is a service that allows a developer to
register custom actions that should be undone. A few actions you can think of:
Property change of a model
Item is added or removed to/from a collection
A method is executed
One way to introduce the memento pattern is by creating a copy of the whole memory at each step (yes, some people actually do this), but in
Catel it is done a bit smarter. For each possible action type, there is an implementation of the UndoBase. This way, each action will know by itself
how to undo or redo. Catel offers the following default implementations:
PropertyChangeUndo
CollectionChangeUndo
ActionUndo
If there are more actions supported, it is possible to write a custom UndoBase implementation and add the specific action to the IMementoService
It is possible to check whether it is possible to undo actions by using the CanUndo property. This check is not required since the Undo method will
also check this internally.
To redo an action, use the code below:
It is possible to check whether it is possible to redo actions by using the CanRedo property. This check is not required since the Redo method will
also check this internally.
Note that the Title and Description are optional. They are however a great way to represent the batches in the user interface
[IgnoreMementoSupport]
public string IgnoredProperty { get; set; }
Note that all actions should be implemented, such as adding, replacing, removing and resetting to fully support undo/redo
Note that unregistering a collection will both cancel change notifications and remove the actions that belong to this collection from the
undo/redo stack
As you can see in the example, it is not possible to use the PropertyChangeUndo because the property has no setter and no change notification.
So, we will create custom actions for undo/redo.
First, the class with only undo support:
The code above will add a new action to the undo stack every time the IncreaseNumber method is called. Then, it will not add it to the redo stack
because redo is not possible (we haven't provided a redo action).
Below is the same class, but now with redo support:
The code above will add a new action to the undo stack every time the IncreaseNumber method is called. Then, when an action is undo-ed, the
action is added to the redo stack and it is possible to redo the action because the redo action was provided as well.
Note that unregistering an object will both cancel change notifications and remove the actions that belong to this object from the
undo/redo stack
Catel.Extensions.Prism
Table of contents:
Making the region manager available
Activating a view into a specific region
Dealing with more than one and only one shell
Deactivating a view
Deactivating a view automatically
More information:
Using the bootstrapper
Declaring modules
Module catalogs
Translating or customizing the initialization task messages
The latest version of the guidance, Prism, includes a feature named "User Interface Composition". Basically it allows build a mosaic like
application by loading multiple views that comes from different modules into an active regions exposed by a control, also know as the shell.
But all this is about view models. Therefore, the Catel team decide to introduce an extension that implements a good starting point in order create
a composite user interface without actually referencing any view. At this point, you must have heared about of the UIVisualizerService, and the
way it can resolve a view from an instance of a view model. So, we are please to introduce you the UICompositionService.
but if you use ServiceLocator primary IoC contanier, and your Bootstrapper class inherits from BootstrapperBase the region manager is actually
available and you don't have to write the synchronization container code.
Since Catel 3.2 ServiceLocator support Dependency injection, therefore now you are able write Prism base application without the
usage of a third party container.
Since Catel 4.0 UICompositionService is available for Prism. Use this service instead UIVisualizerService extension methods.
But if the context allows you to know the instance of the view model of the view owner of the region where you will inject a view, is strongly
recommended use the Activate overload that allows you to specify the parent view-model. This call will enforce the view models parent-child
relationship:
Assume that this references an instance of the view-model of a view with MainRegion region specified, so this is used as parent view
model reference in the previous call.
Deactivating a view
To deactivate a view, use the following code:
uiCompositionService.Deactivate(viewModel);
If you keep your view model alive (see: Keeping view models alive), you can reactivate a deactivated the view using Activate method without
specify the region name.
/// <summary>
/// The bootstrapper that will create and run the shell.
/// </summary>
public class MyApplicationBootstrapper : BootstrapperBase
{
/// <summary>
/// Initializes a new instance of the <see cref="MyApplicationBootstrapper"/> class.
/// </summary>
public MyApplicationBootstrapper()
{
LogManager.RegisterDebugListener();
}
/// <summary>
/// Creates the <see cref="T:Microsoft.Practices.Prism.Modularity.IModuleCatalog"/>
used by Prism.
/// </summary>
/// <returns></returns>
protected override IModuleCatalog CreateModuleCatalog()
{
var moduleCatalog = new DirectoryModuleCatalog { ModulePath = @".\Modules"};
moduleCatalog.Initialize();
return moduleCatalog;
}
/// <summary>
/// Creates the shell or main window of the application.
/// </summary>
/// <returns>
/// The shell of the application.
/// </returns>
protected override DependencyObject CreateShell()
{
var shell = new MainWindow();
shell.Show();
return shell;
}
}
/// <summary>
/// The bootstrapper that will create and run the shell.
/// </summary>
public class MyApplicationBootstrapper : BootstrapperBase<MainWindow>
{
/// <summary>
/// Initializes a new instance of the <see cref="MyApplicationBootstrapper"/>
class.
/// </summary>
public MyApplicationBootstrapper()
{
LogManager.RegisterDebugListener();
}
/// <summary>
/// Creates the <see
cref="T:Microsoft.Practices.Prism.Modularity.IModuleCatalog"/> used by Prism.
/// </summary>
/// <returns></returns>
protected override IModuleCatalog CreateModuleCatalog()
{
var moduleCatalog = new DirectoryModuleCatalog { ModulePath = @".\Modules"};
moduleCatalog.Initialize();
return moduleCatalog;
}
}
Declaring modules
When developing a module in Prism, most of the time you need a base class for all the models that contain the IoC container, IRegionManager an
d more. To make development with prism and Catel as easy as possible, the ModuleBase is included with Catel. There are two versions of the Mo
duleBase available, which are described below.
Generic ModuleBase
The generic module base allows the end-developer to decide what IoC container is used. For example, the developer has the choice whether the
ServiceLocator or any other IoC container such as Unity or MEF is used.
Defining a module
Defining a module is very simple. Just create a new class with the ModuleBase as base class.
Non-generic ModuleBase
The non-generic base uses the ServiceLocator as default IoC container. This class derives from the generic module base and specifies the Servic
eLocator as IoC container.
Module catalogs
Catel ships with different module catalog implementations.
CompositeModuleCatalog
DownloadingModuleCatalog
NuGetBasedModuleCatalog
SafeDirectoryModuleCatalog
CompositeModuleCatalog
The CompositeModuleCatalog is a catalog that can combine different types of catalogs.
Initializing the catalog
Customizing the catalog
DownloadingModuleCatalog
The DownloadingModuleCatalog allow the downloading of all modules dynamically based on the module info.
Initializing the catalog
Customizing the catalog
NuGetBasedModuleCatalog
The NuGetBasedModuleCatalog allows a user to use a NuGet package source as a module source.
NuGetModuleCatalog is not supported into Silverlight
Note that the CompositeNuGetBasedModuleCatalog works the same but can combine multiple NuGet sources
itializationMode property.
ModuleCatalog.DefaultInitializationMode = InitializationMode.WhenAvailable;
Creating a module
To prevent the catalog to consider all packages on a source as a module, there is a naming convention required for the modules. In the nuspec fil
e, make sure to use the following description element:
For example:
It is possible to manually add a module, then the naming convention is not required
<description>ModuleName=Catel.Examples.WPF.Prism.Modules.NuGetBasedModuleC.NuGetBasedM
oduleC;
ModuleType=Catel.Examples.WPF.Prism.Modules.NuGetBasedModuleC.NuGetBasedModuleC,
Catel.Examples.WPF.Prism.Modules.NuGetBasedModuleC; DependsOn={
Catel.Examples.WPF.Prism.Modules.NuGetBasedModuleB.NuGetBasedModuleB }<description>
Or you can specify libraries dependencies using the dependencies section just as regular nuspec file.
<dependencies>
<dependency id="Newtonsoft.Json" version="6.0.8" />
</dependencies>
http://go.microsoft.com/fwlink/?LinkId=155569
for more information
<runtime>
<loadFromRemoteSources enabled="true"/>
</runtime>
SafeDirectoryModuleCatalog
The SafeDirectoryModuleCatalog is a safe implementation of the DirectoryModuleCatalog that ships with prism.
Initializing the catalog
Customizing the catalog
Note that this example only customizes a single action. To customize all the actions, override all methods
Catel.Extensions.Wcf.Server
Documentation must be written here
Catel.Fody
Fody is an extensible tool for weaving .NET assemblies. For more information about Fody, see its official website.
Enabling Catel.Fody
Available functionality
Disabling weaving for specific types or properties
Configuring Catel.Fody
Enabling Catel.Fody
To enable Catel.Fody to weave assemblies, you need to perform the following steps:
Install the Catel.Fody NuGet package
Update FodyWeavers.xml and make sure it contains <Catel />
Note that the FodyWeavers.xml should be updated automatically when you install the package
Available functionality
Below are the several options available in Catel.Fody:
Weaving properties
Weaving argument checks
Exposing properties on view models
XmlSchema generation
[NoWeaving]
public class MyClass : ModelBase
{
...
}
Configuring Catel.Fody
Though we recommend to leave the default settings (great for most people), it is possible to configure the weaver. Below is a list of options that
can be configured.
Property
WeaveProperties
Default
value
true
Description
Weave all regular properties on classes that inherit (directly or indirectly) from Catel.Data.ModelBase
into Catel properties.
WeaveExposedProperties
true
Weave all Catel properties decorated with both the Catel.MVVM.Model attribute and Fody.Expose att
ribute as automatic mappings.
WeaveArguments
true
WeaveLogging
true
GenerateXmlSchemas
false
Generate xml schemas for all classes that inherit (directly or indirectly) from Catel.Data.ModelBase.
To configure an option, modify FodyWeavers.xml by adding the property and value to the Catel element. For example, the example below will
disable argument and logging weaving:
Weaving properties
Support for computed properties
Automatically excluded properties
Specifying default values for weaved properties
How to get automatic change notifications
The PropertyChanged.Fody plugin for Fody already supports Catel out of the box, but only for property change notifications. However, with the
Catel.Fody plugin, it is possible to automatically weave a simple property into a Catel property.
The following property definition:
Note that this feature is automatically disabled for classes that already override the OnPropertyChanged method. It is too complex to
determine where the logic should be added so the end-developer is responsible for implementing this feature when overriding OnProper
tyChanged
Argument.IsNotNull("myString", myString);
This is much faster because the expression doesn't have to be parsed at runtime. This is a very noticeable performance boost if the expression
check is used more than 50 times per second.
When using the latest version of Catel.Fody, the team recommends using expressions above the regular argument checks (with name
and value specified separately) because it will result in cleaner code. With this feature, there is no longer a performance penalty for
using the expressions version
Usage example
The following method definition:
Argument method
NotNull
Argument.IsNotNull
NotNullOrEmptyArray
Argument.IsNotNullOrEmptyArray
NotNullOrEmpty
Argument.IsNotNullOrEmpty
NotNullOrWhitespace
Argument.IsNotNullOrWhitespace
Match
Argument.IsMatch
NotMatch
Argument.IsNotMatch
NotOutOfRange
Argument.IsNotOutOfRange
Maximum
Argument.IsMaximum
Minimal
Argument.IsMinimal
OfType
Argument.IsOfType
ImplementsInterface
Argument.ImplementsInterface
InheritsFromAttribute
Argument.InheritsFrom
The Catel version for WPF provides the Expose attribute. Starting with Catel 3.8, this feature has been moved to Fody to support all platforms
instead of just WPF. One of the features that already existed in Catel before is the ViewModelToModelAttribute. The goal of these attributes is to
easily map properties from a model to the view model so as much of the plumbing (setting/getting properties, rechecking validation, etc) is done
automatically for the developer.
Using the ViewModelToModelAttribute, this is the syntax to map properties automatically:
/// <summary>
/// Gets or sets the person.
/// </summary>
[Model]
public Person Person
{
get { return GetValue<Person>(PersonProperty); }
private set { SetValue(PersonProperty, value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(Person));
/// <summary>
/// Gets or sets the first name.
/// </summary>
[ViewModelToModel("Person")]
public string FirstName
{
get { return GetValue<string>(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
/// <summary>
/// Register the FirstName property so it is known in the class.
/// </summary>
public static readonly PropertyData FirstNameProperty = RegisterProperty("FirstName",
typeof(string));
However, if you only define the FirstName property just to protect your model, then why should you define the whole property? This is where the E
xposeAttribute property comes in very handy. This attribute internally registers a new dynamic property on the view model, and then uses the
same technique as the ViewModelToModelAttribute.
Below is the new way you can easily expose properties of a model and protect other properties of the model from the view:
/// <summary>
/// Gets or sets the person.
/// </summary>
[Model]
[Expose("FirstName")]
[Expose("MiddleName")]
[Expose("LastName")]
private Person Person
{
get { return GetValue<Person>(PersonProperty); }
set { SetValue(PersonProperty, value); }
}
/// <summary>
/// Register the Person property so it is known in the class.
/// </summary>
public static readonly PropertyData PersonProperty = RegisterProperty("Person",
typeof(Person));
This is a very cool feature that allows you to protect your model without having to re-define all the properties on the view model. Also, the
validation in the model is automatically synchronized with the view model when you use this attribute.
XmlSchema generation
The .NET framework supports XmlSchema attributes to allow static members to define a custom schema method required for WCF serialization.
Unfortunately this cannot be implemented in Catel itself because it would required too much reflection and the method is static. Therefore this
feature included in Catel.Fody.
Starting with Catel.Fody 2.0, this feature is disabled by default. To enabled it, use the following option in FodyWeavers.xml:
When the XmlSchemaProvider is available on the target platform where Catel is used, the changes will be made to classes deriving from ModelB
ase:
1. Decorate the class with XmlSchemaProvider attribute:
[XmlSchemaProvider("GetXmlSchemaForCatelFodyTestAssemblyInheritedClass")]
public class InheritedClass : BaseClass
{
// rest of the class definition
}
[CompilerGenerated]
public sta