├── .gitignore ├── CHANGELOG.md ├── Documentation ├── logo.png └── project_structured.png ├── LICENSE.md ├── NuGet └── MvvmCodeGenerator.nuspec ├── README.md └── Sources ├── MvvmCodeGenerator.Dev ├── MvvmCodeGenMapper.xml ├── MvvmCodeGenerator.Dev.csproj └── Program.cs ├── MvvmCodeGenerator.Gen ├── Bootstrap.cs ├── Generation │ ├── CSharpGenerator.cs │ └── IGenerator.cs ├── Generators │ ├── FreshMvvmGenerator.cs │ ├── MvvmCrossGenerator.cs │ ├── MvvmLightLibsGenerator.cs │ ├── MvvmicroGenerator.cs │ └── PrismCoreGenerator.cs ├── Helpers │ ├── CommandHelper.cs │ ├── Constants.cs │ ├── FileHelper.cs │ ├── FormatterHelper.cs │ ├── PropertiesHelper.cs │ └── TypeExtensions.cs ├── MvvmCodeGenerator.Gen.csproj ├── MvvmCodeGenerator.targets ├── MvvmCodeGeneratorBaseTask.cs ├── MvvmCodeGeneratorCleanTask.cs ├── MvvmCodeGeneratorTask.cs ├── Parsing │ └── XmlParser.cs ├── Resources │ ├── Arguments.cs │ ├── Command.cs │ ├── Property.cs │ ├── ResourceFile.cs │ └── ViewModel.cs └── Settings.StyleCop ├── MvvmCodeGenerator.sln └── Xamarin.Sample ├── Models └── House.cs ├── MvvmCodeGenMapper.xml └── Xamarin.Sample.csproj /.gitignore: -------------------------------------------------------------------------------- 1 | # globs 2 | Makefile.in 3 | *.userprefs 4 | *.usertasks 5 | config.make 6 | config.status 7 | aclocal.m4 8 | install-sh 9 | autom4te.cache/ 10 | *.tar.gz 11 | tarballs/ 12 | test-results/ 13 | 14 | # Mac bundle stuff 15 | *.dmg 16 | *.app 17 | 18 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 19 | # General 20 | .DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | 24 | # Icon must end with two \r 25 | Icon 26 | 27 | 28 | # Thumbnails 29 | ._* 30 | 31 | # Files that might appear in the root of a volume 32 | .DocumentRevisions-V100 33 | .fseventsd 34 | .Spotlight-V100 35 | .TemporaryItems 36 | .Trashes 37 | .VolumeIcon.icns 38 | .com.apple.timemachine.donotpresent 39 | 40 | # Directories potentially created on remote AFP share 41 | .AppleDB 42 | .AppleDesktop 43 | Network Trash Folder 44 | Temporary Items 45 | .apdisk 46 | 47 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 48 | # Windows thumbnail cache files 49 | Thumbs.db 50 | ehthumbs.db 51 | ehthumbs_vista.db 52 | 53 | # Dump file 54 | *.stackdump 55 | 56 | # Folder config file 57 | [Dd]esktop.ini 58 | 59 | # Recycle Bin used on file shares 60 | $RECYCLE.BIN/ 61 | 62 | # Windows Installer files 63 | *.cab 64 | *.msi 65 | *.msix 66 | *.msm 67 | *.msp 68 | 69 | # Windows shortcuts 70 | *.lnk 71 | 72 | # content below from: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 73 | ## Ignore Visual Studio temporary files, build results, and 74 | ## files generated by popular Visual Studio add-ons. 75 | ## 76 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 77 | 78 | # User-specific files 79 | *.suo 80 | *.user 81 | *.userosscache 82 | *.sln.docstates 83 | 84 | # User-specific files (MonoDevelop/Xamarin Studio) 85 | *.userprefs 86 | 87 | # Build results 88 | [Dd]ebug/ 89 | [Dd]ebugPublic/ 90 | [Rr]elease/ 91 | [Rr]eleases/ 92 | x64/ 93 | x86/ 94 | bld/ 95 | [Bb]in/ 96 | [Oo]bj/ 97 | [Ll]og/ 98 | 99 | # Visual Studio 2015/2017 cache/options directory 100 | .vs/ 101 | # Uncomment if you have tasks that create the project's static files in wwwroot 102 | #wwwroot/ 103 | 104 | # Visual Studio 2017 auto generated files 105 | Generated\ Files/ 106 | 107 | # MSTest test Results 108 | [Tt]est[Rr]esult*/ 109 | [Bb]uild[Ll]og.* 110 | 111 | # NUNIT 112 | *.VisualState.xml 113 | TestResult.xml 114 | 115 | # Build Results of an ATL Project 116 | [Dd]ebugPS/ 117 | [Rr]eleasePS/ 118 | dlldata.c 119 | 120 | # Benchmark Results 121 | BenchmarkDotNet.Artifacts/ 122 | 123 | # .NET Core 124 | project.lock.json 125 | project.fragment.lock.json 126 | artifacts/ 127 | 128 | # StyleCop 129 | StyleCopReport.xml 130 | 131 | # Files built by Visual Studio 132 | *_i.c 133 | *_p.c 134 | *_h.h 135 | *.ilk 136 | *.meta 137 | *.obj 138 | *.iobj 139 | *.pch 140 | *.pdb 141 | *.ipdb 142 | *.pgc 143 | *.pgd 144 | *.rsp 145 | *.sbr 146 | *.tlb 147 | *.tli 148 | *.tlh 149 | *.tmp 150 | *.tmp_proj 151 | *_wpftmp.csproj 152 | *.log 153 | *.vspscc 154 | *.vssscc 155 | .builds 156 | *.pidb 157 | *.svclog 158 | *.scc 159 | 160 | # Chutzpah Test files 161 | _Chutzpah* 162 | 163 | # Visual C++ cache files 164 | ipch/ 165 | *.aps 166 | *.ncb 167 | *.opendb 168 | *.opensdf 169 | *.sdf 170 | *.cachefile 171 | *.VC.db 172 | *.VC.VC.opendb 173 | 174 | # Visual Studio profiler 175 | *.psess 176 | *.vsp 177 | *.vspx 178 | *.sap 179 | 180 | # Visual Studio Trace Files 181 | *.e2e 182 | 183 | # TFS 2012 Local Workspace 184 | $tf/ 185 | 186 | # Guidance Automation Toolkit 187 | *.gpState 188 | 189 | # ReSharper is a .NET coding add-in 190 | _ReSharper*/ 191 | *.[Rr]e[Ss]harper 192 | *.DotSettings.user 193 | 194 | # JustCode is a .NET coding add-in 195 | .JustCode 196 | 197 | # TeamCity is a build add-in 198 | _TeamCity* 199 | 200 | # DotCover is a Code Coverage Tool 201 | *.dotCover 202 | 203 | # AxoCover is a Code Coverage Tool 204 | .axoCover/* 205 | !.axoCover/settings.json 206 | 207 | # Visual Studio code coverage results 208 | *.coverage 209 | *.coveragexml 210 | 211 | # NCrunch 212 | _NCrunch_* 213 | .*crunch*.local.xml 214 | nCrunchTemp_* 215 | 216 | # MightyMoose 217 | *.mm.* 218 | AutoTest.Net/ 219 | 220 | # Web workbench (sass) 221 | .sass-cache/ 222 | 223 | # Installshield output folder 224 | [Ee]xpress/ 225 | 226 | # DocProject is a documentation generator add-in 227 | DocProject/buildhelp/ 228 | DocProject/Help/*.HxT 229 | DocProject/Help/*.HxC 230 | DocProject/Help/*.hhc 231 | DocProject/Help/*.hhk 232 | DocProject/Help/*.hhp 233 | DocProject/Help/Html2 234 | DocProject/Help/html 235 | 236 | # Click-Once directory 237 | publish/ 238 | 239 | # Publish Web Output 240 | *.[Pp]ublish.xml 241 | *.azurePubxml 242 | # Note: Comment the next line if you want to checkin your web deploy settings, 243 | # but database connection strings (with potential passwords) will be unencrypted 244 | *.pubxml 245 | *.publishproj 246 | 247 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 248 | # checkin your Azure Web App publish settings, but sensitive information contained 249 | # in these scripts will be unencrypted 250 | PublishScripts/ 251 | 252 | # NuGet Packages 253 | *.nupkg 254 | # The packages folder can be ignored because of Package Restore 255 | **/[Pp]ackages/* 256 | # except build/, which is used as an MSBuild target. 257 | !**/[Pp]ackages/build/ 258 | # Uncomment if necessary however generally it will be regenerated when needed 259 | #!**/[Pp]ackages/repositories.config 260 | # NuGet v3's project.json files produces more ignorable files 261 | *.nuget.props 262 | *.nuget.targets 263 | 264 | # Microsoft Azure Build Output 265 | csx/ 266 | *.build.csdef 267 | 268 | # Microsoft Azure Emulator 269 | ecf/ 270 | rcf/ 271 | 272 | # Windows Store app package directories and files 273 | AppPackages/ 274 | BundleArtifacts/ 275 | Package.StoreAssociation.xml 276 | _pkginfo.txt 277 | *.appx 278 | 279 | # Visual Studio cache files 280 | # files ending in .cache can be ignored 281 | *.[Cc]ache 282 | # but keep track of directories ending in .cache 283 | !*.[Cc]ache/ 284 | 285 | # Others 286 | ClientBin/ 287 | ~$* 288 | *~ 289 | *.dbmdl 290 | *.dbproj.schemaview 291 | *.jfm 292 | *.pfx 293 | *.publishsettings 294 | orleans.codegen.cs 295 | 296 | # Including strong name files can present a security risk 297 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 298 | #*.snk 299 | 300 | # Since there are multiple workflows, uncomment next line to ignore bower_components 301 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 302 | #bower_components/ 303 | 304 | # RIA/Silverlight projects 305 | Generated_Code/ 306 | 307 | # Backup & report files from converting an old project file 308 | # to a newer Visual Studio version. Backup files are not needed, 309 | # because we have git ;-) 310 | _UpgradeReport_Files/ 311 | Backup*/ 312 | UpgradeLog*.XML 313 | UpgradeLog*.htm 314 | ServiceFabricBackup/ 315 | *.rptproj.bak 316 | 317 | # SQL Server files 318 | *.mdf 319 | *.ldf 320 | *.ndf 321 | 322 | # Business Intelligence projects 323 | *.rdl.data 324 | *.bim.layout 325 | *.bim_*.settings 326 | *.rptproj.rsuser 327 | 328 | # Microsoft Fakes 329 | FakesAssemblies/ 330 | 331 | # GhostDoc plugin setting file 332 | *.GhostDoc.xml 333 | 334 | # Node.js Tools for Visual Studio 335 | .ntvs_analysis.dat 336 | node_modules/ 337 | 338 | # Visual Studio 6 build log 339 | *.plg 340 | 341 | # Visual Studio 6 workspace options file 342 | *.opt 343 | 344 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 345 | *.vbw 346 | 347 | # Visual Studio LightSwitch build output 348 | **/*.HTMLClient/GeneratedArtifacts 349 | **/*.DesktopClient/GeneratedArtifacts 350 | **/*.DesktopClient/ModelManifest.xml 351 | **/*.Server/GeneratedArtifacts 352 | **/*.Server/ModelManifest.xml 353 | _Pvt_Extensions 354 | 355 | # Paket dependency manager 356 | .paket/paket.exe 357 | paket-files/ 358 | 359 | # FAKE - F# Make 360 | .fake/ 361 | 362 | # JetBrains Rider 363 | .idea/ 364 | *.sln.iml 365 | 366 | # CodeRush personal settings 367 | .cr/personal 368 | 369 | # Python Tools for Visual Studio (PTVS) 370 | __pycache__/ 371 | *.pyc 372 | 373 | # Cake - Uncomment if you are using it 374 | # tools/** 375 | # !tools/packages.config 376 | 377 | # Tabs Studio 378 | *.tss 379 | 380 | # Telerik's JustMock configuration file 381 | *.jmconfig 382 | 383 | # BizTalk build output 384 | *.btp.cs 385 | *.btm.cs 386 | *.odx.cs 387 | *.xsd.cs 388 | 389 | # OpenCover UI analysis results 390 | OpenCover/ 391 | 392 | # Azure Stream Analytics local run output 393 | ASALocalRun/ 394 | 395 | # MSBuild Binary and Structured Log 396 | *.binlog 397 | 398 | # NVidia Nsight GPU debugger configuration file 399 | *.nvuser 400 | 401 | # MFractors (Xamarin productivity tool) working folder 402 | .mfractor/ 403 | 404 | # Local History for Visual Studio 405 | .localhistory/ 406 | 407 | # Generated code 408 | *.interface.g.cs 409 | *.part.g.cs 410 | *.g.targets 411 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## 1.1.3 - 2019-08-19 5 | ### Added 6 | - You can now comment lines in your `MvvmCodeGenMapper.xml` 7 | 8 | ### Fixed 9 | - GeneratorTask task was executed after CoreCompile task instead of before CoreCompile. 10 | 11 | ## 1.1.2 - 2019-08-19 12 | ### Added 13 | - Fix .g.targets issue 14 | - Clean code 15 | 16 | ## 1.1.1 - 2019-08-19 17 | ### Added 18 | - Fix .g.targets issue 19 | 20 | ## 1.1.0 - 2019-08-15 21 | ### Added 22 | - Add Prism Core support. 23 | - Update `Readme.md` 24 | 25 | ## 1.0.0 - 2019-07-26 26 | ### Added 27 | - First stable version of the plugin, now supports for MvvmCross, MvvmLightLibs, Mvvmicro and FreshMvvm. 28 | - Generate your ViewModels : commands and properties. 29 | 30 | -------------------------------------------------------------------------------- /Documentation/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damienaicheh/MvvmCodeGenerator/8fdd339ba7c568c0d3da33be29a6760ab13fef90/Documentation/logo.png -------------------------------------------------------------------------------- /Documentation/project_structured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damienaicheh/MvvmCodeGenerator/8fdd339ba7c568c0d3da33be29a6760ab13fef90/Documentation/project_structured.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Damien Aicheh 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. -------------------------------------------------------------------------------- /NuGet/MvvmCodeGenerator.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MvvmCodeGenerator 6 | 7 | 8 | 1.1.3 9 | 10 | 11 | Damien Aicheh 12 | 13 | 17 | Damien Aicheh 18 | 19 | 20 | https://github.com/damienaicheh/MvvmCodeGenerator 21 | 22 | 23 | MIT 24 | 25 | 26 | 27 | https://raw.githubusercontent.com/damienaicheh/MvvmCodeGenerator/master/Documentation/logo.png 28 | 29 | 33 | true 34 | 35 | 36 | Generate boilerplate code for MvvmLightLibs, MvvmCross, Prism, Mvvmicro and FreshMvvm. 37 | 38 | 42 | Save time by generating boilerplate code for different MVVM framework, this nuget support now: MvvmLightLibs, MvvmCross, Prism, Mvvmicro and FreshMvvm. 43 | 44 | 45 | Copyright ©2019 Damien Aicheh 46 | 47 | 48 | mvvm generator xamarin dotnet command properties mvvmicro mvvmcross mvvmlightlibs freshmvvm prism mvvmcodegen 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Schema](./Documentation/logo.png) 2 | 3 | MVVM code generator tool for .NET. 4 | 5 | ## Install 6 | 7 | Available on [NuGet](https://www.nuget.org/packages/MvvmCodeGenerator). 8 | 9 | ## Why ? 10 | 11 | I decided to create this tool to avoid having to always write boilerplate code for my ViewModels. This NuGet is built on top of the [Rosyln .NET compiler](https://github.com/dotnet/roslyn). 12 | 13 | The advantages of using it are : 14 | * Reduce the amount of code in your ViewModels 15 | * The ViewModels only contains the more important code 16 | * Migrate from one MVVM Framework to another easily 17 | 18 | ## Features 19 | 20 | This NuGet allows you to generate your ViewModels with the Properties and Commands specified in your XML file. The code generated will depend on the Mvvm Frameworks you like. 21 | 22 | This tool now support these MVVM Frameworks: 23 | 24 | * [MvvmLightLibs](https://www.nuget.org/packages/MvvmLightLibs/) 25 | * [MvvmCross](https://www.nuget.org/packages/MvvmCross/) 26 | * [Mvvmicro](https://www.nuget.org/packages/Mvvmicro/) 27 | * [FreshMvvm](https://www.nuget.org/packages/FreshMvvm/) 28 | * [Prism.Core](https://www.nuget.org/packages/Prism.Core/) 29 | 30 | ## Tutorial 31 | 32 | You will find a complete tutorial here: 33 | ##### English version : 34 | [https://damienaicheh.github.io/nuget/xamarin/xamarin.forms/dotnet/2019/07/31/a-complete-overview-of-mvvmcodegenerator-en](https://damienaicheh.github.io/nuget/xamarin/xamarin.forms/dotnet/2019/07/31/a-complete-overview-of-mvvmcodegenerator-en) 35 | 36 | ##### French version : 37 | [https://damienaicheh.github.io/nuget/xamarin/xamarin.forms/dotnet/2019/07/31/a-complete-overview-of-mvvmcodegenerator-fr](https://damienaicheh.github.io/nuget/xamarin/xamarin.forms/dotnet/2019/07/31/a-complete-overview-of-mvvmcodegenerator-fr) 38 | 39 | ## Usage 40 | ### Setup 41 | 42 | First of all, install the [NuGet](https://www.nuget.org/packages/MvvmCodeGenerator) package to your .NetStandard project for example. 43 | Then create a new file called `MvvmCodeGenMapper.xml` at the root of your library project. This file will contain the definition of your ViewModels. 44 | 45 | ### Configure your generator 46 | 47 | Now it's time to define which generator we want to use, 4 options are available: 48 | * mvvmlightlibs 49 | * mvvmcross 50 | * mvvmicro 51 | * freshmvvm 52 | * prismcore 53 | 54 | Choose the one you like and declare it inside your `MvvmCodeGenMapper.xml` like this: 55 | 56 | ```xml 57 | 58 | 59 | 60 | 61 | ``` 62 | 63 | ### Create your ViewModels 64 | 65 | Let's define the ViewModels inside the `ViewModels` tags. You can define as many `ViewModels` tags as needed. Here is an example: 66 | 67 | ```xml 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ``` 83 | 84 | As you can see above, you can specify a list of ViewModels for a specific namespace and destination folder. 85 | 86 | This XML will generate the 2 ViewModels with their **.g.cs** class associated: 87 | 88 | The `RootViewModel.cs`: 89 | ```csharp 90 | namespace MvvmCodeGenerator.Sample 91 | { 92 | using System; 93 | using System.Collections.Generic; 94 | using System.Threading; 95 | using System.Threading.Tasks; 96 | using GalaSoft.MvvmLight.Command; 97 | 98 | public partial class RootViewModel 99 | { 100 | } 101 | } 102 | ``` 103 | 104 | The `RootViewModel.interface.g.cs`: 105 | ```csharp 106 | //------------------------------------------------------------------------------ 107 | // 108 | // This code was generated by MvvmCodeGenerator. 109 | // Runtime Version:4.0.30319.42000 110 | // 111 | // Changes to this file may cause incorrect behavior and will be lost if 112 | // the code is regenerated. 113 | // 114 | //------------------------------------------------------------------------------ 115 | namespace MvvmCodeGenerator.Sample 116 | { 117 | using System; 118 | using System.Collections.Generic; 119 | using System.Threading; 120 | using System.Threading.Tasks; 121 | using GalaSoft.MvvmLight.Command; 122 | 123 | public interface IRootViewModel : System.ComponentModel.INotifyPropertyChanged 124 | { 125 | System.Boolean IsLoading 126 | { 127 | get; 128 | } 129 | } 130 | } 131 | ``` 132 | 133 | The `RootViewModel.part.g.cs`: 134 | ```csharp 135 | //------------------------------------------------------------------------------ 136 | // 137 | // This code was generated by MvvmCodeGenerator. 138 | // Runtime Version:4.0.30319.42000 139 | // 140 | // Changes to this file may cause incorrect behavior and will be lost if 141 | // the code is regenerated. 142 | // 143 | //------------------------------------------------------------------------------ 144 | namespace MvvmCodeGenerator.Sample 145 | { 146 | using System; 147 | using System.Collections.Generic; 148 | using System.Threading; 149 | using System.Threading.Tasks; 150 | using GalaSoft.MvvmLight.Command; 151 | 152 | public partial class RootViewModel : GalaSoft.MvvmLight.ViewModelBase, MvvmCodeGenerator.Sample.IRootViewModel 153 | { 154 | private System.Boolean isLoading; 155 | /// 156 | // Gets or sets the loader property. 157 | /// 158 | public System.Boolean IsLoading 159 | { 160 | get => this.isLoading; 161 | set => this.Set(ref this.isLoading, value); 162 | } 163 | } 164 | } 165 | ``` 166 | 167 | The `DashboardViewModel.cs` where you will implement the Commands declared: 168 | 169 | ```csharp 170 | Folder destination ./MvvmCodeGenerator.Dev/ViewModel 171 | namespace MvvmCodeGenerator.Sample 172 | { 173 | using System; 174 | using System.Collections.Generic; 175 | using System.Threading; 176 | using System.Threading.Tasks; 177 | using GalaSoft.MvvmLight.Command; 178 | 179 | public partial class DashboardViewModel 180 | { 181 | } 182 | } 183 | ``` 184 | 185 | The `DashboardViewModel.interface.g.cs`: 186 | ```csharp 187 | //------------------------------------------------------------------------------ 188 | // 189 | // This code was generated by MvvmCodeGenerator. 190 | // Runtime Version:4.0.30319.42000 191 | // 192 | // Changes to this file may cause incorrect behavior and will be lost if 193 | // the code is regenerated. 194 | // 195 | //------------------------------------------------------------------------------ 196 | namespace MvvmCodeGenerator.Sample 197 | { 198 | using System; 199 | using System.Collections.Generic; 200 | using System.Threading; 201 | using System.Threading.Tasks; 202 | using GalaSoft.MvvmLight.Command; 203 | 204 | public interface IDashboardViewModel : MvvmCodeGenerator.Sample.IRootViewModel 205 | { 206 | System.String Title 207 | { 208 | get; 209 | } 210 | 211 | System.Windows.Input.ICommand BuyCommand 212 | { 213 | get; 214 | } 215 | 216 | System.Boolean IsConsultationCommandRunning 217 | { 218 | get; 219 | } 220 | 221 | System.Windows.Input.ICommand ConsultationCommand 222 | { 223 | get; 224 | } 225 | } 226 | } 227 | ``` 228 | 229 | The `DashboardViewModel.part.g.cs`: 230 | ```csharp 231 | //------------------------------------------------------------------------------ 232 | // 233 | // This code was generated by MvvmCodeGenerator. 234 | // Runtime Version:4.0.30319.42000 235 | // 236 | // Changes to this file may cause incorrect behavior and will be lost if 237 | // the code is regenerated. 238 | // 239 | //------------------------------------------------------------------------------ 240 | namespace MvvmCodeGenerator.Sample 241 | { 242 | using System; 243 | using System.Collections.Generic; 244 | using System.Threading; 245 | using System.Threading.Tasks; 246 | using GalaSoft.MvvmLight.Command; 247 | 248 | public partial class DashboardViewModel : MvvmCodeGenerator.Sample.RootViewModel, MvvmCodeGenerator.Sample.IDashboardViewModel 249 | { 250 | private System.String title; 251 | private System.Boolean isConsultationCommandRunning; 252 | private GalaSoft.MvvmLight.Command.RelayCommand buyCommand; 253 | private GalaSoft.MvvmLight.Command.RelayCommand consultationCommand; 254 | /// 255 | // Gets or sets the title. 256 | /// 257 | public System.String Title 258 | { 259 | get => this.title; 260 | set => this.Set(ref this.title, value); 261 | } 262 | 263 | /// 264 | // Gets or sets the value to know if the associated async command is running. 265 | /// 266 | public System.Boolean IsConsultationCommandRunning 267 | { 268 | get => this.isConsultationCommandRunning; 269 | set => this.Set(ref this.isConsultationCommandRunning, value); 270 | } 271 | 272 | /// 273 | // Gets or sets the command to buy a boat 274 | /// 275 | public System.Windows.Input.ICommand BuyCommand 276 | { 277 | get => this.buyCommand ?? (this.buyCommand = new GalaSoft.MvvmLight.Command.RelayCommand(ExecuteBuyCommand)); // You must implement the following method(s): ExecuteBuyCommand and OnExecuteBuyCommandAsyncError 278 | } 279 | 280 | /// 281 | // Gets or sets the command to consult my orders 282 | /// 283 | public System.Windows.Input.ICommand ConsultationCommand 284 | { 285 | get => this.consultationCommand ?? (this.consultationCommand = new GalaSoft.MvvmLight.Command.RelayCommand(async (value) => 286 | { 287 | try 288 | { 289 | this.IsConsultationCommandRunning = true; 290 | await ExecuteConsultationCommandAsync(value); 291 | } 292 | catch (System.Exception ex) 293 | { 294 | OnExecuteConsultationCommandAsyncError(ex); 295 | } 296 | finally 297 | { 298 | this.IsConsultationCommandRunning = false; 299 | } 300 | } 301 | 302 | , CanExecuteConsultationCommand)); // You must implement the following method(s): ExecuteConsultationCommandAsync and the CanExecuteConsultationCommand and OnExecuteConsultationCommandAsyncError 303 | } 304 | } 305 | } 306 | ``` 307 | 308 | Now you juste have to implement the commands in each *ViewModel.cs* file. 309 | 310 | A *IsRunning* property is added in the case of **MvvmLightLibs** and **FreshMvvm** async commands for you. **MvvmCross** and **Mvvmicro** already have this kind of mechanism. 311 | 312 | ### Define your ViewModels and ItemViewModels 313 | 314 | Each `ViewModel` tag has different properties : 315 | 316 | * **Key** : The ViewModel name prefix 317 | * **Base** : The ViewModel name prefix of the parent class 318 | 319 | If you want to generate an ItemViewModel for each items of a listview for example, just use the `ItemViewModel` tag instead of `ViewModel`. You will have the same properties than the `ViewModel` tag available. 320 | 321 | You can specify the **Properties** and **Commands** for each ViewModels: 322 | 323 | For the **Properties** you have the ability to define it using the `Property` tag: 324 | 325 | * **Name** : The name of the property 326 | * **Description** : The comment associated to the property 327 | * **Type** : The type of the property 328 | 329 | You have different options to specify the **Type** of your property 330 | 331 | Here the list of base types that can be used: 332 | 333 | * **string** 334 | * **int** 335 | * **long** 336 | * **bool** 337 | * **float** 338 | * **object** 339 | * **double** 340 | * **DateTime** 341 | * **DateTimeOffset** 342 | * **TimeSpan** 343 | 344 | If the type you need is not in this list, you can just specify it using the complete namespace for example: 345 | 346 | ```xml 347 | 348 | ``` 349 | 350 | You can also declare a list like this: 351 | ```xml 352 | 353 | 354 | ``` 355 | 356 | For the **Commands** you have the ability to define: 357 | 358 | * Synchronous command using the `Command` tag 359 | * Asynchronous command using the `AsyncCommand` tag 360 | 361 | Each command tag has different properties: 362 | * **Name** : The name of the command 363 | * **Parameter** : The parameter type of the command 364 | * **CanExecute** : A boolean to know if the command must have a can execute method or not 365 | * **Description** : The comment associated to the command 366 | 367 | ### Visual Studio display 368 | 369 | If you build your project now, you will have this kind of result: 370 | ![Visual Studio File Viewer](./Documentation/project_structured.png) 371 | 372 | If your files are not grouped correctly like above, please check if the `MvvmCodeGenMapper.g.targets` file was imported correctly in your *.csproj* associated to your project. If it's not the case you can do it manually by adding this line: 373 | 374 | ```xml 375 | 376 | ``` 377 | 378 | Relaod your project and you will be good to go. 379 | 380 | You will find a full example in the `Xamarin.Sample` project [here](https://github.com/damienaicheh/MvvmCodeGenerator/tree/master/Sources/Xamarin.Sample) 381 | 382 | 383 | ## Contributions 384 | 385 | Contributions are welcome! If you find a bug or if you want a feature please report it. 386 | 387 | If you want to contribute create a branch from the current master branch and when your code is ready create a pull request. 388 | 389 | ### License 390 | 391 | ![MIT © Damien](https://img.shields.io/badge/licence-MIT-blue.svg) 392 | 393 | © [Damien Aicheh](https://damienaicheh.github.io/) 394 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Dev/MvvmCodeGenMapper.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Dev/MvvmCodeGenerator.Dev.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Dev/Program.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Dev 2 | { 3 | using System; 4 | using MvvmCodeGenerator.Gen; 5 | 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | Console.WriteLine($"Args:"); 11 | foreach (var arg in args) 12 | { 13 | Console.WriteLine($" {arg}"); 14 | } 15 | 16 | // Run this command in terminal to test the sample project: 17 | 18 | //dotnet run --project ./MvvmCodeGenerator.Gen/MvvmCodeGenerator.Gen.csproj -i:"./MvvmCodeGenerator.Sample/MvvmCodeGenMapper.xml" -o:"./MvvmCodeGenerator.Sample" -g:mvvmicro 19 | //var inputFile = args[0]?.Split("-i:")[1]; 20 | //var outputFolderProject = args[1]?.Split("-o:")[1]; 21 | //var content = File.ReadAllText(inputFile); 22 | 23 | // Run the project directly with this configuration: 24 | 25 | var outputFolderProject = "./MvvmCodeGenerator.Dev"; 26 | 27 | Arguments arguments = new Arguments 28 | { 29 | OutputFolderProject = outputFolderProject 30 | }; 31 | 32 | Bootstrap.Start("./../../../../MvvmCodeGenerator.Dev/MvvmCodeGenMapper.xml", arguments); 33 | 34 | Console.WriteLine("End of generation."); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Bootstrap.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.IO; 4 | using Microsoft.Build.Utilities; 5 | 6 | /// 7 | /// Bootstrap the generator. 8 | /// 9 | public static class Bootstrap 10 | { 11 | /// 12 | /// Start the generation. 13 | /// 14 | /// The path to the Resource file. 15 | /// The arguments from the project. 16 | /// The default logging system. 17 | public static void Start(string filePath, Arguments arguments, TaskLoggingHelper logger = null) 18 | { 19 | var content = File.ReadAllText(filePath); 20 | 21 | XmlParser xmlParser = new XmlParser(); 22 | ResourceFile resourceFile = xmlParser.ReadResourceFile(content); 23 | 24 | if (resourceFile.Generator != null) 25 | { 26 | CSharpGenerator gen = null; 27 | 28 | switch (resourceFile.Generator.ToLower()) 29 | { 30 | case "mvvmicro": 31 | gen = new MvvmicroCSharpGenerator(resourceFile.ViewModels, arguments); 32 | break; 33 | case "mvvmlightlibs": 34 | gen = new MvvmLightLibsGenerator(resourceFile.ViewModels, arguments); 35 | break; 36 | case "mvvmcross": 37 | gen = new MvvmCrossGenerator(resourceFile.ViewModels, arguments); 38 | break; 39 | case "freshmvvm": 40 | gen = new FreshMvvmGenerator(resourceFile.ViewModels, arguments); 41 | break; 42 | case "prismcore": 43 | gen = new PrismCoreGenerator(resourceFile.ViewModels, arguments); 44 | break; 45 | } 46 | 47 | gen.Log = logger; 48 | gen.CleanGeneratedFiles(); 49 | gen.Generate(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Generation/CSharpGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Text; 7 | using System.Xml; 8 | using System.Xml.Linq; 9 | using Microsoft.Build.Utilities; 10 | using Microsoft.CodeAnalysis; 11 | using Microsoft.CodeAnalysis.CSharp; 12 | using Microsoft.CodeAnalysis.CSharp.Syntax; 13 | 14 | /// 15 | /// Base for all generators. 16 | /// 17 | public abstract class CSharpGenerator : IGenerator 18 | { 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// Constructor base for all generators. 22 | /// 23 | /// List of ViewModels to generate. 24 | /// The arguments console. 25 | protected CSharpGenerator(List viewModels, Arguments arguments) 26 | { 27 | this.ViewModels = viewModels; 28 | this.Arguments = arguments; 29 | } 30 | 31 | /// 32 | /// Logger defined in the task. 33 | /// 34 | public TaskLoggingHelper Log { private get; set; } 35 | 36 | /// 37 | /// Define if the generator needs to have a IsRunning property for async commands. 38 | /// 39 | protected bool IsRunningPropertyForCommandNeeded { private get; set; } 40 | 41 | /// 42 | /// Gets or sets The namespace declaration for the current generated class. 43 | /// 44 | /// The namespace declaration. 45 | protected NamespaceDeclarationSyntax NamespaceDeclaration { get; set; } 46 | 47 | /// 48 | /// Gets or sets the current generated class declaration. 49 | /// 50 | /// The class declaration. 51 | protected ClassDeclarationSyntax ClassDeclaration { get; set; } 52 | 53 | /// 54 | /// Gets or sets the current generated interface. 55 | /// 56 | /// The interface declaration. 57 | protected InterfaceDeclarationSyntax InterfaceDeclaration { get; set; } 58 | 59 | private List ViewModels { get; set; } 60 | 61 | private Arguments Arguments { get; set; } 62 | 63 | /// 64 | /// Generate the ViewModels. 65 | /// 66 | public void Generate() 67 | { 68 | foreach (var viewModel in this.ViewModels) 69 | { 70 | this.GenerateViewModelImplementationFile(viewModel); 71 | this.GenerateViewModelInterfaceFile(viewModel); 72 | this.GenerateViewModelPartialFile(viewModel); 73 | } 74 | 75 | this.GenerateTarget(ViewModels, out string generatedTargetFilename); 76 | #if !DEBUG 77 | this.InjectProject(generatedTargetFilename); 78 | #endif 79 | } 80 | 81 | /// 82 | /// Clean previous files. 83 | /// 84 | /// 85 | /// At this time, this method has no effect.
86 | /// If the project has not previously built, or if it has been cleaned, there is no file to delete.
87 | /// And the dedicated task to file generation is not run, due to the cached data state of the project. 88 | ///
89 | public void CleanGeneratedFiles() 90 | { 91 | // Becarefull the cleanTask do the same thing. 92 | FileHelper.ResetTarget(this.Arguments.OutputFolderProject, Constants.GeneratedTargetFileWithoutExtension, Constants.GeneratedTargetFileExtension); 93 | FileHelper.Clean(this.Arguments.OutputFolderProject, "interface.g.cs"); 94 | FileHelper.Clean(this.Arguments.OutputFolderProject, "part.g.cs"); 95 | } 96 | 97 | /// 98 | /// Get the entire namespace of the command implementation. 99 | /// 100 | /// The namespace of the command. 101 | protected abstract string GetCommandNamespace(); 102 | 103 | /// 104 | /// Get the entire namespace of the async command implementation. 105 | /// 106 | /// The namespace of the async command. 107 | protected abstract string GetAsyncCommandNamespace(); 108 | 109 | /// 110 | /// Add base ViewModel if it is required by the framework. 111 | /// 112 | /// SyntaxNodeOrToken with the ViewModel base. 113 | protected abstract SyntaxNodeOrToken AddBaseViewModel(); 114 | 115 | /// 116 | /// Writes a message into the default logging system. 117 | /// 118 | /// The message to log. 119 | [Conditional("DEBUG")] 120 | protected void LogMessage(string message) 121 | { 122 | if (!string.IsNullOrEmpty(message)) 123 | { 124 | Log?.LogMessage(message); 125 | } 126 | } 127 | 128 | /// 129 | /// Create the default namespaces. 130 | /// 131 | /// The namespace of the current class generated. 132 | protected virtual void CreateNamespace(string @namespace) 133 | { 134 | this.NamespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(@namespace)).NormalizeWhitespace(); 135 | 136 | // Add System using statement: 137 | var usings = new[] { "System", "System.Collections.Generic", "System.Threading", "System.Threading.Tasks" }; 138 | 139 | foreach (var item in usings) 140 | { 141 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(item))); 142 | } 143 | } 144 | 145 | /// 146 | /// Get command method syntax, depending on the framework used. 147 | /// 148 | /// The command to generate. 149 | /// The ArgumentListSyntax that represent the command syntax to generate 150 | protected virtual ArgumentListSyntax GetCommandMethodSyntax(Command command) 151 | { 152 | var syntaxeNodeOrToken = new List 153 | { 154 | SyntaxFactory.Argument(SyntaxFactory.IdentifierName(command.FormatExecuteCommandName())) 155 | }; 156 | 157 | if (command.HasCanExecute) 158 | { 159 | syntaxeNodeOrToken.Add(SyntaxFactory.Token(SyntaxKind.CommaToken)); 160 | syntaxeNodeOrToken.Add(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(command.FormatCanExecuteCommandName()))); 161 | } 162 | 163 | return SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(syntaxeNodeOrToken)); 164 | } 165 | 166 | /// 167 | /// Build base class to inherit. 168 | /// 169 | /// Namespace of the class. 170 | /// The class name. 171 | /// The SimpleBaseTypeSyntax of the base class. 172 | protected SimpleBaseTypeSyntax BuildBaseClass(string @namespace, string @class) => 173 | SyntaxFactory.SimpleBaseType(SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName(@namespace), SyntaxFactory.IdentifierName(@class))); 174 | 175 | /// 176 | /// Creates the public properties for each Mvvm library. 177 | /// 178 | /// The public properties. 179 | /// The property to generate. 180 | public virtual PropertyDeclarationSyntax CreatePublicProperties(Property property) 181 | { 182 | return SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(property.Type.FindType()), property.Name) 183 | .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) 184 | .AddAccessorListAccessors( 185 | SyntaxFactory.AccessorDeclaration( 186 | SyntaxKind.GetAccessorDeclaration) 187 | .WithExpressionBody( 188 | SyntaxFactory.ArrowExpressionClause( 189 | SyntaxFactory.MemberAccessExpression( 190 | SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(property.Name.ToCamelCase())))) 191 | .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), 192 | SyntaxFactory.AccessorDeclaration( 193 | SyntaxKind.SetAccessorDeclaration) 194 | .WithExpressionBody( 195 | SyntaxFactory.ArrowExpressionClause( 196 | SyntaxFactory.InvocationExpression( 197 | SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName("Set"))) 198 | .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( 199 | new SyntaxNodeOrToken[] 200 | { 201 | SyntaxFactory.Argument(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(property.Name.ToCamelCase()))) 202 | .WithRefKindKeyword(SyntaxFactory.Token(SyntaxKind.RefKeyword)), SyntaxFactory.Token(SyntaxKind.CommaToken), SyntaxFactory.Argument(SyntaxFactory.IdentifierName("value")) 203 | }))))) 204 | .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))) 205 | .WithModifiers(FormatterHelper.GenerateComment(property.Comment)); 206 | } 207 | 208 | /// 209 | /// Generate command comment to indicate which method associated to the command needs to be implemented. 210 | /// 211 | /// The command. 212 | /// The complete comment to the command. 213 | public virtual string GenerateCommandComment(Command command) 214 | { 215 | var defaultComment = $"// You must implement the following method(s): {command.FormatExecuteCommandName()}"; 216 | var isComment = command.HasCanExecute ? $" and the {command.FormatCanExecuteCommandName()}" : string.Empty; 217 | return string.Concat(defaultComment, isComment); 218 | } 219 | 220 | private void GenerateViewModelImplementationFile(ViewModel viewModel) 221 | { 222 | this.CreateNamespace(viewModel.Namespace); 223 | this.CreateClass(viewModel); 224 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddMembers(this.ClassDeclaration); 225 | 226 | // Normalize and get code as string. 227 | var content = this.NamespaceDeclaration.NormalizeWhitespace().ToFullString(); 228 | 229 | FileHelper.SaveFileContent(this.Arguments.OutputFolderProject, viewModel.DestinationFolder, content, viewModel.CreateViewModelName(), ".cs", true); 230 | } 231 | 232 | private void GenerateViewModelPartialFile(ViewModel viewModel) 233 | { 234 | this.GenerateViewModel(viewModel); 235 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddMembers(this.ClassDeclaration); 236 | this.NamespaceDeclaration = this.NamespaceDeclaration.AutoGeneratedCommand(); 237 | 238 | // Normalize and get code as string. 239 | var content = this.NamespaceDeclaration.NormalizeWhitespace().ToFullString(); 240 | 241 | FileHelper.SaveFileContent(this.Arguments.OutputFolderProject, viewModel.DestinationFolder, content, viewModel.CreateViewModelName(), ".part.g.cs", false); 242 | } 243 | 244 | private void GenerateViewModelInterfaceFile(ViewModel viewModel) 245 | { 246 | this.GenerateInterfaceViewModel(viewModel); 247 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddMembers(this.InterfaceDeclaration); 248 | this.NamespaceDeclaration = this.NamespaceDeclaration.AutoGeneratedCommand(); 249 | 250 | // Normalize and get code as string. 251 | var content = this.NamespaceDeclaration.NormalizeWhitespace().ToFullString(); 252 | 253 | FileHelper.SaveFileContent(this.Arguments.OutputFolderProject, viewModel.DestinationFolder, content, viewModel.CreateViewModelName(), ".interface.g.cs", false); 254 | } 255 | 256 | private void GenerateViewModel(ViewModel viewModel) 257 | { 258 | this.CreateNamespace(viewModel.Namespace); 259 | this.CreateClass(viewModel); 260 | this.AddBaseClass(viewModel); 261 | 262 | var privateFields = new List(); 263 | var publicProperties = new List(); 264 | 265 | // Create all properties. 266 | if (viewModel.Properties.Count > 0) 267 | { 268 | foreach (var property in viewModel.Properties) 269 | { 270 | privateFields.Add(this.CreatePrivateVariable(property.Type.FindType(), property.Name)); 271 | publicProperties.Add(this.CreatePublicProperties(property)); 272 | } 273 | } 274 | 275 | // Create all commands. 276 | if (viewModel.Commands.Count > 0) 277 | { 278 | foreach (var command in viewModel.Commands) 279 | { 280 | // This is voluntary done in two separate loops to keep the public and private properties at the top of the file. 281 | if (this.IsRunningPropertyForCommandNeeded && command.IsAsync) 282 | { 283 | privateFields.Add(this.CreatePrivateVariable(typeof(bool).ToString(), command.FormatPropertyIsCommandRunning())); 284 | publicProperties.Add(this.CreatePublicProperties(new Property(command.FormatPropertyIsCommandRunning(), typeof(bool).ToString(), "Gets or sets the value to know if the associated async command is running.", true, true))); 285 | } 286 | } 287 | 288 | foreach (var command in viewModel.Commands) 289 | { 290 | var type = this.GetCommandType(command); 291 | privateFields.Add(this.CreatePrivateVariable(type, command.FormatCommandName())); 292 | publicProperties.Add(this.CreateCommandProperty(command)); 293 | } 294 | } 295 | 296 | // Add all private fields to the class. 297 | foreach (var field in privateFields) 298 | { 299 | this.ClassDeclaration = this.ClassDeclaration.AddMembers(SyntaxFactory.FieldDeclaration(field) 300 | .AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))); 301 | } 302 | 303 | // Add all public properties to the class. 304 | foreach (var publicProperty in publicProperties) 305 | { 306 | this.ClassDeclaration = this.ClassDeclaration.AddMembers(publicProperty); 307 | } 308 | } 309 | 310 | private void GenerateInterfaceViewModel(ViewModel viewModel) 311 | { 312 | this.CreateNamespace(viewModel.Namespace); 313 | this.CreateInterface(viewModel); 314 | this.AddBaseInterface(viewModel); 315 | 316 | // Create and add all properties to the interface. 317 | foreach (var property in viewModel.Properties) 318 | { 319 | var field = this.CreatePublicProperty(property.Type.FindType(), property.Name); 320 | this.InterfaceDeclaration = this.InterfaceDeclaration.AddMembers(field); 321 | } 322 | 323 | // Create and add all commands to the interface. 324 | foreach (var command in viewModel.Commands) 325 | { 326 | if (this.IsRunningPropertyForCommandNeeded && command.IsAsync) 327 | { 328 | var commandField = this.CreatePublicProperty(typeof(bool).ToString(), command.FormatPropertyIsCommandRunning()); 329 | this.InterfaceDeclaration = this.InterfaceDeclaration.AddMembers(commandField); 330 | } 331 | 332 | var field = this.CreatePublicProperty(typeof(System.Windows.Input.ICommand).ToString(), command.FormatCommandName()); 333 | this.InterfaceDeclaration = this.InterfaceDeclaration.AddMembers(field); 334 | } 335 | } 336 | 337 | private void CreateClass(ViewModel viewModel) 338 | { 339 | this.ClassDeclaration = SyntaxFactory.ClassDeclaration(viewModel.CreateViewModelName()); 340 | this.ClassDeclaration = this.ClassDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) 341 | .AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword)); 342 | } 343 | 344 | private void CreateInterface(ViewModel viewModel) 345 | { 346 | this.InterfaceDeclaration = SyntaxFactory.InterfaceDeclaration(string.Concat(viewModel.CreateInterfaceViewModelName())); 347 | this.InterfaceDeclaration = this.InterfaceDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); 348 | } 349 | 350 | private void AddBaseClass(ViewModel viewModel) 351 | { 352 | var baseClass = new List(); 353 | 354 | if (viewModel.HasBase) 355 | { 356 | baseClass.Add(this.BuildBaseClass(viewModel.Namespace, viewModel.CreateBaseViewModelName())); 357 | baseClass.Add(SyntaxFactory.Token(SyntaxKind.CommaToken)); 358 | } 359 | else 360 | { 361 | baseClass.Add(this.AddBaseViewModel()); 362 | 363 | if (baseClass.Count > 0) 364 | { 365 | baseClass.Add(SyntaxFactory.Token(SyntaxKind.CommaToken)); 366 | } 367 | } 368 | 369 | // Add the interface that will be associated with it 370 | baseClass.Add(this.BuildBaseClass(viewModel.Namespace, viewModel.CreateInterfaceViewModelName())); 371 | 372 | this.ClassDeclaration = this.ClassDeclaration.WithBaseList(this.CreateBaseClass(baseClass)); 373 | } 374 | 375 | private void AddBaseInterface(ViewModel viewModel) 376 | { 377 | var baseClass = new List(); 378 | 379 | if (viewModel.HasBase) 380 | { 381 | baseClass.Add(this.BuildBaseClass(viewModel.Namespace, viewModel.CreateInterfaceBaseViewModelName())); 382 | } 383 | else 384 | { 385 | baseClass.Add(this.BuildBaseClass("System.ComponentModel", "INotifyPropertyChanged")); 386 | } 387 | 388 | this.InterfaceDeclaration = this.InterfaceDeclaration.WithBaseList(this.CreateBaseClass(baseClass)); 389 | } 390 | 391 | private PropertyDeclarationSyntax CreatePublicProperty(string type, string name) => 392 | SyntaxFactory.PropertyDeclaration(SyntaxFactory.IdentifierName(type), SyntaxFactory.Identifier(name)) 393 | .WithAccessorList(SyntaxFactory.AccessorList( 394 | SyntaxFactory.SingletonList(SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) 395 | .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))))); 396 | 397 | private VariableDeclarationSyntax CreatePrivateVariable(string type, string name) => 398 | SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName(type)).AddVariables(SyntaxFactory.VariableDeclarator(name.ToCamelCase())); 399 | 400 | private BaseListSyntax CreateBaseClass(List baseClasses) => 401 | SyntaxFactory.BaseList(SyntaxFactory.SeparatedList(baseClasses)); 402 | 403 | private PropertyDeclarationSyntax CreateCommandProperty(Command command) 404 | { 405 | var name = command.FormatCommandName(); 406 | var type = this.GetCommandType(command); 407 | 408 | return SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(typeof(System.Windows.Input.ICommand).ToString()), name) 409 | .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) 410 | .AddAccessorListAccessors( 411 | SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) 412 | .WithExpressionBody( 413 | SyntaxFactory.ArrowExpressionClause( 414 | SyntaxFactory.BinaryExpression( 415 | SyntaxKind.CoalesceExpression, 416 | SyntaxFactory.MemberAccessExpression( 417 | SyntaxKind.SimpleMemberAccessExpression, 418 | SyntaxFactory.ThisExpression(), 419 | SyntaxFactory.IdentifierName(name.ToCamelCase())), 420 | SyntaxFactory.ParenthesizedExpression( 421 | SyntaxFactory.AssignmentExpression( 422 | SyntaxKind.SimpleAssignmentExpression, 423 | SyntaxFactory.MemberAccessExpression( 424 | SyntaxKind.SimpleMemberAccessExpression, 425 | SyntaxFactory.ThisExpression(), 426 | SyntaxFactory.IdentifierName(name.ToCamelCase())), 427 | SyntaxFactory.ObjectCreationExpression(SyntaxFactory.IdentifierName(type)) 428 | .WithArgumentList(this.GetCommandMethodSyntax(command))))))) 429 | .WithSemicolonToken(SyntaxFactory.Token( 430 | SyntaxFactory.TriviaList(), 431 | SyntaxKind.SemicolonToken, 432 | SyntaxFactory.TriviaList(SyntaxFactory.Comment(this.GenerateCommandComment(command)))))) 433 | .WithModifiers(FormatterHelper.GenerateComment(command.Comment)); 434 | } 435 | 436 | private string GetCommandType(Command command) 437 | { 438 | var type = command.IsAsync ? this.GetAsyncCommandNamespace() : this.GetCommandNamespace(); 439 | return command.HasParameterType ? $"{type}<{command.ParameterType}>" : type; 440 | } 441 | 442 | private void GenerateTarget(List viewModels, out string generatedTargetFilename) 443 | { 444 | var xml = new XElement("Project"); 445 | 446 | xml.Add(new XComment("This file has been generated with MvvmCodeGenerator, do not modify it.")); 447 | 448 | var group = new XElement("ItemGroup"); 449 | 450 | foreach (var viewModel in viewModels) 451 | { 452 | var name = viewModel.CreateViewModelName(); 453 | var vmFolder = viewModel.DestinationFolder; 454 | 455 | var compile = new XElement("Compile"); 456 | 457 | if (string.IsNullOrEmpty(vmFolder)) 458 | { 459 | compile.Add(new XAttribute("Update", $"{name}.*.g.cs")); 460 | } 461 | else 462 | { 463 | compile.Add(new XAttribute("Update", string.Concat(Path.Combine(vmFolder, name), ".*.g.cs"))); 464 | } 465 | 466 | var dependentUpon = new XElement("DependentUpon"); 467 | dependentUpon.Add(new XText($"{name}.cs")); 468 | compile.Add(dependentUpon); 469 | 470 | group.Add(compile); 471 | } 472 | 473 | xml.Add(group); 474 | 475 | FileHelper.SaveFileContent(this.Arguments.OutputFolderProject, string.Empty, xml, Constants.GeneratedTargetFileWithoutExtension, Constants.GeneratedTargetFileExtension); 476 | generatedTargetFilename = string.Concat(Constants.GeneratedTargetFileWithoutExtension, Constants.GeneratedTargetFileExtension); 477 | } 478 | 479 | /// 480 | /// Inject the import of the generated project into the original project, if necessary. 481 | /// 482 | /// The name of the project to import. 483 | private void InjectProject(string generatedTargetFilename) 484 | { 485 | LogMessage($@"Generated target filename: ""{generatedTargetFilename}"""); 486 | if (!string.IsNullOrEmpty(generatedTargetFilename)) 487 | { 488 | XmlDocument document = new XmlDocument(); 489 | var projectPath = Arguments.ProjectPath; 490 | document.Load(projectPath); 491 | var node = document.SelectSingleNode($"/Project/Import[@Project='{generatedTargetFilename}']"); 492 | 493 | LogMessage($"The generated project {(node == null ? "has not" : "has already")} been imported in the original project"); 494 | if (node == null) 495 | { 496 | node = document.CreateElement("Import"); 497 | var attribute = document.CreateAttribute("Project"); 498 | attribute.Value = generatedTargetFilename; 499 | node.Attributes.Append(attribute); 500 | document.SelectSingleNode("/Project").AppendChild(node); 501 | 502 | XmlWriterSettings settings = new XmlWriterSettings 503 | { 504 | CheckCharacters = true, 505 | CloseOutput = true, 506 | Encoding = new UTF8Encoding(false), 507 | Indent = true, 508 | IndentChars = " ", 509 | NewLineChars = "\r\n", 510 | NewLineHandling = NewLineHandling.None, 511 | NewLineOnAttributes = false, 512 | OmitXmlDeclaration = true 513 | }; 514 | using (var stream = new FileStream(projectPath, FileMode.Create, FileAccess.Write, FileShare.None)) 515 | { 516 | var xmlWriter = XmlWriter.Create(stream, settings); 517 | using (xmlWriter) 518 | { 519 | LogMessage("Overwrite the original project"); 520 | document.WriteContentTo(xmlWriter); 521 | xmlWriter.Flush(); 522 | } 523 | } 524 | } 525 | } 526 | } 527 | } 528 | } -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Generation/IGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | /// 4 | /// Interface for all code generators. 5 | /// 6 | public interface IGenerator 7 | { 8 | /// 9 | /// Generate the code. 10 | /// 11 | void Generate(); 12 | 13 | /// 14 | /// Clean all previous generated files. 15 | /// 16 | void CleanGeneratedFiles(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Generators/FreshMvvmGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | 8 | /// 9 | /// FreshMvvm library CSharp generator. 10 | /// 11 | public class FreshMvvmGenerator : CSharpGenerator 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// List of ViewModels to generate. 17 | /// The build arguments. 18 | public FreshMvvmGenerator(List viewModels, Arguments arguments) : base(viewModels, arguments) 19 | { 20 | this.IsRunningPropertyForCommandNeeded = true; 21 | } 22 | 23 | /// 24 | /// Creates the namespace that add specific using for FreshMvvmGenerator. 25 | /// 26 | /// The namespace of the class. 27 | protected override void CreateNamespace(string @namespace) 28 | { 29 | base.CreateNamespace(@namespace); 30 | 31 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("FreshMvvm"))); 32 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Xamarin.Forms"))); 33 | } 34 | 35 | /// 36 | /// Gets the command namespace for FreshMvvm. 37 | /// 38 | /// The command namespace. 39 | protected override string GetAsyncCommandNamespace() => "Xamarin.Forms.Command"; 40 | 41 | /// 42 | /// Gets the command namespace for FreshMvvm. 43 | /// 44 | /// The command namespace. 45 | protected override string GetCommandNamespace() => "Xamarin.Forms.Command"; 46 | 47 | /// 48 | /// Add base ViewModel if it is required by the framework. 49 | /// 50 | /// SyntaxNodeOrToken with the ViewModel base. 51 | protected override SyntaxNodeOrToken AddBaseViewModel() 52 | { 53 | return this.BuildBaseClass("FreshMvvm", "FreshBasePageModel"); 54 | } 55 | 56 | /// 57 | /// Creates the public properties for each Mvvm library. 58 | /// 59 | /// The public properties. 60 | /// The property to generate. 61 | public override PropertyDeclarationSyntax CreatePublicProperties(Property property) 62 | { 63 | return SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(property.Type.FindType()), property.Name) 64 | .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) 65 | .AddAccessorListAccessors( 66 | SyntaxFactory.AccessorDeclaration( 67 | SyntaxKind.GetAccessorDeclaration) 68 | .WithExpressionBody( 69 | SyntaxFactory.ArrowExpressionClause( 70 | SyntaxFactory.MemberAccessExpression( 71 | SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(property.Name.ToCamelCase())))) 72 | .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), 73 | SyntaxFactory.AccessorDeclaration( 74 | SyntaxKind.SetAccessorDeclaration) 75 | .WithBody( 76 | SyntaxFactory.Block( 77 | SyntaxFactory.ExpressionStatement( 78 | SyntaxFactory.AssignmentExpression( 79 | SyntaxKind.SimpleAssignmentExpression, 80 | SyntaxFactory.IdentifierName(property.Name.ToCamelCase()), 81 | SyntaxFactory.IdentifierName("value"))), 82 | SyntaxFactory.ExpressionStatement( 83 | SyntaxFactory.InvocationExpression( 84 | SyntaxFactory.IdentifierName("RaisePropertyChanged")))))) 85 | .WithModifiers(FormatterHelper.GenerateComment(property.Comment)); 86 | } 87 | 88 | /// 89 | /// Get command method syntax, depending on the framework used. 90 | /// 91 | /// The command to generate. 92 | /// The ArgumentListSyntax that represent the command syntax to generate 93 | protected override ArgumentListSyntax GetCommandMethodSyntax(Command command) 94 | { 95 | return CommandHelper.GenerateSafeCommandSyntax(command); 96 | } 97 | 98 | /// 99 | /// Generate command comment to indicate which method associated to the command needs to be implemented. 100 | /// 101 | /// The command. 102 | /// The complete comment to the command. 103 | public override string GenerateCommandComment(Command command) 104 | { 105 | var comment = base.GenerateCommandComment(command); 106 | return CommandHelper.AddCommandExceptionComment(command, comment); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Generators/MvvmCrossGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | using MvvmCodeGenerator.Gen.Helpers; 8 | 9 | /// 10 | /// MvvmCross library CSharp generator. 11 | /// 12 | public class MvvmCrossGenerator : CSharpGenerator 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// List of ViewModels to generate. 18 | /// The build arguments. 19 | public MvvmCrossGenerator(List viewModels, Arguments arguments) : base(viewModels, arguments) 20 | { 21 | } 22 | 23 | /// 24 | /// Creates the namespace that add specific using for Mvvmicro. 25 | /// 26 | /// The namespace of the class. 27 | protected override void CreateNamespace(string @namespace) 28 | { 29 | base.CreateNamespace(@namespace); 30 | 31 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("MvvmCross.Commands"))); 32 | } 33 | 34 | /// 35 | /// Get the entire namespace of the command implementation. 36 | /// 37 | /// The namespace of the command. 38 | protected override string GetCommandNamespace() => "MvvmCross.Commands.MvxCommand"; 39 | 40 | /// 41 | /// Get the entire namespace of the async command implementation. 42 | /// 43 | /// The namespace of the async command. 44 | protected override string GetAsyncCommandNamespace() => "MvvmCross.Commands.MvxAsyncCommand"; 45 | 46 | /// 47 | /// Add base ViewModel if it is required by the framework. 48 | /// 49 | /// SyntaxNodeOrToken with the ViewModel base. 50 | protected override SyntaxNodeOrToken AddBaseViewModel() 51 | { 52 | return this.BuildBaseClass("MvvmCross.ViewModels", "MvxViewModel"); 53 | } 54 | 55 | /// 56 | /// Creates the public properties for each Mvvm library. 57 | /// 58 | /// The public properties. 59 | /// The property to generate. 60 | public override PropertyDeclarationSyntax CreatePublicProperties(Property property) 61 | { 62 | return PropertiesHelper.GeneratePropertiesSyntax(property); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Generators/MvvmLightLibsGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | 8 | /// 9 | /// MvvmLightLibs library CSharp generator. 10 | /// 11 | public class MvvmLightLibsGenerator : CSharpGenerator 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// List of ViewModels to generate. 17 | /// The build arguments. 18 | public MvvmLightLibsGenerator(List viewModels, Arguments arguments) : base(viewModels, arguments) 19 | { 20 | this.IsRunningPropertyForCommandNeeded = true; 21 | } 22 | 23 | /// 24 | /// Creates the namespace that add specific using for MvvmLightLibs. 25 | /// 26 | /// The namespace of the class. 27 | protected override void CreateNamespace(string @namespace) 28 | { 29 | base.CreateNamespace(@namespace); 30 | 31 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("GalaSoft.MvvmLight.Command"))); 32 | } 33 | 34 | /// 35 | /// Gets the command namespace for MvvmLightLibs. 36 | /// 37 | /// The command namespace. 38 | protected override string GetAsyncCommandNamespace() => "GalaSoft.MvvmLight.Command.RelayCommand"; 39 | 40 | /// 41 | /// Gets the command namespace for MvvmLightLibs. 42 | /// 43 | /// The command namespace. 44 | protected override string GetCommandNamespace() => "GalaSoft.MvvmLight.Command.RelayCommand"; 45 | 46 | /// 47 | /// Add base ViewModel if it is required by the framework. 48 | /// 49 | /// SyntaxNodeOrToken with the ViewModel base. 50 | protected override SyntaxNodeOrToken AddBaseViewModel() 51 | { 52 | return this.BuildBaseClass("GalaSoft.MvvmLight", "ViewModelBase"); 53 | } 54 | 55 | /// 56 | /// Get command method syntax, depending on the framework used. 57 | /// 58 | /// The command to generate. 59 | /// The ArgumentListSyntax that represent the command syntax to generate 60 | protected override ArgumentListSyntax GetCommandMethodSyntax(Command command) 61 | { 62 | return CommandHelper.GenerateSafeCommandSyntax(command); 63 | } 64 | 65 | /// 66 | /// Generate command comment to indicate which method associated to the command needs to be implemented. 67 | /// 68 | /// The command. 69 | /// The complete comment to the command. 70 | public override string GenerateCommandComment(Command command) 71 | { 72 | var comment = base.GenerateCommandComment(command); 73 | return CommandHelper.AddCommandExceptionComment(command, comment); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Generators/MvvmicroGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | 7 | /// 8 | /// Mvvmicro library CSharp generator. 9 | /// 10 | public class MvvmicroCSharpGenerator : CSharpGenerator 11 | { 12 | /// 13 | /// Initializes a new instance of the class. 14 | /// 15 | /// List of ViewModels to generate. 16 | /// The build arguments. 17 | public MvvmicroCSharpGenerator(List viewModels, Arguments arguments) : base(viewModels, arguments) 18 | { 19 | } 20 | 21 | /// 22 | /// Creates the namespace that add specific using for Mvvmicro. 23 | /// 24 | /// The namespace of the class. 25 | protected override void CreateNamespace(string @namespace) 26 | { 27 | base.CreateNamespace(@namespace); 28 | 29 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Mvvmicro"))); 30 | } 31 | 32 | /// 33 | /// Gets the command namespace for Mvvmicro. 34 | /// 35 | /// The command namespace. 36 | protected override string GetCommandNamespace() => "Mvvmicro.RelayCommand"; 37 | 38 | /// 39 | /// Gets the async command namespace for Mvvmicro. 40 | /// 41 | /// The async command namespace. 42 | protected override string GetAsyncCommandNamespace() => "Mvvmicro.AsyncRelayCommand"; 43 | 44 | /// 45 | /// Add base ViewModel if it is required by the framework. 46 | /// 47 | /// SyntaxNodeOrToken with the ViewModel base. 48 | protected override SyntaxNodeOrToken AddBaseViewModel() 49 | { 50 | return this.BuildBaseClass("Mvvmicro", "ViewModelBase"); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Generators/PrismCoreGenerator.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | using MvvmCodeGenerator.Gen.Helpers; 8 | 9 | /// 10 | /// Prism library CSharp generator. 11 | /// 12 | public class PrismCoreGenerator : CSharpGenerator 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// List of ViewModels to generate. 18 | /// The build arguments. 19 | public PrismCoreGenerator(List viewModels, Arguments arguments) : base(viewModels, arguments) 20 | { 21 | this.IsRunningPropertyForCommandNeeded = true; 22 | } 23 | 24 | /// 25 | /// Creates the namespace that add specific using for PrismGenerator. 26 | /// 27 | /// The namespace of the class. 28 | protected override void CreateNamespace(string @namespace) 29 | { 30 | base.CreateNamespace(@namespace); 31 | 32 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Prism.Mvvm"))); 33 | this.NamespaceDeclaration = this.NamespaceDeclaration.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("Prism.Commands"))); 34 | } 35 | 36 | /// 37 | /// Gets the command namespace for Prism. 38 | /// 39 | /// The command namespace. 40 | protected override string GetAsyncCommandNamespace() => "Prism.Commands.DelegateCommand"; 41 | 42 | /// 43 | /// Gets the command namespace for Prism. 44 | /// 45 | /// The command namespace. 46 | protected override string GetCommandNamespace() => "Prism.Commands.DelegateCommand"; 47 | 48 | /// 49 | /// Add base ViewModel if it is required by the framework. 50 | /// 51 | /// SyntaxNodeOrToken with the ViewModel base. 52 | protected override SyntaxNodeOrToken AddBaseViewModel() 53 | { 54 | return this.BuildBaseClass("Prism.Mvvm", "BindableBase"); 55 | } 56 | 57 | /// 58 | /// Get command method syntax, depending on the framework used. 59 | /// 60 | /// The command to generate. 61 | /// The ArgumentListSyntax that represent the command syntax to generate 62 | protected override ArgumentListSyntax GetCommandMethodSyntax(Command command) 63 | { 64 | return CommandHelper.GenerateSafeCommandSyntax(command); 65 | } 66 | 67 | /// 68 | /// Generate command comment to indicate which method associated to the command needs to be implemented. 69 | /// 70 | /// The command. 71 | /// The complete comment to the command. 72 | public override string GenerateCommandComment(Command command) 73 | { 74 | var comment = base.GenerateCommandComment(command); 75 | return CommandHelper.AddCommandExceptionComment(command, comment); 76 | } 77 | 78 | /// 79 | /// Creates the public properties for each Mvvm library. 80 | /// 81 | /// The public properties. 82 | /// The property to generate. 83 | public override PropertyDeclarationSyntax CreatePublicProperties(Property property) 84 | { 85 | return PropertiesHelper.GeneratePropertiesSyntax(property); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Helpers/CommandHelper.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | using Microsoft.CodeAnalysis; 5 | using Microsoft.CodeAnalysis.CSharp; 6 | using Microsoft.CodeAnalysis.CSharp.Syntax; 7 | 8 | /// 9 | /// This class help to mutualize some code related to the Commands that are the same for multiple frameworks. 10 | /// 11 | public static class CommandHelper 12 | { 13 | /// 14 | /// Generates the safe command syntax using the try / catch to encapsulate the async commands. 15 | /// 16 | /// The safe command syntax. 17 | /// The command. 18 | public static ArgumentListSyntax GenerateSafeCommandSyntax(Command command) 19 | { 20 | var syntaxeNodeOrToken = new List(); 21 | 22 | if (command.IsAsync) 23 | { 24 | var methodExpression = SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(command.FormatExecuteCommandName())); 25 | 26 | if (command.HasParameterType) 27 | { 28 | methodExpression = methodExpression.WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.IdentifierName("value"))))); 29 | } 30 | 31 | // Wrap the async void command to a try catch using the SafeFireAndForget extension 32 | var arg = SyntaxFactory.ParenthesizedLambdaExpression( 33 | SyntaxFactory.Block( 34 | SyntaxFactory.SingletonList( 35 | SyntaxFactory.TryStatement( 36 | SyntaxFactory.SingletonList( 37 | SyntaxFactory.CatchClause().WithDeclaration( 38 | SyntaxFactory.CatchDeclaration(SyntaxFactory.IdentifierName("System.Exception")).WithIdentifier(SyntaxFactory.Identifier("ex"))) 39 | .WithBlock(SyntaxFactory.Block( 40 | SyntaxFactory.SingletonList(SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(command.FormatCommandExceptionName())) 41 | .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(SyntaxFactory.IdentifierName("ex"))))))))))) 42 | .WithFinally(SyntaxFactory.FinallyClause(SyntaxFactory.Block( 43 | SyntaxFactory.SingletonList( 44 | SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, 45 | SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(command.FormatPropertyIsCommandRunning())), 46 | SyntaxFactory.LiteralExpression(SyntaxKind.FalseLiteralExpression))))))) 47 | .WithBlock( 48 | SyntaxFactory.Block( 49 | SyntaxFactory.ExpressionStatement( 50 | SyntaxFactory.AssignmentExpression( 51 | SyntaxKind.SimpleAssignmentExpression, 52 | SyntaxFactory.MemberAccessExpression( 53 | SyntaxKind.SimpleMemberAccessExpression, 54 | SyntaxFactory.ThisExpression(), 55 | SyntaxFactory.IdentifierName(command.FormatPropertyIsCommandRunning())), 56 | SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression))), 57 | SyntaxFactory.ExpressionStatement(SyntaxFactory.AwaitExpression(methodExpression)) 58 | ))))).WithAsyncKeyword(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)); 59 | 60 | if (command.HasParameterType) 61 | { 62 | arg = arg.WithParameterList(SyntaxFactory.ParameterList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Parameter(SyntaxFactory.Identifier("value"))))); 63 | } 64 | 65 | syntaxeNodeOrToken.Add(SyntaxFactory.Argument(arg)); 66 | } 67 | else 68 | { 69 | syntaxeNodeOrToken.Add(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(command.FormatExecuteCommandName()))); 70 | } 71 | 72 | if (command.HasCanExecute) 73 | { 74 | syntaxeNodeOrToken.Add(SyntaxFactory.Token(SyntaxKind.CommaToken)); 75 | syntaxeNodeOrToken.Add(SyntaxFactory.Argument(SyntaxFactory.IdentifierName(command.FormatCanExecuteCommandName()))); 76 | } 77 | 78 | return SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(syntaxeNodeOrToken)); 79 | } 80 | 81 | /// 82 | /// Add the command exception comment. 83 | /// 84 | /// The command exception comment. 85 | /// The command. 86 | /// The command comment to complete. 87 | public static string AddCommandExceptionComment(Command command, string comment) 88 | { 89 | return string.Concat($"{comment} and {command.FormatCommandExceptionName()}"); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Helpers/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | /// 4 | /// Manage all constants common to the project. 5 | /// 6 | public static class Constants 7 | { 8 | /// 9 | /// Constant value for the name, wihtout extension, of the target generated file. 10 | /// 11 | public const string GeneratedTargetFileWithoutExtension = "MvvmCodeGenMapper"; 12 | 13 | /// 14 | /// Constant value for the file extension of the target generated file. 15 | /// 16 | public const string GeneratedTargetFileExtension = ".g.targets"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Helpers/FileHelper.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Xml.Linq; 7 | using Microsoft.Build.Utilities; 8 | 9 | /// 10 | /// File helper class to manage the generation files. 11 | /// 12 | public static class FileHelper 13 | { 14 | /// 15 | /// Logger defined in the task. 16 | /// 17 | public static TaskLoggingHelper Log { private get; set; } 18 | 19 | /// 20 | /// Clean the specified files dependening on their extensions and their folder path. 21 | /// 22 | /// Folder path. 23 | /// The file extensions. 24 | public static void Clean(string folderPath, string extensions) 25 | { 26 | if (Directory.Exists(folderPath)) 27 | { 28 | foreach (var file in Directory.GetFiles(folderPath)) 29 | { 30 | if (file.EndsWith(extensions, StringComparison.Ordinal)) 31 | { 32 | LogMessage($"Cleaning file: { file}"); 33 | File.Delete(file); 34 | } 35 | } 36 | 37 | foreach (var file in Directory.GetDirectories(folderPath)) 38 | { 39 | Clean(file, extensions); 40 | } 41 | } 42 | } 43 | 44 | /// 45 | /// Saves the content of the file. 46 | /// 47 | /// Output folder. 48 | /// Destination folder. 49 | /// The file content. 50 | /// The File name. 51 | /// The file extension. 52 | public static void SaveFileContent(string outputFolder,string destinationFolder, string content, string fileName, string fileExtension, bool checkIfExist) 53 | { 54 | var folder = Path.Combine(outputFolder, destinationFolder); 55 | 56 | var path = Path.Combine(folder, string.Concat(fileName, fileExtension)); 57 | 58 | if (checkIfExist) 59 | { 60 | if (!File.Exists(path)) 61 | { 62 | Save(path, content); 63 | } 64 | } 65 | else 66 | { 67 | Save(path, content); 68 | } 69 | } 70 | 71 | /// 72 | /// Saves the content of the file. 73 | /// 74 | /// Output folder. 75 | /// Destination folder. 76 | /// The xml file content. 77 | /// The File name. 78 | /// The file extension. 79 | public static void SaveFileContent(string outputFolder, string destinationFolder, XElement content, string fileName, string fileExtension) 80 | { 81 | var folder = Path.Combine(outputFolder, destinationFolder); 82 | 83 | var path = Path.Combine(folder, string.Concat(fileName, fileExtension)); 84 | 85 | using (var memory = new MemoryStream()) 86 | { 87 | content.Save(memory); 88 | 89 | memory.Seek(0, SeekOrigin.Begin); 90 | 91 | using (var reader = new StreamReader(memory)) 92 | { 93 | var text = reader.ReadToEnd(); 94 | Save(path, text); 95 | } 96 | } 97 | } 98 | 99 | /// 100 | /// Reset the target file generated to re-import all ViewModels. 101 | /// 102 | public static void ResetTarget(string outputFolderProject, string generatedTargetFileWithoutExtension, string generatedTargetFileExtension) 103 | { 104 | var xml = new XElement("Project"); 105 | 106 | xml.Add(new XComment("This file has been generated with MvvmCodeGenerator, do not modify it.")); 107 | 108 | var group = new XElement("ItemGroup"); 109 | xml.Add(group); 110 | 111 | SaveFileContent(outputFolderProject, string.Empty, xml, generatedTargetFileWithoutExtension, generatedTargetFileExtension); 112 | string.Concat(generatedTargetFileWithoutExtension, generatedTargetFileExtension); 113 | } 114 | 115 | /// 116 | /// Writes a message into the default logging system. 117 | /// 118 | /// The message to log. 119 | [Conditional("DEBUG")] 120 | private static void LogMessage(string message) 121 | { 122 | if (!string.IsNullOrEmpty(message)) 123 | { 124 | Log?.LogMessage(message); 125 | Console.WriteLine(message); 126 | } 127 | } 128 | 129 | /// 130 | /// Save the specified content into the file path. 131 | /// 132 | /// The path to the file to save data. 133 | /// The content of the class. 134 | private static void Save(string path, string content) 135 | { 136 | if (File.Exists(path)) 137 | { 138 | File.Delete(path); 139 | } 140 | 141 | var folder = Path.GetDirectoryName(path); 142 | 143 | if (!string.IsNullOrEmpty(folder) && !Directory.Exists(folder)) 144 | { 145 | Directory.CreateDirectory(folder); 146 | } 147 | 148 | File.WriteAllText(path, content); 149 | 150 | LogMessage($"Folder destination {folder}"); 151 | LogMessage($"Save file {path}"); 152 | LogMessage(content); 153 | } 154 | 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Helpers/FormatterHelper.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | /// 8 | /// This class help to format the differents properties or files names. 9 | /// 10 | public static class FormatterHelper 11 | { 12 | /// 13 | /// Transform a property name to camel case. 14 | /// 15 | /// The property name. 16 | /// The property in camel case. 17 | public static string ToCamelCase(this string propertyName) 18 | { 19 | return char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1); 20 | } 21 | 22 | /// 23 | /// Get ViewModel suffix dependeing if it's an ItemViewModel or a ViewModel. 24 | /// 25 | /// Return if it's an ItemViewModel or a ViewModel. 26 | /// The suffix. 27 | public static string GetSuffix(this bool isItem) 28 | { 29 | return isItem ? "ItemViewModel" : "ViewModel"; 30 | } 31 | 32 | /// 33 | /// Creates the name of the ViewModel or ItemViewModel. 34 | /// 35 | /// The ViewModel name correctly formatted. 36 | /// The ViewModel name. 37 | public static string CreateViewModelName(this ViewModel viewModel) 38 | { 39 | return viewModel.Key + viewModel.IsItem.GetSuffix(); 40 | } 41 | 42 | /// 43 | /// Creates the name of the interface of the ViewModel or ItemViewModel. 44 | /// 45 | /// The interface of the ViewModel name correctly formatted. 46 | /// The interface ViewModel name. 47 | public static string CreateInterfaceViewModelName(this ViewModel viewModel) 48 | { 49 | return string.Concat("I", CreateViewModelName(viewModel)); 50 | } 51 | 52 | /// 53 | /// Creates the name of the base ViewModel or ItemViewModel. 54 | /// 55 | /// The ViewModel. 56 | /// The base ViewModel name 57 | public static string CreateBaseViewModelName(this ViewModel viewModel) 58 | { 59 | return viewModel.Base + viewModel.IsItem.GetSuffix(); 60 | } 61 | 62 | /// 63 | /// Creates the name of the base interface ViewModel or ItemViewModel. 64 | /// 65 | /// The interface ViewModel. 66 | /// The base interface ViewModel name 67 | public static string CreateInterfaceBaseViewModelName(this ViewModel viewModel) 68 | { 69 | return string.Concat("I", CreateBaseViewModelName(viewModel)); 70 | } 71 | 72 | /// 73 | /// Formats the name of the command. 74 | /// 75 | /// The command name. 76 | /// The command. 77 | public static string FormatCommandName(this Command command) => string.Concat(command.Name, "Command"); 78 | 79 | /// 80 | /// Formats the name of the command execute method. 81 | /// 82 | /// The command. 83 | /// The command name formatted for an execute method. 84 | public static string FormatExecuteCommandName(this Command command) => string.Concat("Execute", command.FormatCommandName(), command.IsAsync ? "Async" : string.Empty); 85 | 86 | /// 87 | /// Formats the can execute command method associated to the command. 88 | /// 89 | /// The command. 90 | /// The can execute command method name. 91 | public static string FormatCanExecuteCommandName(this Command command) => "CanExecute" + command.FormatCommandName(); 92 | 93 | /// 94 | /// Formats command exception name. 95 | /// 96 | /// The command. 97 | /// The can execute command method name. 98 | public static string FormatCommandExceptionName(this Command command) => string.Concat("OnExecute", command.FormatCommandName(), "AsyncError"); 99 | 100 | /// 101 | /// Formats the name of the property when the command is running. 102 | /// 103 | /// The property name when the command is running. 104 | /// The command. 105 | public static string FormatPropertyIsCommandRunning(this Command command) => string.Concat("Is", command.FormatCommandName(), "Running"); 106 | 107 | /// 108 | /// Generate warning message on top of all the files. 109 | /// 110 | /// The generated warning comment. 111 | /// Namespace declaration. 112 | public static NamespaceDeclarationSyntax AutoGeneratedCommand(this NamespaceDeclarationSyntax namespaceDeclaration) 113 | { 114 | return namespaceDeclaration.WithNamespaceKeyword( 115 | SyntaxFactory.Token( 116 | SyntaxFactory.TriviaList( 117 | new[]{ 118 | SyntaxFactory.Comment("//------------------------------------------------------------------------------ "), 119 | SyntaxFactory.Comment("// "), 120 | SyntaxFactory.Comment("// This code was generated by MvvmCodeGenerator."), 121 | SyntaxFactory.Comment($"// Runtime Version:{System.Environment.Version}"), 122 | SyntaxFactory.Comment("// "), 123 | SyntaxFactory.Comment("// Changes to this file may cause incorrect behavior and will be lost if"), 124 | SyntaxFactory.Comment("// the code is regenerated."), 125 | SyntaxFactory.Comment("// "), 126 | SyntaxFactory.Comment("//------------------------------------------------------------------------------") 127 | }), 128 | SyntaxKind.NamespaceKeyword, 129 | SyntaxTriviaList.Empty)); 130 | } 131 | 132 | /// 133 | /// Generates the comment from the description inside the MvvmCodeGenMapper.xml. 134 | /// 135 | /// The SyntaxTokenList that represent the comment that will be generated. 136 | /// The comment to generate. 137 | public static SyntaxTokenList GenerateComment(string comment) 138 | { 139 | return SyntaxFactory.TokenList( 140 | SyntaxFactory.Token( 141 | SyntaxFactory.TriviaList( 142 | new[]{ 143 | SyntaxFactory.Comment("/// "), 144 | SyntaxFactory.Comment(string.Concat("// ", comment)), 145 | SyntaxFactory.Comment("/// ") 146 | }), 147 | SyntaxKind.PublicKeyword, 148 | SyntaxTriviaList.Empty)); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Helpers/PropertiesHelper.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen.Helpers 2 | { 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp; 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | /// 8 | /// This class help to mutualize some code related to the Properties that are the same for multiple frameworks. 9 | /// 10 | public static class PropertiesHelper 11 | { 12 | /// 13 | /// Creates the public properties for each Mvvm library. 14 | /// 15 | /// The public properties. 16 | /// The property to generate. 17 | public static PropertyDeclarationSyntax GeneratePropertiesSyntax(Property property) 18 | { 19 | return SyntaxFactory.PropertyDeclaration(SyntaxFactory.ParseTypeName(property.Type.FindType()), property.Name) 20 | .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) 21 | .AddAccessorListAccessors( 22 | SyntaxFactory.AccessorDeclaration( 23 | SyntaxKind.GetAccessorDeclaration) 24 | .WithExpressionBody( 25 | SyntaxFactory.ArrowExpressionClause( 26 | SyntaxFactory.MemberAccessExpression( 27 | SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(property.Name.ToCamelCase())))) 28 | .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)), 29 | SyntaxFactory.AccessorDeclaration( 30 | SyntaxKind.SetAccessorDeclaration) 31 | .WithExpressionBody( 32 | SyntaxFactory.ArrowExpressionClause( 33 | SyntaxFactory.InvocationExpression( 34 | SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName("SetProperty"))) 35 | .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList( 36 | new SyntaxNodeOrToken[] 37 | { 38 | SyntaxFactory.Argument(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(property.Name.ToCamelCase()))) 39 | .WithRefKindKeyword(SyntaxFactory.Token(SyntaxKind.RefKeyword)), SyntaxFactory.Token(SyntaxKind.CommaToken), SyntaxFactory.Argument(SyntaxFactory.IdentifierName("value")) 40 | }))))) 41 | .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))) 42 | .WithModifiers(FormatterHelper.GenerateComment(property.Comment)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Helpers/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System; 4 | 5 | /// 6 | /// Manage the Type of the different class and properties. 7 | /// 8 | public static class TypeExtensions 9 | { 10 | /// 11 | /// Finds the type of the object defined in the MvvmCodeGenMapper.xml file as a string. 12 | /// 13 | /// The complete namespace with the type of the object for example : System.Boolean. 14 | /// The type name. 15 | public static string FindType(this string name) 16 | { 17 | switch (name) 18 | { 19 | case "string": 20 | return typeof(string).ToString(); 21 | case "int": 22 | return typeof(int).ToString(); 23 | case "long": 24 | return typeof(long).ToString(); 25 | case "bool": 26 | return typeof(bool).ToString(); 27 | case "float": 28 | return typeof(float).ToString(); 29 | case "object": 30 | return typeof(object).ToString(); 31 | case "double": 32 | return typeof(double).ToString(); 33 | case "DateTime": 34 | return typeof(DateTime).ToString(); 35 | case "DateTimeOffset": 36 | return typeof(DateTimeOffset).ToString(); 37 | case "TimeSpan": 38 | return typeof(TimeSpan).ToString(); 39 | case var list when name.StartsWith("list ", StringComparison.InvariantCulture): 40 | var paramType = FindType(list.Substring("list ".Length).Trim()); 41 | return $"System.Collections.Generic.IList<{paramType}>"; 42 | default: 43 | return name; 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/MvvmCodeGenerator.Gen.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | PreserveNewest 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/MvvmCodeGenerator.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | <_CleanTaskRequired>true 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/MvvmCodeGeneratorBaseTask.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System; 4 | using System.IO; 5 | using Microsoft.Build.Framework; 6 | using Microsoft.Build.Utilities; 7 | 8 | public abstract class MvvmCodeGeneratorBaseTask : Task 9 | { 10 | #region Properties 11 | 12 | [Required] 13 | public string ProjectPath { get; set; } 14 | 15 | [Required] 16 | public ITaskItem SourceFile { get; set; } 17 | 18 | #endregion 19 | 20 | public override sealed bool Execute() 21 | { 22 | if (!File.Exists(this.SourceFile.ItemSpec)) 23 | { 24 | Log.LogError(null, null, null, SourceFile.ItemSpec, 0, 0, 0, 0, $"file {this.SourceFile} not found"); 25 | return false; 26 | } 27 | 28 | try 29 | { 30 | return OnExecute(); 31 | } 32 | catch (Exception e) 33 | { 34 | Log.LogMessage("Error: {0}", e.Message); 35 | Log.LogError(null, null, null, SourceFile.ItemSpec, 0, 0, 0, 0, $"{e.Message}"); 36 | return false; 37 | } 38 | } 39 | 40 | /// 41 | /// Specific work done by each concrete task. 42 | /// 43 | /// true if the task is successful; false otherwise. 44 | protected abstract bool OnExecute(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/MvvmCodeGeneratorCleanTask.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.IO; 4 | 5 | public class MvvmCodeGeneratorCleanTask : MvvmCodeGeneratorBaseTask 6 | { 7 | protected override bool OnExecute() 8 | { 9 | var projectFolder = Path.GetDirectoryName(this.ProjectPath); 10 | 11 | FileHelper.Log = Log; 12 | FileHelper.ResetTarget(projectFolder, Constants.GeneratedTargetFileWithoutExtension, Constants.GeneratedTargetFileExtension); 13 | FileHelper.Clean(projectFolder, "interface.g.cs"); 14 | FileHelper.Clean(projectFolder, "part.g.cs"); 15 | 16 | return true; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/MvvmCodeGeneratorTask.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.IO; 4 | using System.Xml; 5 | 6 | public class MvvmCodeGeneratorTask : MvvmCodeGeneratorBaseTask 7 | { 8 | protected override bool OnExecute() 9 | { 10 | try 11 | { 12 | Log.LogMessage($"Loading xml : {this.SourceFile.ItemSpec}"); 13 | 14 | var projectFolder = Path.GetDirectoryName(this.ProjectPath); 15 | var path = Path.Combine(projectFolder, this.SourceFile.ItemSpec); 16 | 17 | Log.LogMessage($"ProjectFolder: {projectFolder}"); 18 | Log.LogMessage($"Path : {path}"); 19 | 20 | Arguments arguments = new Arguments 21 | { 22 | ProjectPath = ProjectPath, 23 | OutputFolderProject = projectFolder 24 | }; 25 | 26 | Bootstrap.Start(path, arguments, Log); 27 | 28 | Log.LogMessage("End of generation."); 29 | 30 | return true; 31 | } 32 | catch (XmlException xe) 33 | { 34 | Log.LogMessage("Error (xml): {0}", xe.Message); 35 | Log.LogError(null, null, null, SourceFile.ItemSpec, xe.LineNumber, xe.LinePosition, 0, 0, $"{xe.Message}"); 36 | 37 | return false; 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Parsing/XmlParser.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Xml; 6 | 7 | /// 8 | /// The Xml parser to get C# objects. 9 | /// 10 | public class XmlParser 11 | { 12 | public ResourceFile ReadResourceFile(string content) 13 | { 14 | XmlDocument xml = new XmlDocument(); 15 | xml.LoadXml(content); 16 | 17 | XmlNodeList generatorNode = xml.GetElementsByTagName("Generator"); 18 | var generator = generatorNode[0].Attributes["Value"]?.Value; 19 | 20 | var viewModels = new List(); 21 | 22 | XmlNodeList allViewModelsTag = xml.GetElementsByTagName("ViewModels"); 23 | 24 | foreach (XmlElement viewModelTag in allViewModelsTag) 25 | { 26 | var @namespace = viewModelTag.Attributes["Namespace"]?.Value; 27 | var destinationFolder = viewModelTag.Attributes["DestinationFolder"]?.Value ?? string.Empty; 28 | viewModels.AddRange(this.GetViewModels(viewModelTag, @namespace, destinationFolder)); 29 | } 30 | 31 | 32 | return new ResourceFile 33 | { 34 | Generator = generator, 35 | ViewModels = viewModels 36 | }; 37 | } 38 | 39 | /// 40 | /// Parse the xml to a list of ViewModels. 41 | /// 42 | /// The xml resource file. 43 | /// The destination namespace. 44 | /// The destination folder for the generated files. 45 | /// The list of ViewModels parsed. 46 | private List GetViewModels(XmlElement xml, string @namespace, string destinationFolder) 47 | { 48 | var viewModels = new List(); 49 | 50 | var viewModelsTag = xml.GetElementsByTagName("ViewModel"); 51 | var itemViewModelsTag = xml.GetElementsByTagName("ItemViewModel"); 52 | var allViewModelsTag = viewModelsTag.Cast().Concat(itemViewModelsTag.Cast()).ToList(); 53 | 54 | foreach (XmlNode node in allViewModelsTag) 55 | { 56 | var isItemViewModel = node.Name == "ItemViewModel"; 57 | var key = node.Attributes["Key"]?.Value; 58 | var baseViewModel = node.Attributes["Base"]?.Value; 59 | 60 | var viewModel = new ViewModel(isItemViewModel, key, baseViewModel, @namespace, destinationFolder); 61 | 62 | if (node.HasChildNodes) 63 | { 64 | foreach (XmlNode child in node.ChildNodes.Cast().Where(n => n.NodeType == XmlNodeType.Element)) 65 | { 66 | var name = child.Attributes["Name"]?.Value; 67 | var comment = child.Attributes["Description"]?.Value; 68 | 69 | if (child.Name == "Property") 70 | { 71 | var parameterType = child.Attributes["Type"]?.Value; 72 | viewModel.Properties.Add(new Property(name, parameterType, comment, true, true)); 73 | } 74 | else if (child.Name.Contains("Command")) 75 | { 76 | var isAsync = child.Name.Split(new[] { "Command" }, System.StringSplitOptions.None)[0] == "Async"; 77 | var canExecute = child.Attributes["CanExecute"]?.Value.ToLower() == "true"; 78 | var parameterType = child.Attributes["Parameter"]?.Value; 79 | viewModel.Commands.Add(new Command(isAsync, name, parameterType, comment, canExecute)); 80 | } 81 | } 82 | } 83 | 84 | viewModels.Add(viewModel); 85 | } 86 | 87 | return viewModels; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Resources/Arguments.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | /// 4 | /// This class represent all the build arguments needed to generate the code. 5 | /// 6 | public class Arguments 7 | { 8 | /// 9 | /// Gets or sets the path to the project file. 10 | /// 11 | /// The path to the project file. 12 | public string ProjectPath { get; set; } 13 | 14 | /// 15 | /// Gets or sets the output folder project to generate the class. 16 | /// 17 | /// The output folder project. 18 | public string OutputFolderProject { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Resources/Command.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | /// 4 | /// This class represent the commands define in the Resource.xml files. 5 | /// 6 | public class Command 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// If set to true is async. 12 | /// The name of the Command. 13 | /// The type of parameter to pass to it. 14 | /// The comment associated to this command. 15 | /// If set to true can execute. 16 | public Command(bool isAsync, string name, string parameterType, string comment, bool canExecute) 17 | { 18 | this.IsAsync = isAsync; 19 | this.Name = name; 20 | this.ParameterType = parameterType; 21 | this.Comment = comment; 22 | this.HasCanExecute = canExecute; 23 | } 24 | 25 | /// 26 | /// Gets the type of the parameter. 27 | /// 28 | /// The type of the parameter. 29 | public string ParameterType { get; } 30 | 31 | /// 32 | /// Gets a value indicating whether this has parameter type. 33 | /// 34 | /// true if has parameter type; otherwise, false. 35 | public bool HasParameterType => !string.IsNullOrEmpty(this.ParameterType); 36 | 37 | /// 38 | /// Gets the name. 39 | /// 40 | /// The name. 41 | public string Name { get; } 42 | 43 | /// 44 | /// Gets a value indicating whether this is async. 45 | /// 46 | /// true if is async; otherwise, false. 47 | public bool IsAsync { get; } 48 | 49 | /// 50 | /// Gets the comment. 51 | /// 52 | /// The comment. 53 | public string Comment { get; } 54 | 55 | /// 56 | /// Gets a value indicating whether this has can execute. 57 | /// 58 | /// true if has can execute; otherwise, false. 59 | public bool HasCanExecute { get; } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Resources/Property.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | /// 4 | /// This class represent the properties define in the Resource.xml files for the ViewModels. 5 | /// 6 | public class Property 7 | { 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// The name of the property. 12 | /// The type of the property. 13 | /// The comment associated with it. 14 | /// If set to true has get. 15 | /// If set to true has set. 16 | public Property(string name, string type, string comment, bool hasGet, bool hasSet) 17 | { 18 | this.Name = name; 19 | this.Type = type; 20 | this.Comment = comment; 21 | this.HasGet = hasGet; 22 | this.HasSet = hasSet; 23 | } 24 | 25 | /// 26 | /// Gets the name. 27 | /// 28 | /// The name. 29 | public string Name { get; } 30 | 31 | /// 32 | /// Gets the type. 33 | /// 34 | /// The type. 35 | public string Type { get; } 36 | 37 | /// 38 | /// Gets the comment. 39 | /// 40 | /// The comment. 41 | public string Comment { get; } 42 | 43 | /// 44 | /// Gets a value indicating whether this has get. 45 | /// 46 | /// true if has get; otherwise, false. 47 | public bool HasGet { get; } 48 | 49 | /// 50 | /// Gets a value indicating whether this has set. 51 | /// 52 | /// true if has set; otherwise, false. 53 | public bool HasSet { get; } 54 | } 55 | } -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Resources/ResourceFile.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | 5 | /// 6 | /// The model that represent the ResourceFile. 7 | /// 8 | public class ResourceFile 9 | { 10 | /// 11 | /// Generator needed. 12 | /// 13 | public string Generator { get; set; } 14 | 15 | /// 16 | /// The list of ViewModels to generate. 17 | /// 18 | public List ViewModels { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Resources/ViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace MvvmCodeGenerator.Gen 2 | { 3 | using System.Collections.Generic; 4 | 5 | /// 6 | /// This class represent the ViewModel defined in the MvvmCodeGenMapper.xml 7 | /// 8 | public class ViewModel 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// If set to true this is an ItemViewModel. 14 | /// The Key correspond to the prefix of the ViewModel. 15 | /// The Base ViewModel if it exist. 16 | /// The namespace of the ViewModel. 17 | /// The Destination folder for the ViewModel. 18 | public ViewModel(bool isItem, string key, string baseViewModel, string @namespace, string destinationFolder) 19 | { 20 | this.Namespace = @namespace; 21 | this.Key = key; 22 | this.Base = baseViewModel; 23 | this.DestinationFolder = destinationFolder; 24 | this.IsItem = isItem; 25 | } 26 | 27 | /// 28 | /// Gets the namespace. 29 | /// 30 | /// The namespace. 31 | public string Namespace { get; } 32 | 33 | /// 34 | /// Gets the key. 35 | /// 36 | /// The key. 37 | public string Key { get; } 38 | 39 | /// 40 | /// Gets the base. 41 | /// 42 | /// The base. 43 | public string Base { get; } 44 | 45 | /// 46 | /// Gets the destination folder. 47 | /// 48 | /// The destination folder. 49 | public string DestinationFolder { get; } 50 | 51 | /// 52 | /// Gets a value indicating whether this has base. 53 | /// 54 | /// true if has base; otherwise, false. 55 | public bool HasBase => !string.IsNullOrEmpty(this.Base); 56 | 57 | /// 58 | /// Gets a value indicating whether this is item. 59 | /// 60 | /// true if is item; otherwise, false. 61 | public bool IsItem { get; } 62 | 63 | /// 64 | /// Gets the properties. 65 | /// 66 | /// The properties. 67 | public List Properties { get; } = new List(); 68 | 69 | /// 70 | /// Gets the commands. 71 | /// 72 | /// The commands. 73 | public List Commands { get; } = new List(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.Gen/Settings.StyleCop: -------------------------------------------------------------------------------- 1 |  2 | 3 | NoMerge 4 | en-US 5 | 6 | Dll 7 | 8 | 9 | 10 | 11 | 12 | False 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | False 22 | 23 | 24 | 25 | 26 | False 27 | 28 | 29 | 30 | 31 | https://github.com/damienaicheh/MvvmCodeGenerator 32 | MS-PL 33 | True 34 | True 35 | False 36 | 37 | 38 | 39 | 40 | 41 | as 42 | do 43 | id 44 | if 45 | in 46 | ip 47 | is 48 | mx 49 | my 50 | no 51 | on 52 | to 53 | ui 54 | vs 55 | x 56 | y 57 | z 58 | 59 | 60 | 61 | 62 | 63 | False 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Sources/MvvmCodeGenerator.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvvmCodeGenerator.Gen", "MvvmCodeGenerator.Gen\MvvmCodeGenerator.Gen.csproj", "{291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Sample", "Xamarin.Sample\Xamarin.Sample.csproj", "{9AFF23DF-47D3-4120-87DC-F680877AB278}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvvmCodeGenerator.Dev", "MvvmCodeGenerator.Dev\MvvmCodeGenerator.Dev.csproj", "{57090D7A-D459-44DC-B9FB-7F05F329F8DE}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | Debug|iPhoneSimulator = Debug|iPhoneSimulator 15 | Release|iPhoneSimulator = Release|iPhoneSimulator 16 | Debug|iPhone = Debug|iPhone 17 | Release|iPhone = Release|iPhone 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 25 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 26 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 27 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 28 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Debug|iPhone.ActiveCfg = Debug|Any CPU 29 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Debug|iPhone.Build.0 = Debug|Any CPU 30 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Release|iPhone.ActiveCfg = Release|Any CPU 31 | {291A7D13-3FFA-49E8-8DB0-5869A1A8B9C2}.Release|iPhone.Build.0 = Release|Any CPU 32 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 37 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 38 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 39 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 40 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Debug|iPhone.ActiveCfg = Debug|Any CPU 41 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Debug|iPhone.Build.0 = Debug|Any CPU 42 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Release|iPhone.ActiveCfg = Release|Any CPU 43 | {9AFF23DF-47D3-4120-87DC-F680877AB278}.Release|iPhone.Build.0 = Release|Any CPU 44 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU 49 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU 50 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU 51 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU 52 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Debug|iPhone.ActiveCfg = Debug|Any CPU 53 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Debug|iPhone.Build.0 = Debug|Any CPU 54 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Release|iPhone.ActiveCfg = Release|Any CPU 55 | {57090D7A-D459-44DC-B9FB-7F05F329F8DE}.Release|iPhone.Build.0 = Release|Any CPU 56 | EndGlobalSection 57 | EndGlobal 58 | -------------------------------------------------------------------------------- /Sources/Xamarin.Sample/Models/House.cs: -------------------------------------------------------------------------------- 1 | namespace Xamarin.Sample.Models 2 | { 3 | public class House 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/Xamarin.Sample/MvvmCodeGenMapper.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Sources/Xamarin.Sample/Xamarin.Sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------