├── .gitattributes
├── .gitignore
├── Build
├── SimpleMvvmToolkit.Express.1.0.0-rc2.nupkg
├── SimpleMvvmToolkit.Express.1.0.0.nupkg
├── SimpleMvvmToolkit.Express.2.0.0-beta.nupkg
└── SimpleMvvmToolkit.Express.2.0.0.nupkg
├── Images
├── simple-icon-sm.png
├── simple-icon.ico
├── simple-icon.jpg
├── simple-icon.png
├── simple-logo.jpg
├── simple-logo.png
└── simple-preview.png
├── LICENSE
├── README.md
├── Snippets
├── Code Snippets
│ ├── mvvmcommand.snippet
│ ├── mvvmcommandcanex.snippet
│ └── mvvmprop.snippet
├── ReadMe.md
└── Xml Snippets
│ ├── mvvmcontext.snippet
│ ├── mvvmtrigger.snippet
│ └── mvvmxmlns.snippet
└── Src
├── SimpleMvvmToolkit.Express.sln
└── SimpleMvvmToolkit.Express
├── DelegateCommand.cs
├── GenericDelegateCommand.cs
├── INotifyable.cs
├── MessageBus.cs
├── MessageBusProxy.cs
├── ModelBase.cs
├── ModelExtensions.cs
├── NotificationEventArgs.cs
├── Properties
└── AssemblyInfo.cs
├── SimpleMvvmToolkit.Express.csproj
├── ViewModelBase.cs
└── ViewModelDetailBase.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.doc diff=astextplain
2 | *.DOC diff=astextplain
3 | *.docx diff=astextplain
4 | *.DOCX diff=astextplain
5 | *.dot diff=astextplain
6 | *.DOT diff=astextplain
7 | *.pdf diff=astextplain
8 | *.PDF diff=astextplain
9 | *.rtf diff=astextplain
10 | *.RTF diff=astextplain
11 |
12 | *.jpg binary
13 | *.png binary
14 | *.gif binary
15 | *.bson binary
16 |
17 | *.cs text diff=csharp
18 | *.vb text
19 | *.c text
20 | *.cpp text
21 | *.cxx text
22 | *.h text
23 | *.hxx text
24 | *.py text
25 | *.rb text
26 | *.java text
27 | *.html text
28 | *.htm text
29 | *.css text
30 | *.scss text
31 | *.sass text
32 | *.less text
33 | *.js text
34 | *.lisp text
35 | *.clj text
36 | *.sql text
37 | *.php text
38 | *.lua text
39 | *.m text
40 | *.asm text
41 | *.erl text
42 | *.fs text
43 | *.fsx text
44 | *.hs text
45 | *.ps1 text
46 | *.psm1 text
47 |
48 | *.csproj text merge=union
49 | *.vbproj text merge=union
50 | *.fsproj text merge=union
51 | *.dbproj text merge=union
52 | *.sln text eol=crlf merge=union
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | # NUNIT
34 | *.VisualState.xml
35 | TestResult.xml
36 |
37 | # Build Results of an ATL Project
38 | [Dd]ebugPS/
39 | [Rr]eleasePS/
40 | dlldata.c
41 |
42 | # DNX
43 | project.lock.json
44 | artifacts/
45 |
46 | *_i.c
47 | *_p.c
48 | *_i.h
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.pch
53 | *.pdb
54 | *.pgc
55 | *.pgd
56 | *.rsp
57 | *.sbr
58 | *.tlb
59 | *.tli
60 | *.tlh
61 | *.tmp
62 | *.tmp_proj
63 | *.log
64 | *.vspscc
65 | *.vssscc
66 | .builds
67 | *.pidb
68 | *.svclog
69 | *.scc
70 |
71 | # Chutzpah Test files
72 | _Chutzpah*
73 |
74 | # Visual C++ cache files
75 | ipch/
76 | *.aps
77 | *.ncb
78 | *.opendb
79 | *.opensdf
80 | *.sdf
81 | *.cachefile
82 |
83 | # Visual Studio profiler
84 | *.psess
85 | *.vsp
86 | *.vspx
87 | *.sap
88 |
89 | # TFS 2012 Local Workspace
90 | $tf/
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 | *.DotSettings.user
99 |
100 | # JustCode is a .NET coding add-in
101 | .JustCode
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | _NCrunch_*
111 | .*crunch*.local.xml
112 | nCrunchTemp_*
113 |
114 | # MightyMoose
115 | *.mm.*
116 | AutoTest.Net/
117 |
118 | # Web workbench (sass)
119 | .sass-cache/
120 |
121 | # Installshield output folder
122 | [Ee]xpress/
123 |
124 | # DocProject is a documentation generator add-in
125 | DocProject/buildhelp/
126 | DocProject/Help/*.HxT
127 | DocProject/Help/*.HxC
128 | DocProject/Help/*.hhc
129 | DocProject/Help/*.hhk
130 | DocProject/Help/*.hhp
131 | DocProject/Help/Html2
132 | DocProject/Help/html
133 |
134 | # Click-Once directory
135 | publish/
136 |
137 | # Publish Web Output
138 | *.[Pp]ublish.xml
139 | *.azurePubxml
140 | # TODO: Comment the next line if you want to checkin your web deploy settings
141 | # but database connection strings (with potential passwords) will be unencrypted
142 | *.pubxml
143 | *.publishproj
144 |
145 | # NuGet Packages
146 | *.nupkg
147 | # The packages folder can be ignored because of Package Restore
148 | **/packages/*
149 | # except build/, which is used as an MSBuild target.
150 | !**/packages/build/
151 | # Uncomment if necessary however generally it will be regenerated when needed
152 | #!**/packages/repositories.config
153 | # NuGet v3's project.json files produces more ignoreable files
154 | *.nuget.props
155 | *.nuget.targets
156 |
157 | # Microsoft Azure Build Output
158 | csx/
159 | *.build.csdef
160 |
161 | # Microsoft Azure Emulator
162 | ecf/
163 | rcf/
164 |
165 | # Microsoft Azure ApplicationInsights config file
166 | ApplicationInsights.config
167 |
168 | # Windows Store app package directory
169 | AppPackages/
170 | BundleArtifacts/
171 |
172 | # Visual Studio cache files
173 | # files ending in .cache can be ignored
174 | *.[Cc]ache
175 | # but keep track of directories ending in .cache
176 | !*.[Cc]ache/
177 |
178 | # Others
179 | ClientBin/
180 | ~$*
181 | *~
182 | *.dbmdl
183 | *.dbproj.schemaview
184 | *.pfx
185 | *.publishsettings
186 | node_modules/
187 | orleans.codegen.cs
188 |
189 | # RIA/Silverlight projects
190 | Generated_Code/
191 |
192 | # Backup & report files from converting an old project file
193 | # to a newer Visual Studio version. Backup files are not needed,
194 | # because we have git ;-)
195 | _UpgradeReport_Files/
196 | Backup*/
197 | UpgradeLog*.XML
198 | UpgradeLog*.htm
199 |
200 | # SQL Server files
201 | *.mdf
202 | *.ldf
203 |
204 | # Business Intelligence projects
205 | *.rdl.data
206 | *.bim.layout
207 | *.bim_*.settings
208 |
209 | # Microsoft Fakes
210 | FakesAssemblies/
211 |
212 | # GhostDoc plugin setting file
213 | *.GhostDoc.xml
214 |
215 | # Node.js Tools for Visual Studio
216 | .ntvs_analysis.dat
217 |
218 | # Visual Studio 6 build log
219 | *.plg
220 |
221 | # Visual Studio 6 workspace options file
222 | *.opt
223 |
224 | # Visual Studio LightSwitch build output
225 | **/*.HTMLClient/GeneratedArtifacts
226 | **/*.DesktopClient/GeneratedArtifacts
227 | **/*.DesktopClient/ModelManifest.xml
228 | **/*.Server/GeneratedArtifacts
229 | **/*.Server/ModelManifest.xml
230 | _Pvt_Extensions
231 |
232 | # Paket dependency manager
233 | .paket/paket.exe
234 |
235 | # FAKE - F# Make
236 | .fake/
237 |
--------------------------------------------------------------------------------
/Build/SimpleMvvmToolkit.Express.1.0.0-rc2.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Build/SimpleMvvmToolkit.Express.1.0.0-rc2.nupkg
--------------------------------------------------------------------------------
/Build/SimpleMvvmToolkit.Express.1.0.0.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Build/SimpleMvvmToolkit.Express.1.0.0.nupkg
--------------------------------------------------------------------------------
/Build/SimpleMvvmToolkit.Express.2.0.0-beta.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Build/SimpleMvvmToolkit.Express.2.0.0-beta.nupkg
--------------------------------------------------------------------------------
/Build/SimpleMvvmToolkit.Express.2.0.0.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Build/SimpleMvvmToolkit.Express.2.0.0.nupkg
--------------------------------------------------------------------------------
/Images/simple-icon-sm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Images/simple-icon-sm.png
--------------------------------------------------------------------------------
/Images/simple-icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Images/simple-icon.ico
--------------------------------------------------------------------------------
/Images/simple-icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Images/simple-icon.jpg
--------------------------------------------------------------------------------
/Images/simple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Images/simple-icon.png
--------------------------------------------------------------------------------
/Images/simple-logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Images/simple-logo.jpg
--------------------------------------------------------------------------------
/Images/simple-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Images/simple-logo.png
--------------------------------------------------------------------------------
/Images/simple-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SimpleMvvm/SimpleMvvmToolkit.Express/c71073d7a3e61e2de925819b1eeb57d4b4b4a189/Images/simple-preview.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Tony Sneed
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Simple MVVM Toolkit Express
2 |
3 | ## Streamlined version of Simple MVVM Toolkit targeting the .NET Standard 2.0
4 |
5 | This library is compatible with the [.NET Standard](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) 2.0 specification, which supports the following platforms (or higher):
6 | - **.NET Framework 4.6.1**
7 | - **.NET Core 2.0**
8 | - **Mono 5.4**
9 | - **Xamarin.iOS 10.14**
10 | - **Xamarin.Mac 3.8**
11 | - **Xamarin.Android 8.0**
12 | - **Universal Windows Platform 10.0.16299**
13 |
14 | The following platforms will **not** be supported:
15 | - *Windows 8.1*
16 | - *Windows Phone 8.1*
17 | - *Windows Phone Silverlight*
18 | - *.NET 4.0 or earlier*
19 | - *Silverlight*
20 |
21 | If you require support for any of these platforms, please use the prior version of Simple MVVM Toolkit.
22 |
23 | Because cross-threading concerns are generally handled by `Task` and `async/await`, threading support will be removed.
24 | This will allow removal of platform-specific implementations that were required for cross-thread marshalling.
25 |
26 | Object cloning will be implemented using **Json.Net**, rather than Data Contract Serializer.
27 | The Json.Net serializer is configured *programmatically* to handle cyclical references.
28 |
--------------------------------------------------------------------------------
/Snippets/Code Snippets/mvvmcommand.snippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Expansion
7 |
8 | Command Property
9 | Tony Sneed
10 | Command Property
11 |
12 |
13 | mvvmcommand
14 |
15 |
16 |
17 |
18 | ParameterName
19 | Parameter Name
20 | <object>
21 |
22 |
23 |
24 |
25 | FieldName
26 | Field Name
27 | myMethod
28 |
29 |
30 |
31 |
32 | MethodName
33 | Method Name
34 | MyMethod
35 |
36 |
37 |
38 |
39 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Snippets/Code Snippets/mvvmcommandcanex.snippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Expansion
7 |
8 | Command Property With CanExecute
9 | Tony Sneed
10 | Command Property With CanExecute
11 |
12 |
13 | mvvmcommandcanex
14 |
15 |
16 |
17 |
18 | ParameterName
19 | Parameter Name
20 | object
21 |
22 |
23 |
24 |
25 | FieldName
26 | Field Name
27 | myMethod
28 |
29 |
30 |
31 |
32 | MethodName
33 | Method Name
34 | MyMethod
35 |
36 |
37 |
38 |
39 | $FieldName$Command;
40 | public DelegateCommand<$ParameterName$> $MethodName$Command
41 | {
42 | get
43 | {
44 | if ($FieldName$Command == null) $FieldName$Command =
45 | new DelegateCommand<$ParameterName$>($MethodName$, Can$MethodName$);
46 | return $FieldName$Command;
47 | }
48 | private set { $FieldName$Command = value; }
49 | }]]>
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Snippets/Code Snippets/mvvmprop.snippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Expansion
7 |
8 | Bindable Model or View-Model Property
9 | Tony Sneed
10 | Bindable Model or ViewModel Property
11 |
12 |
13 | mvvmprop
14 |
15 |
16 |
17 |
18 | type
19 | Property Type
20 | int
21 |
22 |
23 |
24 |
25 | field
26 | Private Field
27 | _myProperty
28 |
29 |
30 |
31 |
32 | m
33 | Lambda Parameter
34 | m
35 |
36 |
37 |
38 |
39 | property
40 | Property Name
41 | MyProperty
42 |
43 |
44 |
45 |
46 | $m$.$property$);
54 | }
55 | }]]>
56 |
57 |
58 |
--------------------------------------------------------------------------------
/Snippets/ReadMe.md:
--------------------------------------------------------------------------------
1 | # Simple MVVM Toolkit Express: Code Snippets
2 |
3 | *Instructions for installing the code snippets for Simple MVVM Toolkit Express*
4 |
5 | 1. In Visual Studio 2015, select **Tools**, **Code Snippets Manager**
6 |
7 | 2. Select the **CSharp** language
8 | - Click the *Add* button to add the C# code snippets
9 |
10 | 3. Select the **XAML** language
11 | - Click the *Add* button to add the XML snippets
12 |
--------------------------------------------------------------------------------
/Snippets/Xml Snippets/mvvmcontext.snippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Expansion
7 |
8 | mvvmcontext
9 | Tony Sneed
10 | DataContext for ViewModel
11 |
12 |
13 | mvvmcontext
14 |
15 |
16 |
17 |
18 |
19 |
20 | MyViewModel
21 | ViewModel Name
22 | MyViewModel
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Snippets/Xml Snippets/mvvmtrigger.snippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Expansion
7 |
8 | mvvmtrigger
9 | Tony
10 | Insert Blend event trigger / action
11 |
12 |
13 | mvvmtrigger
14 |
15 |
16 |
17 |
18 |
19 |
20 | event_name
21 | Event Name
22 | Click
23 |
24 |
25 |
26 |
27 | method_name
28 | Method Name
29 | MyMethod
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
41 |
42 | $end$]]>
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Snippets/Xml Snippets/mvvmxmlns.snippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Expansion
7 |
8 | mvvmxmlns
9 | Tony Sneed
10 | XML namespaces for Blend behaviors
11 |
12 |
13 | mvvmxmlns
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Src/SimpleMvvmToolkit.Express.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29318.209
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3A1CFEC2-818F-4257-BE74-500887B1A9D9}"
7 | ProjectSection(SolutionItems) = preProject
8 | ..\README.md = ..\README.md
9 | EndProjectSection
10 | EndProject
11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleMvvmToolkit.Express", "SimpleMvvmToolkit.Express\SimpleMvvmToolkit.Express.csproj", "{8073FF79-D552-4C54-A27C-2467B991760F}"
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {8073FF79-D552-4C54-A27C-2467B991760F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {8073FF79-D552-4C54-A27C-2467B991760F}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {8073FF79-D552-4C54-A27C-2467B991760F}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {8073FF79-D552-4C54-A27C-2467B991760F}.Release|Any CPU.Build.0 = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {3644FA0E-1919-4C9C-BD08-2B6B8064D754}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/Src/SimpleMvvmToolkit.Express/DelegateCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Input;
3 |
4 | namespace SimpleMvvmToolkit.Express
5 | {
6 | ///
7 | /// Provide a command that can bind to ButtonBase.Command
8 | /// without accepting command parameters for Execute and CanExecute.
9 | ///
10 | public class DelegateCommand : ICommand
11 | {
12 | private readonly Func _canExecute;
13 | private readonly Action _executeAction;
14 |
15 | ///
16 | /// Event fired when CanExecute changes.
17 | ///
18 | public event EventHandler CanExecuteChanged;
19 |
20 | ///
21 | /// Constructor
22 | ///
23 | /// Action to be executed.
24 | /// Flag indicating whether action can be executed.
25 | public DelegateCommand(Action executeAction,
26 | Func canExecute)
27 | {
28 | _executeAction = executeAction;
29 | _canExecute = canExecute;
30 | }
31 |
32 | ///
33 | /// DelegateCommand constructor.
34 | ///
35 | /// Action to be executed.
36 | public DelegateCommand(Action executeAction)
37 | {
38 | _executeAction = executeAction;
39 | _canExecute = () => true;
40 | }
41 |
42 | ///
43 | /// Method to fire CanExecuteChanged event.
44 | ///
45 | public void RaiseCanExecuteChanged()
46 | {
47 | CanExecuteChanged?.Invoke(this, EventArgs.Empty);
48 | }
49 |
50 | ///
51 | /// Flag indicating whether action can be executed.
52 | ///
53 | /// Argument for action.
54 | /// True if action can be executed.
55 | public bool CanExecute(object parameter)
56 | {
57 | return _canExecute == null || _canExecute();
58 | }
59 |
60 | ///
61 | /// Execute action.
62 | ///
63 | /// Argument for action.
64 | public void Execute(object parameter)
65 | {
66 | _executeAction();
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Src/SimpleMvvmToolkit.Express/GenericDelegateCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 | using System.Windows.Input;
5 |
6 | namespace SimpleMvvmToolkit.Express
7 | {
8 | ///
9 | /// Provide a command that can bind to ButtonBase.Command
10 | /// and accept command parameters for Execute and CanExecute.
11 | ///
12 | ///
13 | public class DelegateCommand : ICommand
14 | {
15 | private readonly Func _canExecute;
16 | private readonly Action _executeAction;
17 |
18 | ///
19 | /// Notification for when CanExecute property has changed.
20 | ///
21 | public event EventHandler CanExecuteChanged;
22 |
23 | ///
24 | /// DelegateCommand constructor.
25 | ///
26 | /// Action to be executed.
27 | /// Flag indicating whether action can be executed.
28 | public DelegateCommand(Action executeAction,
29 | Func canExecute)
30 | {
31 | _executeAction = executeAction;
32 | _canExecute = canExecute;
33 | }
34 |
35 | ///
36 | /// DelegateCommand constructor.
37 | ///
38 | /// Action to be executed.
39 | public DelegateCommand(Action executeAction)
40 | {
41 | _executeAction = executeAction;
42 | _canExecute = x => true;
43 | }
44 |
45 | ///
46 | /// Method to fire CanExecuteChanged event.
47 | ///
48 | public void RaiseCanExecuteChanged()
49 | {
50 | CanExecuteChanged?.Invoke(this, EventArgs.Empty);
51 | }
52 |
53 | ///
54 | /// Flag indicating whether action can be executed.
55 | ///
56 | /// Argument for action.
57 | /// True if action can be executed.
58 | public bool CanExecute(object parameter)
59 | {
60 | if (parameter == null) return true;
61 | T param = ConvertParameter(parameter);
62 | return _canExecute == null || _canExecute(param);
63 | }
64 |
65 | ///
66 | /// Execute action.
67 | ///
68 | /// Argument for action.
69 | public void Execute(object parameter)
70 | {
71 | T param = ConvertParameter(parameter);
72 | _executeAction(param);
73 | }
74 |
75 | // Convert parameter to expected type, parsing if necessary
76 | private T ConvertParameter(object parameter)
77 | {
78 | if (parameter == null) return default(T);
79 |
80 | string exceptionMessage = $"Cannot convert {parameter.GetType()} to {typeof(T)}";
81 |
82 | T result = default(T);
83 | if (parameter is T)
84 | {
85 | result = (T)parameter;
86 | }
87 | else if (parameter is string)
88 | {
89 | MethodInfo mi = (from m in typeof(T).GetRuntimeMethods()
90 | where m.IsPublic && m.IsStatic &&
91 | m.Name == "Parse" && m.GetParameters().Count() == 1
92 | select m).FirstOrDefault();
93 | if (mi != null)
94 | {
95 | try
96 | {
97 | result = (T)mi.Invoke(null, new object[] { parameter });
98 | }
99 | catch (Exception ex)
100 | {
101 | if (ex.InnerException != null) throw ex.InnerException;
102 | throw new InvalidCastException(exceptionMessage);
103 | }
104 | }
105 | }
106 | else
107 | {
108 | throw new InvalidCastException(exceptionMessage);
109 | }
110 | return result;
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Src/SimpleMvvmToolkit.Express/INotifyable.cs:
--------------------------------------------------------------------------------
1 | namespace SimpleMvvmToolkit.Express
2 | {
3 | ///
4 | /// Implemented by MessageBusProxy so that it can send notifications in a leak-proof manner.
5 | ///
6 | public interface INotifyable
7 | {
8 | ///
9 | /// Notify registered callbacks.
10 | ///
11 | /// String identifying a message token
12 | /// Sender of notification
13 | /// Event args carrying message
14 | void Notify(string token, object sender, NotificationEventArgs e);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Src/SimpleMvvmToolkit.Express/MessageBus.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace SimpleMvvmToolkit.Express
8 | {
9 | ///
10 | /// Facilitates communication among view-models.
11 | /// To prevent memory leaks weak references are used.
12 | ///
13 | public class MessageBus
14 | {
15 | private static MessageBus _instance;
16 | private static readonly object StaticLock = new object();
17 |
18 | ///
19 | /// Singleton of MessageBus.
20 | ///
21 | public static MessageBus Default
22 | {
23 | get
24 | {
25 | if (_instance == null)
26 | {
27 | CreateInstance();
28 | }
29 | return _instance;
30 | }
31 | }
32 |
33 | private static void CreateInstance()
34 | {
35 | lock (StaticLock)
36 | {
37 | if (_instance == null)
38 | {
39 | _instance = new MessageBus();
40 | }
41 | }
42 | }
43 |
44 | // Provide thread-safe access to subscriptions
45 | private readonly object _sharedLock = new object();
46 |
47 | // Each token can have multiple subscriptions
48 | private readonly Dictionary> _subscriptions =
49 | new Dictionary>();
50 |
51 | ///
52 | /// Register subscriber using a string token, which is usually defined as a constant.
53 | /// Subscriber performs internal notifications.
54 | ///
55 | /// String identifying a message token
56 | /// Subscriber requesting notifications
57 | public void Register(string token, INotifyable subscriber)
58 | {
59 | lock (_sharedLock)
60 | {
61 | // Add token if not present
62 | if (!_subscriptions.ContainsKey(token))
63 | {
64 | _subscriptions.Add(token, new List { new WeakReference(subscriber) });
65 | return;
66 | }
67 |
68 | // Get subscribers for this token
69 | List weakSubscribers;
70 | if (_subscriptions.TryGetValue(token, out weakSubscribers))
71 | {
72 | // See if subcriber is already present
73 | var existing = (from w in weakSubscribers
74 | where w != null && w.IsAlive &&
75 | ReferenceEquals(w.Target, subscriber)
76 | select w).SingleOrDefault();
77 |
78 | // Add if subcriber is already present
79 | if (existing == null)
80 | _subscriptions[token].Add(new WeakReference(subscriber));
81 | }
82 | }
83 | }
84 |
85 | ///
86 | /// Remove subscriber from the invocation list
87 | ///
88 | /// String identifying a message token
89 | /// Subscriber to remove from notifications
90 | public void Unregister(string token, INotifyable subscriber)
91 | {
92 | lock (_sharedLock)
93 | {
94 | List weakSubscribers;
95 | if (_subscriptions.TryGetValue(token, out weakSubscribers))
96 | {
97 | // Find subscriber
98 | WeakReference weakSubscriber = weakSubscribers
99 | .SingleOrDefault(w => w.IsAlive && ReferenceEquals
100 | (w.Target, subscriber));
101 |
102 | // Remove subscriber
103 | if (weakSubscriber != null)
104 | weakSubscribers.Remove(weakSubscriber);
105 |
106 | // Remove dictionary entry if no subscibers left
107 | if (_subscriptions[token].Count == 0) _subscriptions.Remove(token);
108 | }
109 | }
110 | }
111 |
112 | ///
113 | /// Notify registered subscribers.
114 | ///
115 | /// String identifying a message token
116 | /// Sender of notification
117 | /// Event args carrying message
118 | public void Notify(string token, object sender, NotificationEventArgs e)
119 | {
120 | // Notify subscriber on UI thread
121 | InternalNotify(token, sender, e, true);
122 | }
123 |
124 | ///
125 | /// Notify registered subscribers.
126 | ///
127 | /// Type used by notifier to send data
128 | /// String identifying a message token
129 | /// Sender of notification
130 | /// Event args carrying message
131 | public void Notify(string token, object sender,
132 | NotificationEventArgs e)
133 | {
134 | // Notify subscriber on UI thread
135 | InternalNotify(token, sender, e, true);
136 | }
137 |
138 | ///
139 | /// Notify registered subscribers.
140 | ///
141 | /// Type used by notifier to send data
142 | /// Type sent by subscriber to send data back to notifier
143 | /// String identifying a message token
144 | /// Sender of notification
145 | /// Event args carrying message
146 | public void Notify(string token, object sender,
147 | NotificationEventArgs e)
148 | {
149 | // Notify subscriber on UI thread
150 | InternalNotify(token, sender, e, true);
151 | }
152 |
153 | ///
154 | /// Notify registered subscribers asynchronously.
155 | ///
156 | /// String identifying a message token
157 | /// Sender of notification
158 | /// Event args carrying message
159 | public void BeginNotify(string token, object sender, NotificationEventArgs e)
160 | {
161 | // Notify subscriber on ThreadPool thread
162 | InternalNotify(token, sender, e, false);
163 | }
164 |
165 | ///
166 | /// Notify registered subscribers asynchronously.
167 | ///
168 | /// Type used by notifier to send data
169 | /// String identifying a message token
170 | /// Sender of notification
171 | /// Event args carrying message
172 | public void BeginNotify(string token, object sender,
173 | NotificationEventArgs e)
174 | {
175 | // Notify subscriber on ThreadPool thread
176 | InternalNotify(token, sender, e, false);
177 | }
178 |
179 | ///
180 | /// Notify registered subscribers asynchronously.
181 | ///
182 | /// Type used by notifier to send data
183 | /// Type sent by subscriber to send data back to notifier
184 | /// String identifying a message token
185 | /// Sender of notification
186 | /// Event args carrying message
187 | public void BeginNotify(string token, object sender,
188 | NotificationEventArgs e)
189 | {
190 | // Notify subscriber on ThreadPool thread
191 | InternalNotify(token, sender, e, false);
192 | }
193 |
194 | private void InternalNotify(string token, object sender,
195 | NotificationEventArgs e, bool post)
196 | {
197 | // Get weak subscribers
198 | List weakSubscribers;
199 | lock (_sharedLock)
200 | {
201 | if (!_subscriptions.TryGetValue(token, out weakSubscribers)) return;
202 |
203 | // Make a copy while locked
204 | weakSubscribers = weakSubscribers.ToList();
205 | }
206 |
207 | // Get compatible living subscribers
208 | var subscribers = from w in weakSubscribers
209 | let s = w.Target as INotifyable
210 | where w != null && w.IsAlive && s != null
211 | select s;
212 |
213 | // Invoke each callback associated with token
214 | foreach (var subscriber in subscribers)
215 | {
216 | INotifyable subscriber1 = subscriber;
217 | SafeNotify(() => subscriber1.Notify(token, sender, e));
218 | }
219 |
220 | lock (_sharedLock)
221 | {
222 | if (_subscriptions.ContainsKey(token))
223 | {
224 | // Remove subscribers who are no longer alive
225 | var deadSubscribers = weakSubscribers
226 | .Where(w => w == null || !w.IsAlive);
227 | foreach (var s in deadSubscribers)
228 | {
229 | _subscriptions[token].Remove(s);
230 | }
231 |
232 | // Remove dictionary entry if no subscibers left
233 | if (_subscriptions[token].Count == 0) _subscriptions.Remove(token);
234 | }
235 | }
236 | }
237 |
238 | private void SafeNotify(Action method)
239 | {
240 | try
241 | {
242 | Task.Run(method);
243 | }
244 | catch (Exception ex)
245 | {
246 | // If there's an exception write it to the Output window
247 | Debug.WriteLine(ex.ToString());
248 | }
249 | }
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/Src/SimpleMvvmToolkit.Express/MessageBusProxy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace SimpleMvvmToolkit.Express
6 | {
7 | ///
8 | /// Handles communication with the MessageBus.
9 | /// ViewModelBase stores this internally.
10 | ///
11 | internal class MessageBusProxy : INotifyable
12 | {
13 | // Provide thread-safe access to subscriptions
14 | private readonly object _sharedLock = new object();
15 |
16 | // Each token can have multiple callbacks
17 | private readonly Dictionary> _subscriptions =
18 | new Dictionary>();
19 |
20 | ///
21 | /// Register callback using a string token, which is usually defined as a constant
22 | ///
23 | /// String identifying a message token
24 | /// Method to invoke when notified
25 | public void Register(string token, EventHandler callback)
26 | {
27 | InternalRegister(token, callback, typeof(NotificationEventArgs));
28 | }
29 |
30 | ///
31 | /// Register callback using string token and notification with TOutgoing data
32 | ///
33 | /// Type used by notifier to send data
34 | /// String identifying a message token
35 | /// Method to invoke when notified
36 | public void Register(string token,
37 | EventHandler> callback)
38 | {
39 | InternalRegister(token, callback, typeof(NotificationEventArgs));
40 | }
41 |
42 | ///
43 | /// Register callback using string token and notification with TOutgoing data
44 | /// and the subscriber's callback with TIncoming data.
45 | ///
46 | /// Type used by notifier to send data
47 | /// Type sent by subscriber to send data back to notifier
48 | /// String identifying a message token
49 | /// Method to invoke when notified
50 | public void Register(string token,
51 | EventHandler> callback)
52 | {
53 | InternalRegister(token, callback, typeof(NotificationEventArgs));
54 | }
55 |
56 | ///
57 | /// Remove callback from the invocation list.
58 | ///
59 | /// String identifying a message token
60 | /// Method to remove from notifications
61 | public void Unregister(string token, EventHandler callback)
62 | {
63 | InternalUnregister(token, callback, typeof(NotificationEventArgs));
64 | }
65 |
66 | ///
67 | /// Remove callback from the invocation list.
68 | ///
69 | /// String identifying a message token
70 | /// Method to remove from notifications
71 | public void Unregister(string token,
72 | EventHandler> callback)
73 | {
74 | InternalUnregister(token, callback, typeof(NotificationEventArgs));
75 | }
76 |
77 | ///
78 | /// Remove callback from the invocation list.
79 | ///
80 | /// String identifying a message token
81 | /// Method to remove from notifications
82 | public void Unregister(string token,
83 | EventHandler> callback)
84 | {
85 | InternalUnregister(token, callback, typeof(NotificationEventArgs));
86 | }
87 |
88 | private void InternalUnregister(string token, Delegate callback, Type eventArgsType)
89 | {
90 | lock (_sharedLock)
91 | {
92 | if (_subscriptions.ContainsKey(token))
93 | {
94 | // Get handler with the same type
95 | Handler handler = _subscriptions[token]
96 | .SingleOrDefault(h => h.EventArgsType == eventArgsType);
97 |
98 | // If registered, remove callback from the list
99 | if (handler != null) _subscriptions[token].Remove(handler);
100 |
101 | // Remove dictionary entry if no subscibers left
102 | if (_subscriptions[token].Count == 0) _subscriptions.Remove(token);
103 | }
104 | }
105 | }
106 |
107 | private void InternalRegister(string token, Delegate callback, Type eventArgsType)
108 | {
109 | // If already registered, add callback to the list
110 | lock (_sharedLock)
111 | {
112 | if (_subscriptions.ContainsKey(token))
113 | {
114 | _subscriptions[token].Add(new Handler(callback, eventArgsType));
115 | }
116 | // Otherwise create an entry with a new callback list
117 | else
118 | {
119 | _subscriptions.Add(token, new List { new Handler(callback, eventArgsType) });
120 | }
121 | }
122 | }
123 |
124 | ///
125 | /// Notify registered callbacks.
126 | ///
127 | /// String identifying a message token
128 | /// Sender of notification
129 | /// Event args carrying message
130 | void INotifyable.Notify(string token, object sender, NotificationEventArgs e)
131 | {
132 | // Notify subscriber
133 | InternalNotify(token, sender, e);
134 | }
135 |
136 | private void InternalNotify(string token, object sender,
137 | NotificationEventArgs e)
138 | {
139 | List handlers;
140 | lock (_sharedLock)
141 | {
142 | // Return if no handlers registered
143 | if (!_subscriptions.TryGetValue(token, out handlers)) return;
144 |
145 | // Get callbacks with the same event args type
146 | handlers = handlers
147 | .Where(h => h.EventArgsType == e.GetType())
148 | .ToList();
149 | }
150 |
151 | // Invoke each callback associated with token
152 | foreach (var handler in handlers)
153 | {
154 | handler?.Callback.DynamicInvoke(sender, e);
155 | }
156 | }
157 |
158 | internal class Handler
159 | {
160 | public Delegate Callback { get; set; }
161 | public Type EventArgsType { get; set; }
162 |
163 | public Handler(Delegate callback, Type eventArgsType)
164 | {
165 | this.Callback = callback;
166 | this.EventArgsType = eventArgsType;
167 | }
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/Src/SimpleMvvmToolkit.Express/ModelBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Linq.Expressions;
6 |
7 | namespace SimpleMvvmToolkit.Express
8 | {
9 | ///
10 | /// Provides support to entities for two-way data binding and data validation.
11 | ///
12 | /// Class inheriting from ModelBase
13 | public abstract class ModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
14 | {
15 | readonly Dictionary> _errors =
16 | new Dictionary>();
17 |
18 | ///
19 | /// Allows you to specify a lambda for notify property changed.
20 | ///
21 | /// Property type
22 | /// Property for notification
23 | protected virtual void NotifyPropertyChanged
24 | (Expression> property)
25 | {
26 | // Convert expression to a property name
27 | string propertyName = ((MemberExpression)property.Body).Member.Name;
28 |
29 | // Fire PropertyChanged event
30 | NotifyPropertyChanged(propertyName);
31 | }
32 |
33 | ///
34 | /// Allows you to specify a lambda for notify errors changed.
35 | ///
36 | /// Property type
37 | /// Property for notification
38 | protected virtual void NotifyErrorsChanged
39 | (Expression> property)
40 | {
41 | // Convert expression to a property name
42 | string propertyName = ((MemberExpression)property.Body).Member.Name;
43 |
44 | // Fire PropertyChanged event
45 | NotifyErrorsChanged(propertyName);
46 | }
47 |
48 | ///
49 | /// PropertyChanged event accessible to derived classes
50 | ///
51 | public event PropertyChangedEventHandler PropertyChanged
52 | {
53 | add { PropertyChangedField += value; }
54 | remove { PropertyChangedField -= value; }
55 | }
56 |
57 | ///
58 | /// PropertyChanged field accessible to derived classes.
59 | ///
60 | protected event PropertyChangedEventHandler PropertyChangedField;
61 |
62 | ///
63 | /// Fire PropertyChanged event.
64 | ///
65 | /// Property name
66 | protected void NotifyPropertyChanged(string propertyName)
67 | {
68 | PropertyChangedField?.Invoke(this, new PropertyChangedEventArgs(propertyName));
69 | }
70 |
71 | ///
72 | /// ErrorsChanged event accessible to derived classes
73 | ///
74 | public event EventHandler ErrorsChanged
75 | {
76 | add { ErrorsChangedField += value; }
77 | remove { ErrorsChangedField -= value; }
78 | }
79 |
80 | ///
81 | /// ErrorsChanged field accessible to derived classes
82 | ///
83 | protected event EventHandler ErrorsChangedField;
84 |
85 | ///
86 | /// Enumeration for sequence of errors.
87 | ///
88 | /// Property name
89 | /// Sequence of errors.
90 | public IEnumerable GetErrors(string propertyName)
91 | {
92 | if (_errors.ContainsKey(propertyName))
93 | return _errors[propertyName];
94 | return null;
95 | }
96 |
97 | ///
98 | /// Returns true if errors list is not empty.
99 | ///
100 | public bool HasErrors => _errors.Count > 0;
101 |
102 | ///
103 | /// Add error to the list.
104 | ///
105 | /// Property name
106 | /// Validation error
107 | protected void AddError(string propertyName, string error)
108 | {
109 | _errors[propertyName] = new List { error };
110 | NotifyErrorsChanged(propertyName);
111 | }
112 |
113 | ///
114 | /// Remove error from the list.
115 | ///
116 | /// Property name
117 | protected void RemoveError(string propertyName)
118 | {
119 | if (_errors.ContainsKey(propertyName))
120 | _errors.Remove(propertyName);
121 | NotifyErrorsChanged(propertyName);
122 | }
123 |
124 | ///
125 | /// Fire ErrorsChanged event.
126 | ///
127 | /// Property name
128 | protected void NotifyErrorsChanged(string propertyName)
129 | {
130 | ErrorsChangedField?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/Src/SimpleMvvmToolkit.Express/ModelExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Runtime.Serialization;
7 | using Newtonsoft.Json;
8 |
9 | namespace SimpleMvvmToolkit.Express
10 | {
11 | ///
12 | /// Extension methods for model entities.
13 | ///
14 | public static class ModelExtensions
15 | {
16 | ///
17 | /// Performs a deep copy using Json Serializer.
18 | ///
19 | /// Entity type
20 | /// Entity object
21 | /// Cloned entity
22 | public static T Clone(this T obj)
23 | {
24 | var settings = new JsonSerializerSettings
25 | {
26 | PreserveReferencesHandling = PreserveReferencesHandling.All
27 | };
28 | var json = JsonConvert.SerializeObject(obj, settings);
29 | var result = JsonConvert.DeserializeObject(json, settings);
30 | return result;
31 | }
32 |
33 | ///
34 | /// Performs a deep copy using DatacontractSerializer.
35 | ///
36 | /// Entity type
37 | /// Entity object
38 | /// Cloned entity
39 | public static T CloneXml(this T obj)
40 | {
41 | T copy;
42 | using (var stream = new MemoryStream())
43 | {
44 | var settings = new DataContractSerializerSettings {PreserveObjectReferences = true};
45 | var ser = new DataContractSerializer(typeof(T), settings);
46 | ser.WriteObject(stream, obj);
47 | stream.Position = 0;
48 | copy = (T)ser.ReadObject(stream);
49 | }
50 | return copy;
51 | }
52 |
53 | ///
54 | /// Performs a shallow copy of all properties
55 | ///
56 | /// Entity type
57 | /// Source entity object
58 | /// Destination entity object
59 | public static void CopyValuesTo(this T source, T dest)
60 | {
61 | var properties = typeof(T).GetRuntimeProperties()
62 | .Where(p => p.CanRead && p.CanWrite);
63 |
64 | foreach (var property in properties)
65 | {
66 | if (property.SetMethod == null) continue;
67 | property.SetValue(dest, property.GetValue(source, null), null);
68 | }
69 | }
70 |
71 | ///
72 | /// Determines equality based on property hash codes.
73 | ///
74 | /// Entity type
75 | /// Source entity object
76 | /// Object to compare
77 | /// Properties excluded from comparison
78 | /// True if object properties are the same
79 | public static bool AreSame(this T source, T item, params string[] excludeProps)
80 | where T : class
81 | {
82 | int hashCode1 = GetObjectHashCode(source, excludeProps);
83 | int hashCode2 = GetObjectHashCode(item, excludeProps);
84 | return hashCode1 == hashCode2;
85 | }
86 |
87 | // Calculates object has code based on property hash codes
88 | private static int GetObjectHashCode(object item, params string[] excludeProps)
89 | {
90 | int hashCode = 0;
91 | foreach (var prop in item.GetType().GetRuntimeProperties())
92 | {
93 | if (!excludeProps.Contains(prop.Name))
94 | {
95 | object propVal = prop.GetValue(item, null);
96 | if (propVal != null)
97 | {
98 | hashCode = hashCode ^ propVal.GetHashCode();
99 | }
100 | }
101 | }
102 | return hashCode;
103 | }
104 |
105 | ///
106 | /// Convert an enum into a list of value / string pairs for
107 | /// showing in list controls
108 | ///
109 | /// Enum type
110 | /// Sequence of enum / name pairs
111 | public static IEnumerable> GetEnumItems()
112 | where TEnum : struct
113 | {
114 | // Get TEnum type and verify it's an enum
115 | var enumType = typeof(TEnum);
116 | if (!IsEnum(enumType))
117 | throw new ArgumentException($"Type {enumType.Name} is not an enum");
118 |
119 | // Project enum values and names
120 | var items = from f in enumType.GetRuntimeFields()
121 | where f.IsPublic && f.IsStatic && f.IsLiteral
122 | let val = (TEnum)f.GetValue(enumType)
123 | let name = f.Name
124 | select new EnumItem(val, name);
125 | return items;
126 | }
127 |
128 | ///
129 | /// Convert an enum into a list of value / string pairs for
130 | /// showing in list controls
131 | ///
132 | /// Enum type
133 | /// Enum base type
134 | /// Sequence of value / name pairs
135 | public static IEnumerable> GetEnumItems()
136 | where TEnum : struct
137 | where TValue : struct
138 | {
139 | // Get TEnum type and verify it's an enum
140 | var enumType = typeof(TEnum);
141 | if (!IsEnum(enumType))
142 | throw new ArgumentException($"Type {enumType.Name} is not an enum");
143 |
144 | // Verify TValue is an allowed numeric type
145 | var valType = typeof(TValue);
146 | if (!IsValidEnumBase(valType))
147 | throw new ArgumentException($"Type {enumType.Name} is not a valid enum base type");
148 |
149 | // Project enum values and names
150 | var items = from f in enumType.GetRuntimeFields()
151 | where f.IsPublic && f.IsStatic && f.IsLiteral
152 | let val = (TValue)f.GetValue(enumType)
153 | let name = f.Name
154 | select new EnumItem(val, name);
155 | return items;
156 | }
157 |
158 | private static bool IsEnum(Type enumType)
159 | {
160 | return typeof(Enum).GetTypeInfo().IsAssignableFrom(enumType.GetTypeInfo());
161 | }
162 |
163 | private static bool IsValidEnumBase(Type valType)
164 | {
165 | return valType == typeof(byte)
166 | || valType == typeof(short)
167 | || valType == typeof(int)
168 | || valType == typeof(long);
169 | }
170 | }
171 |
172 | ///
173 | /// Name-value pair for enum-based lists.
174 | ///
175 | ///
176 | public class EnumItem
177 | {
178 | ///
179 | /// Constructor.
180 | ///
181 | public EnumItem() { }
182 |
183 | ///
184 | /// Constructor.
185 | ///
186 | /// Enum value.
187 | /// Enum name.
188 | public EnumItem(TValue value, string name)
189 | {
190 | Value = value;
191 | Name = name;
192 | }
193 |
194 | ///
195 | /// Enum value.
196 | ///
197 | public TValue Value { get; private set; }
198 |
199 | ///
200 | /// Enum name.
201 | ///
202 | public string Name { get; private set; }
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/Src/SimpleMvvmToolkit.Express/NotificationEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SimpleMvvmToolkit.Express
4 | {
5 | /*
6 | * There are three levels of notifications:
7 | * NotificationEventArgs - with or without a string message
8 | * NotificationEventArgs - with outgoing data
9 | * NotificationEventArgs - with completion callback,
10 | * which can be