PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/cinch/V1 (VS2008 WPF Only)/cinch/CinchCodeGen/Persistence/ViewModelPersistence.cs

#
C# | 902 lines | 613 code | 128 blank | 161 comment | 29 complexity | 02b593bcd9213af47e95e99aa5f04196 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO;
  6. using System.Xml;
  7. using System.Xml.Serialization;
  8. using System.Windows;
  9. using Cinch;
  10. using System.Reflection;
  11. namespace CinchCodeGen
  12. {
  13. /// <summary>
  14. /// Provides public methods to allow the current InMemoryViewModel to be serialized to and from
  15. /// an XML file on disk
  16. /// </summary>
  17. public static class ViewModelPersistence
  18. {
  19. #region Data
  20. private static IMessageBoxService MessageBoxService =
  21. ViewModelBase.ServiceProvider.Resolve<IMessageBoxService>();
  22. #endregion
  23. #region Public Methods
  24. /// <summary>
  25. /// Serializes a PesistentVM to disk, in a XML file format
  26. /// </summary>
  27. /// <param name="fileName">The file name of the ViewModel to save</param>
  28. /// <param name="vmToPersist">The actual serializable ViewModel</param>
  29. /// <returns>True if the save operation succeeds</returns>
  30. public static Boolean PersistViewModel(String fileName, PesistentVM vmToPersist)
  31. {
  32. try
  33. {
  34. FileInfo file = new FileInfo(fileName);
  35. if (!file.Extension.Equals(".xml"))
  36. throw new NotSupportedException(
  37. String.Format("The file name {0} you picked is not supported\r\n\r\nOnly .xml files are valid",
  38. file.Name));
  39. if (vmToPersist == null)
  40. throw new NotSupportedException("The ViewModel is null");
  41. //write the file to disk
  42. XmlSerializer serializer = new XmlSerializer(typeof(PesistentVM));
  43. using (TextWriter writer = new StreamWriter(file.FullName))
  44. {
  45. serializer.Serialize(writer, vmToPersist);
  46. }
  47. return true;
  48. }
  49. catch (Exception ex)
  50. {
  51. throw ex;
  52. }
  53. }
  54. /// <summary>
  55. /// Strips all extension off a file name
  56. /// </summary>
  57. /// <param name="wholeFileName">The entire file name</param>
  58. /// <returns>A stripped filename string</returns>
  59. public static String StripFileName(String wholeFileName)
  60. {
  61. String justFilePart=wholeFileName;
  62. do
  63. {
  64. justFilePart =justFilePart.LastIndexOf(".") > 0 ?
  65. justFilePart.Substring(0, justFilePart.LastIndexOf("."))
  66. : justFilePart;
  67. }while (justFilePart.LastIndexOf(".") > 0);
  68. return justFilePart;
  69. }
  70. /// <summary>
  71. /// Creates the ViewModel code in C# format
  72. /// </summary>
  73. /// <param name="fileName">The file name of the ViewModel to save</param>
  74. /// <param name="vmToPersist">The actual serializable ViewModel</param>
  75. /// <returns>True if the save operation succeeds</returns>
  76. public static Boolean CreateViewModelCode(String fileName, PesistentVM vmToPersist)
  77. {
  78. try
  79. {
  80. if (vmToPersist == null)
  81. throw new NotSupportedException("The ViewModel is null");
  82. FileInfo file = new FileInfo(fileName);
  83. String allCode = CreateAllCodeParts(vmToPersist);
  84. Tuple<Boolean,String> compilationSucceeded = CompileGeneratedCode(allCode);
  85. //check for .g.cs
  86. String justFilePart = StripFileName(fileName);
  87. //Write the Auto generated file part
  88. String autoGeneratedFileName = String.Format("{0}.g.cs", justFilePart);
  89. String autoGeneratedFilePath = Path.Combine(file.Directory.FullName, autoGeneratedFileName);
  90. String autoGenPartContents = CreateAutoGeneratedPart(vmToPersist, true);
  91. //No need to carry on if user says don't generate 1st part
  92. if (!SaveGeneratedCode(compilationSucceeded.First,compilationSucceeded.Second,
  93. autoGeneratedFileName, autoGenPartContents))
  94. return false;
  95. //Write the Custom file part
  96. String customFileName = String.Format("{0}.cs", justFilePart);
  97. String customFilePath = Path.Combine(file.Directory.FullName, customFileName);
  98. String customPartContents = CreateCustomPart(vmToPersist, true);
  99. return SaveGeneratedCode(compilationSucceeded.First, compilationSucceeded.Second,
  100. customFileName, customPartContents);
  101. }
  102. catch (Exception ex)
  103. {
  104. throw ex;
  105. }
  106. }
  107. /// <summary>
  108. /// DeSerializes an XML file into a PesistentVM
  109. /// (if the xml is of the correct formatting)
  110. /// </summary>
  111. /// <param name="fileName">The file name of the ViewModel to open</param>
  112. /// <returns>The XML read PesistentVM, or null if it can't be read</returns>
  113. public static PesistentVM HydratePersistedViewModel(String fileName)
  114. {
  115. try
  116. {
  117. FileInfo file = new FileInfo(fileName);
  118. if (!file.Extension.Equals(".xml"))
  119. throw new NotSupportedException(
  120. String.Format("The file name {0} you picked is not supported\r\n\r\nOnly .xml files are valid",
  121. file.Name));
  122. //read the file from disk
  123. XmlSerializer serializer = new XmlSerializer(typeof(PesistentVM));
  124. serializer.UnknownNode += Serializer_UnknownNode;
  125. serializer.UnknownAttribute += Serializer_UnknownAttribute;
  126. PesistentVM vmToHydrate = null;
  127. using (FileStream fs = new FileStream(file.FullName, FileMode.Open))
  128. {
  129. vmToHydrate = (PesistentVM)serializer.Deserialize(fs);
  130. }
  131. return vmToHydrate;
  132. }
  133. catch (Exception ex)
  134. {
  135. throw ex;
  136. }
  137. }
  138. #endregion
  139. #region Private Methods
  140. /// <summary>
  141. /// Compiles and generates the code, and returns a tuple of success/failure and the
  142. /// generated filename
  143. /// </summary>
  144. /// <param name="code">The code to compile</param>
  145. private static Tuple<Boolean, String> CompileGeneratedCode(String code)
  146. {
  147. bool succeeded = false;
  148. try
  149. {
  150. succeeded = DynamicCompiler.ComplileCodeBlock(code);
  151. return TupleHelper.New(true, String.Empty);
  152. }
  153. catch (Exception ex)
  154. {
  155. return TupleHelper.New(false, ex.Message);
  156. }
  157. }
  158. /// <summary>
  159. /// If the compilationSucceeded is false, the user is asks if they would
  160. /// still like to generate the code anyway, and if they say yes the code
  161. /// is generated. If the compilationSucceeded is true, the code is simply
  162. /// generated. In either case this method returns true if the operation was
  163. /// successful.
  164. /// </summary>
  165. private static Boolean SaveGeneratedCode(Boolean compilationSucceeded,
  166. String compilationErrorMsg, String fileName, String code)
  167. {
  168. if (!compilationSucceeded)
  169. {
  170. if (MessageBoxService.ShowYesNo(
  171. "There was an issue with the code file\r\n" +
  172. compilationErrorMsg + "\r\n\r\n" +
  173. "But this could be due to a missing import required by one of the properties\r\n" +
  174. "Generate anyway?", CustomDialogIcons.Question) == CustomDialogResults.Yes)
  175. {
  176. using (TextWriter writer = new StreamWriter(fileName))
  177. writer.Write(code);
  178. return true;
  179. }
  180. else
  181. {
  182. return false;
  183. }
  184. }
  185. else
  186. {
  187. using (TextWriter writer = new StreamWriter(fileName))
  188. writer.Write(code);
  189. return true;
  190. }
  191. }
  192. /// <summary>
  193. /// Creates and returns a single generated code String which has both parts
  194. /// of the partial class, wcich can then be sent to the compilation phase
  195. /// as a single file. The single code string is generated based on the vmToPersist
  196. /// parameter
  197. /// </summary>
  198. private static String CreateAllCodeParts(PesistentVM vmToPersist)
  199. {
  200. //The order is important, do not change the order here
  201. //otherwise it will not work
  202. String autoPart = CreateAutoGeneratedPart(vmToPersist, false);
  203. String customPart = CreateCustomPart(vmToPersist, false);
  204. StringBuilder sb = new StringBuilder(1000);
  205. sb.Append(autoPart);
  206. sb.Append(customPart);
  207. sb.AppendLine("}");
  208. return sb.ToString();
  209. }
  210. /// <summary>
  211. /// Creates and returns an auto generated part String, based on the vmToPersist
  212. /// parameter. The returned string will form tne auto generated part (.g.cs)
  213. /// </summary>
  214. private static String CreateAutoGeneratedPart(PesistentVM vmToPersist, Boolean shouldEmit)
  215. {
  216. StringBuilder sb = new StringBuilder(1000);
  217. //Write out namespaces
  218. #region Namespaces
  219. sb.AppendLine("using System;");
  220. sb.AppendLine("using System.Collections.Generic;");
  221. sb.AppendLine("using System.Linq;");
  222. sb.AppendLine("using System.ComponentModel;");
  223. sb.AppendLine("using System.Collections.ObjectModel;");
  224. sb.AppendLine("using System.Windows.Data;");
  225. sb.AppendLine("using System.Collections.Specialized;");
  226. //write out referenced assemblies Using statements
  227. List<String> refAssUsingStments =
  228. ReferencedAssembliesHelper.GetReferencedAssembliesUsingStatements();
  229. if (refAssUsingStments.Count > 0)
  230. {
  231. sb.AppendLine();
  232. sb.AppendLine("//Referenced assemblies");
  233. foreach (String usingStmnt in refAssUsingStments)
  234. {
  235. sb.AppendLine(usingStmnt);
  236. }
  237. sb.AppendLine();
  238. }
  239. sb.AppendLine("using Cinch;");
  240. sb.AppendLine();
  241. #endregion
  242. #region Class code
  243. sb.AppendLine(String.Format("namespace {0}", vmToPersist.VMNamespace));
  244. sb.AppendLine("{");
  245. sb.AppendLine("\t/// <summary>");
  246. sb.AppendLine("\t///NOTE : This class was auto generated by a tool");
  247. sb.AppendLine("\t///Edit this code at your peril!!!!!!");
  248. sb.AppendLine("\t/// </summary>");
  249. sb.AppendLine(String.Format("\tpublic partial class {0} : Cinch.{1}",
  250. vmToPersist.VMName, vmToPersist.InheritenceVMType.ToString()));
  251. sb.AppendLine("\t{");
  252. //write Data
  253. #region Data
  254. sb.AppendLine("\t\t#region Data");
  255. foreach (var prop in vmToPersist.VMProperties)
  256. sb.AppendLine(prop.FieldDeclaration);
  257. sb.AppendLine("\t\t//callbacks to allow auto generated part to notify custom part, when property changes");
  258. sb.AppendLine("\t\tprivate Dictionary<String, Action> autoPartPropertyCallBacks = new Dictionary<String, Action>();");
  259. sb.AppendLine("\t\t#endregion");
  260. sb.AppendLine("\r\n");
  261. #endregion
  262. //write get/set declarations
  263. #region Props
  264. sb.AppendLine("\t\t#region Public Properties");
  265. foreach (var prop in vmToPersist.VMProperties)
  266. sb.AppendLine(prop.PropGetSet);
  267. sb.AppendLine("\t\t#endregion");
  268. #endregion
  269. #region EditableValidatingObject overrides
  270. var propsWithWrappers =
  271. vmToPersist.VMProperties.Where(x => x.UseDataWrapper == true);
  272. //EditableValidatingObject overrides
  273. if (vmToPersist.VMType == ViewModelType.ValidatingAndEditable
  274. && propsWithWrappers.Count() > 0)
  275. {
  276. sb.AppendLine("\t\t#region EditableValidatingObject overrides");
  277. sb.AppendLine("\t\t/// <summary>");
  278. sb.AppendLine("\t\t/// Override hook which allows us to also put any child");
  279. sb.AppendLine("\t\t/// EditableValidatingObject objects into the BeginEdit state");
  280. sb.AppendLine("\t\t/// </summary>");
  281. sb.AppendLine("\t\tprotected override void OnBeginEdit()");
  282. sb.AppendLine("\t\t{");
  283. sb.AppendLine("\t\t base.OnBeginEdit();");
  284. sb.AppendLine("\t\t //Now walk the list of properties in the ViewModel");
  285. sb.AppendLine("\t\t //and call BeginEdit() on all Cinch.DataWrapper<T>s.");
  286. sb.AppendLine("\t\t //we can use the Cinch.DataWrapperHelper class for this");
  287. sb.AppendLine("\t\t DataWrapperHelper.SetBeginEdit(cachedListOfDataWrappers);");
  288. sb.AppendLine("\t\t}");
  289. sb.AppendLine();
  290. sb.AppendLine("\t\t/// <summary>");
  291. sb.AppendLine("\t\t/// Override hook which allows us to also put any child");
  292. sb.AppendLine("\t\t/// EditableValidatingObject objects into the EndEdit state");
  293. sb.AppendLine("\t\t/// </summary>");
  294. sb.AppendLine("\t\tprotected override void OnEndEdit()");
  295. sb.AppendLine("\t\t{");
  296. sb.AppendLine("\t\t base.OnEndEdit();");
  297. sb.AppendLine("\t\t //Now walk the list of properties in the ViewModel");
  298. sb.AppendLine("\t\t //and call CancelEdit() on all Cinch.DataWrapper<T>s.");
  299. sb.AppendLine("\t\t //we can use the Cinch.DataWrapperHelper class for this");
  300. sb.AppendLine("\t\t DataWrapperHelper.SetEndEdit(cachedListOfDataWrappers);");
  301. sb.AppendLine("\t\t}");
  302. sb.AppendLine();
  303. sb.AppendLine("\t\t/// <summary>");
  304. sb.AppendLine("\t\t/// Override hook which allows us to also put any child ");
  305. sb.AppendLine("\t\t/// EditableValidatingObject objects into the CancelEdit state");
  306. sb.AppendLine("\t\t/// </summary>");
  307. sb.AppendLine("\t\tprotected override void OnCancelEdit()");
  308. sb.AppendLine("\t\t{");
  309. sb.AppendLine("\t\t base.OnCancelEdit();");
  310. sb.AppendLine("\t\t //Now walk the list of properties in the ViewModel");
  311. sb.AppendLine("\t\t //and call CancelEdit() on all Cinch.DataWrapper<T>s.");
  312. sb.AppendLine("\t\t //we can use the Cinch.DataWrapperHelper class for this");
  313. sb.AppendLine("\t\t DataWrapperHelper.SetCancelEdit(cachedListOfDataWrappers);");
  314. sb.AppendLine("\t\t}");
  315. sb.AppendLine("\t\t#endregion");
  316. }
  317. #endregion
  318. #endregion
  319. sb.AppendLine("\t}");
  320. if (shouldEmit)
  321. sb.AppendLine("}");
  322. return sb.ToString();
  323. }
  324. /// <summary>
  325. /// Creates and returns an custom generated part String, based on the vmToPersist
  326. /// parameter. The returned string will form tne auto generated part (.cs)
  327. /// </summary>
  328. private static String CreateCustomPart(PesistentVM vmToPersist, Boolean shouldEmit)
  329. {
  330. StringBuilder sb = new StringBuilder(1000);
  331. //write out namespaces
  332. #region Namespaces
  333. if (shouldEmit)
  334. {
  335. sb.AppendLine("using System;");
  336. sb.AppendLine("using System.Collections.Generic;");
  337. sb.AppendLine("using System.Linq;");
  338. sb.AppendLine("using System.ComponentModel;");
  339. sb.AppendLine("using System.Collections.ObjectModel;");
  340. sb.AppendLine("using System.Windows.Data;");
  341. sb.AppendLine("using System.Collections.Specialized;");
  342. //write out referenced assemblies Using statements
  343. List<String> refAssUsingStments =
  344. ReferencedAssembliesHelper.GetReferencedAssembliesUsingStatements();
  345. if (refAssUsingStments.Count > 0)
  346. {
  347. sb.AppendLine("//Referenced assemblies");
  348. foreach (String usingStmnt in refAssUsingStments)
  349. {
  350. sb.AppendLine(usingStmnt);
  351. }
  352. sb.AppendLine();
  353. }
  354. sb.AppendLine("using Cinch;");
  355. sb.AppendLine();
  356. sb.AppendLine(String.Format("namespace {0}", vmToPersist.VMNamespace));
  357. sb.AppendLine("{");
  358. }
  359. #endregion
  360. //Write out data
  361. #region Data
  362. sb.AppendLine("\t/// <summary>");
  363. sb.AppendLine("\t///You may edit this code by hand, but there is DataWrapper code");
  364. sb.AppendLine("\t///and some boiler plate code provided here, to help you on your way.");
  365. sb.AppendLine("\t///A lot of which is actually quite useful, and a lot of thought has been");
  366. sb.AppendLine("\t///put into, what code to place in which file parts, and this custom part");
  367. sb.AppendLine("\t///does have some excellent starting code, so use it as you wish.");
  368. sb.AppendLine("\t///");
  369. sb.AppendLine("\t///But please note : One area that will need to be examined closely if you decide to introduce");
  370. sb.AppendLine("\t///New DataWrapper<T> properties in this part, is the IsValid override");
  371. sb.AppendLine("\t///Which will need to include the dataWrappers something like:");
  372. sb.AppendLine("\t///<pre>");
  373. sb.AppendLine("\t/// return base.IsValid &&");
  374. sb.AppendLine("\t/// DataWrapperHelper.AllValid(cachedListOfDataWrappers);");
  375. sb.AppendLine("\t///</pre>");
  376. sb.AppendLine("\t/// </summary>");
  377. sb.AppendLine(String.Format("\tpublic partial class {0}", vmToPersist.VMName));
  378. sb.AppendLine("\t{");
  379. //write fields
  380. var propsWithWrappers = vmToPersist.VMProperties.Where(x => x.UseDataWrapper == true);
  381. if (propsWithWrappers.Count() > 0)
  382. {
  383. sb.AppendLine("\t\t#region Data");
  384. sb.AppendLine("\t\tprivate IEnumerable<DataWrapperBase> cachedListOfDataWrappers;");
  385. sb.AppendLine("\t\tprivate ViewMode currentViewMode = ViewMode.AddMode;");
  386. sb.AppendLine("\t\t//Example rule declaration : YOU WILL NEED TO DO THIS BIT");
  387. sb.AppendLine("\t\t//private static SimpleRule quantityRule;");
  388. sb.AppendLine("\t\t#endregion");
  389. }
  390. sb.AppendLine();
  391. #endregion
  392. //Ctor
  393. #region Ctor
  394. sb.AppendLine("\t\t#region Ctor");
  395. sb.AppendLine(String.Format("\t\tpublic {0}()", vmToPersist.VMName));
  396. sb.AppendLine("\t\t{");
  397. //write out auto generated part callbacks
  398. sb.AppendLine();
  399. sb.AppendLine("\t\t\t#region Create Auto Generated Property Callbacks");
  400. sb.AppendLine("\t\t\t//Create callbacks for auto generated properties in auto generated partial class part");
  401. sb.AppendLine("\t\t\t//Which allows this part to know when a property in the generated part changes");
  402. foreach (var prop in vmToPersist.VMProperties)
  403. {
  404. sb.AppendLine(prop.CustomPartCtorCallBack);
  405. }
  406. sb.AppendLine("\t\t\t#endregion");
  407. //write out wrappers if they are needed
  408. if (propsWithWrappers.Count() > 0)
  409. {
  410. sb.AppendLine("\t\t\t#region Create DataWrappers");
  411. foreach (var propWithWrapper in propsWithWrappers)
  412. sb.AppendLine(propWithWrapper.ConstructorDeclaration);
  413. sb.AppendLine("\t\t\t//fetch list of all DataWrappers, so they can be used again later without the");
  414. sb.AppendLine("\t\t\t//need for reflection");
  415. sb.AppendLine("\t\t\tcachedListOfDataWrappers =");
  416. sb.AppendLine(String.Format(
  417. "\t\t\t DataWrapperHelper.GetWrapperProperties<{0}>(this);", vmToPersist.VMName));
  418. sb.AppendLine("\t\t\t#endregion");
  419. }
  420. //write out example validation, if this ViewModel type supports them
  421. if (vmToPersist.VMType != ViewModelType.Standard)
  422. {
  423. sb.AppendLine();
  424. sb.AppendLine("\t\t\t// #region TODO : You WILL need to create YOUR OWN validation rules");
  425. sb.AppendLine("\t\t\t// //Here is an example of how to create a validation rule");
  426. sb.AppendLine("\t\t\t// //you can use this as a guide to create your own validation rules");
  427. sb.AppendLine("\t\t\t// quantity.AddRule(quantityRule);");
  428. sb.AppendLine("\t\t\t// #endregion");
  429. }
  430. sb.AppendLine("\t\t}");
  431. //write out static Constructor
  432. sb.AppendLine();
  433. sb.AppendLine(String.Format("\t\tstatic {0}()", vmToPersist.VMName));
  434. sb.AppendLine("\t\t{");
  435. sb.AppendLine("\t\t\t//quantityRule = new SimpleRule(\"DataValue\", \"Quantity can not be < 0\",");
  436. sb.AppendLine("\t\t\t// (Object domainObject)=>");
  437. sb.AppendLine("\t\t\t// {");
  438. sb.AppendLine("\t\t\t// DataWrapper<Int32> obj = (DataWrapper<Int32>)domainObject;");
  439. sb.AppendLine("\t\t\t// return obj.DataValue <= 0;");
  440. sb.AppendLine("\t\t\t// });");
  441. sb.AppendLine("\t\t}");
  442. sb.AppendLine("\t\t#endregion");
  443. #endregion
  444. //Write out actual auto generated property changed callback stubs
  445. #region Property Changed Stubs
  446. sb.AppendLine();
  447. sb.AppendLine("\t\t#region Auto Generated Property Changed CallBacks");
  448. sb.AppendLine("\t\t//Callbacks which are called whenever an auto generated property in auto generated partial class part changes");
  449. sb.AppendLine("\t\t//Which allows this part to know when a property in the generated part changes");
  450. foreach (var prop in vmToPersist.VMProperties)
  451. {
  452. sb.AppendLine(prop.CustomPartActualCallBack);
  453. }
  454. sb.AppendLine("\t\t#endregion");
  455. #endregion
  456. //region ViewMode property
  457. #region ViewMode property (which allows all DataWrappers to be put into a specific ViewMode state)
  458. if (propsWithWrappers.Count() > 0)
  459. {
  460. sb.AppendLine("\t\t/// <summary>");
  461. sb.AppendLine("\t\t/// The current ViewMode, when changed will loop");
  462. sb.AppendLine("\t\t/// through all nested DataWrapper objects and change");
  463. sb.AppendLine("\t\t/// their state also");
  464. sb.AppendLine("\t\t/// </summary>");
  465. sb.AppendLine("\t\tstatic PropertyChangedEventArgs currentViewModeChangeArgs =");
  466. sb.AppendLine(String.Format(
  467. "\t\t ObservableHelper.CreateArgs<{0}>(x => x.CurrentViewMode);",
  468. vmToPersist.VMName));
  469. sb.AppendLine();
  470. sb.AppendLine("\t\tpublic ViewMode CurrentViewMode");
  471. sb.AppendLine("\t\t{");
  472. sb.AppendLine("\t\t get { return currentViewMode; }");
  473. sb.AppendLine("\t\t set");
  474. sb.AppendLine("\t\t {");
  475. sb.AppendLine("\t\t currentViewMode = value;");
  476. sb.AppendLine("\t\t //Now change all the cachedListOfDataWrappers");
  477. sb.AppendLine("\t\t //Which sets all the Cinch.DataWrapper<T>s to the correct IsEditable");
  478. sb.AppendLine("\t\t //state based on the new ViewMode applied to the ViewModel");
  479. sb.AppendLine("\t\t //we can use the Cinch.DataWrapperHelper class for this");
  480. sb.AppendLine("\t\t DataWrapperHelper.SetMode(");
  481. sb.AppendLine("\t\t cachedListOfDataWrappers,");
  482. sb.AppendLine("\t\t currentViewMode);");
  483. sb.AppendLine();
  484. sb.AppendLine("\t\t NotifyPropertyChanged(currentViewModeChangeArgs);");
  485. sb.AppendLine("\t\t }");
  486. sb.AppendLine("\t\t}");
  487. }
  488. #endregion
  489. //Overrides (IsValid)
  490. #region Overrides
  491. if (vmToPersist.VMType != ViewModelType.Standard)
  492. {
  493. sb.AppendLine();
  494. sb.AppendLine("\t\t#region Overrides");
  495. sb.AppendLine("\t\t/// <summary>");
  496. sb.AppendLine("\t\t/// Override hook which allows us to also put any child ");
  497. sb.AppendLine("\t\t/// EditableValidatingObject objects IsValid state into");
  498. sb.AppendLine("\t\t/// a combined IsValid state for the whole ViewModel,");
  499. sb.AppendLine("\t\t/// should you need to do so");
  500. sb.AppendLine("\t\t/// </summary>");
  501. sb.AppendLine("\t\tpublic override bool IsValid");
  502. sb.AppendLine("\t\t{");
  503. sb.AppendLine("\t\t get");
  504. sb.AppendLine("\t\t {");
  505. if (propsWithWrappers.Count() > 0)
  506. {
  507. sb.AppendLine("\t\t //return base.IsValid and use DataWrapperHelper, as you are");
  508. sb.AppendLine("\t\t //using DataWrappers");
  509. sb.AppendLine("\t\t return base.IsValid &&");
  510. sb.AppendLine("\t\t DataWrapperHelper.AllValid(cachedListOfDataWrappers);");
  511. }
  512. else
  513. {
  514. sb.AppendLine("\t\t //simply return base.IsValid as you are not");
  515. sb.AppendLine("\t\t //using DataWrappers");
  516. sb.AppendLine("\t\t return base.IsValid;");
  517. }
  518. sb.AppendLine("\t\t }");
  519. sb.AppendLine("\t\t}");
  520. sb.AppendLine("\t\t#endregion");
  521. }
  522. #endregion
  523. sb.AppendLine("\t}");
  524. if (shouldEmit)
  525. sb.AppendLine("}");
  526. return sb.ToString();
  527. }
  528. /// <summary>
  529. /// Fault handling for reading an unknown node from the specified Xml file
  530. /// </summary>
  531. private static void Serializer_UnknownNode(object sender, XmlNodeEventArgs e)
  532. {
  533. MessageBox.Show(String.Format("Unknown Node: {0} \t {1}", e.Name, e.Text),
  534. "Error reading file",
  535. MessageBoxButton.OK, MessageBoxImage.Error);
  536. }
  537. /// <summary>
  538. /// Fault handling for reading an unknown attribute from the specified Xml file
  539. /// </summary>
  540. private static void Serializer_UnknownAttribute(object sender, XmlAttributeEventArgs e)
  541. {
  542. MessageBox.Show(String.Format("Unknown attribute: {0} ='{1}'", e.Attr.Name, e.Attr.Value),
  543. "Error reading file",
  544. MessageBoxButton.OK, MessageBoxImage.Error);
  545. }
  546. #endregion
  547. }
  548. /// <summary>
  549. /// Represents a light weight persistable ViewModel
  550. /// </summary>
  551. public class PesistentVM
  552. {
  553. #region Ctor
  554. public PesistentVM()
  555. {
  556. VMProperties = new List<PesistentVMSingleProperty>();
  557. }
  558. #endregion
  559. #region Public Properties
  560. /// <summary>
  561. /// ViewModel type
  562. /// </summary>
  563. public String InheritenceVMType
  564. {
  565. get
  566. {
  567. switch (VMType)
  568. {
  569. case ViewModelType.Standard:
  570. return "ViewModelBase";
  571. case ViewModelType.Validating:
  572. return "ValidatingViewModelBase";
  573. case ViewModelType.ValidatingAndEditable:
  574. return "EditableValidatingViewModelBase";
  575. default:
  576. return "ViewModelBase";
  577. }
  578. }
  579. }
  580. /// <summary>
  581. /// ViewModel type
  582. /// </summary>
  583. public ViewModelType VMType { get; set; }
  584. /// <summary>
  585. /// Viewmodel name
  586. /// </summary>
  587. public String VMName { get; set; }
  588. /// <summary>
  589. /// ViewModel namespace
  590. /// </summary>
  591. public String VMNamespace { get; set; }
  592. /// <summary>
  593. /// Nested properties
  594. /// </summary>
  595. public List<PesistentVMSingleProperty> VMProperties { get; set; }
  596. #endregion
  597. }
  598. /// <summary>
  599. /// Represents a light weight persistable ViewModel property
  600. /// </summary>
  601. public class PesistentVMSingleProperty
  602. {
  603. #region Private Properties
  604. /// <summary>
  605. /// Gets lower cased property name
  606. /// </summary>
  607. private String LowerPropName
  608. {
  609. get
  610. {
  611. return this.PropName.Substring(0, 1).ToLower() +
  612. this.PropName.Substring(1);
  613. }
  614. }
  615. /// <summary>
  616. /// Gets upper cased property name
  617. /// </summary>
  618. private String UpperPropName
  619. {
  620. get
  621. {
  622. return this.PropName.Substring(0, 1).ToUpper() +
  623. this.PropName.Substring(1);
  624. }
  625. }
  626. #endregion
  627. #region Public Properties
  628. /// <summary>
  629. /// Property name
  630. /// </summary>
  631. public String PropName { get; set; }
  632. /// <summary>
  633. /// Property type, Double, Int32 etc etc
  634. /// </summary>
  635. public String PropertyType { get; set; }
  636. /// <summary>
  637. /// Use a DataWrapper
  638. /// </summary>
  639. public Boolean UseDataWrapper { get; set; }
  640. /// <summary>
  641. /// Parent ViewModel name
  642. /// </summary>
  643. public String ParentViewModelName { get; set; }
  644. /// <summary>
  645. /// Generated constructor declaration string
  646. /// </summary>
  647. public String ConstructorDeclaration
  648. {
  649. get
  650. {
  651. #region Example text generated
  652. //HomePhoneNumber = new DataWrapper<String>(this, homePhoneNumberChangeArgs);
  653. #endregion
  654. return
  655. UseDataWrapper ?
  656. String.Format("\t\t\t{0} = new Cinch.DataWrapper<{1}>(this,{2}ChangeArgs, {2}Callback);",
  657. UpperPropName, PropertyType, LowerPropName) :
  658. String.Empty;
  659. }
  660. }
  661. /// <summary>
  662. /// Generated field declaration string
  663. /// </summary>
  664. public String FieldDeclaration
  665. {
  666. get
  667. {
  668. #region Example text generated
  669. //private Cinch.DataWrapper<String> homePhoneNumber;
  670. //private String homePhoneNumber;
  671. #endregion
  672. return
  673. UseDataWrapper ?
  674. String.Format("\t\tprivate Cinch.DataWrapper<{0}> {1};", PropertyType, LowerPropName) :
  675. String.Format("\t\tprivate {0} {1};", PropertyType, LowerPropName);
  676. }
  677. }
  678. /// <summary>
  679. /// Provides the Custom part Ctor Action callback strings
  680. /// </summary>
  681. public String CustomPartCtorCallBack
  682. {
  683. #region Example Text Generated
  684. //Action firstNameCallBack = new Action(FistNameChanged);
  685. //autoPartPropertyCallBacks.Add(firstNameChangeArgs.PropertyName,firstNameCallBack);
  686. #endregion
  687. get
  688. {
  689. StringBuilder sb = new StringBuilder(200);
  690. sb.AppendLine(String.Format("\t\t\tAction {0}Callback = new Action({1}Changed);", LowerPropName, UpperPropName));
  691. sb.AppendLine(String.Format("\t\t\tautoPartPropertyCallBacks.Add({0}ChangeArgs.PropertyName,{0}Callback);", LowerPropName));
  692. return sb.ToString();
  693. }
  694. }
  695. /// <summary>
  696. /// Provides the Custom part Ctor Action callback strings
  697. /// </summary>
  698. public String CustomPartActualCallBack
  699. {
  700. #region Example Text Generated
  701. //private void FistNameChanged()
  702. //{
  703. // //You can insert code here that needs to run when the FistName property changes
  704. //}
  705. #endregion
  706. get
  707. {
  708. StringBuilder sb = new StringBuilder(200);
  709. sb.AppendLine(String.Format("\t\tprivate void {0}Changed()", UpperPropName));
  710. sb.AppendLine("\t\t{");
  711. sb.AppendLine(String.Format("\t\t //You can insert code here that needs to run when the {0} property changes", UpperPropName));
  712. sb.AppendLine("\t\t}");
  713. return sb.ToString();
  714. }
  715. }
  716. /// <summary>
  717. /// Property Get/Set string
  718. /// </summary>
  719. public String PropGetSet
  720. {
  721. get
  722. {
  723. #region Example text generated
  724. ///// <summary>
  725. ///// FirstName
  726. ///// </summary>
  727. //static PropertyChangedEventArgs firstNameChangeArgs =
  728. // ObservableHelper.CreateArgs<CustomerModel>(x => x.FirstName);
  729. //public Cinch.DataWrapper<String> FirstName
  730. //{
  731. // get { return firstName; }
  732. // set
  733. // {
  734. // firstName = value;
  735. // NotifyPropertyChanged(firstNameChangeArgs);
  736. // }
  737. //}
  738. #endregion
  739. StringBuilder sb = new StringBuilder(1000);
  740. sb.AppendLine(String.Format("\t\t#region {0}", UpperPropName));
  741. sb.AppendLine(String.Format("\t\t/// <summary>"));
  742. sb.AppendLine(String.Format("\t\t/// {0}", UpperPropName));
  743. sb.AppendLine(String.Format("\t\t/// </summary>"));
  744. sb.AppendLine(String.Format("\t\tstatic PropertyChangedEventArgs {0}ChangeArgs =",
  745. LowerPropName));
  746. sb.AppendLine(String.Format("\t\t\tObservableHelper.CreateArgs<{0}>(x => x.{1});",
  747. ParentViewModelName, UpperPropName));
  748. sb.AppendLine("\r\n");
  749. sb.AppendLine(String.Format("\t\tpublic {0} {1}",
  750. UseDataWrapper ? String.Format("Cinch.DataWrapper<{0}>", PropertyType) : PropertyType,
  751. UpperPropName));
  752. sb.AppendLine("\t\t{");
  753. sb.AppendLine("\t\t\tget { return " + LowerPropName +"; }");
  754. sb.AppendLine(UseDataWrapper ? "\t\t\tprivate set" : "\t\t\tset");
  755. sb.AppendLine("\t\t\t{");
  756. sb.AppendLine(String.Format("\t\t\t\t{0} = value;",LowerPropName));
  757. sb.AppendLine(String.Format("\t\t\t\tNotifyPropertyChanged({0}ChangeArgs);", LowerPropName));
  758. sb.AppendLine("\t\t\t\t//Use callback to provide non auto generated part of partial");
  759. sb.AppendLine("\t\t\t\t//class with notification, when an auto generated property value changes");
  760. sb.AppendLine("\t\t\t\tAction callback = null;");
  761. sb.AppendLine("\t\t\t\tif (autoPartPropertyCallBacks.TryGetValue(");
  762. sb.AppendLine(String.Format("\t\t\t\t {0}ChangeArgs.PropertyName, out callback))", LowerPropName));
  763. sb.AppendLine("\t\t\t\t{");
  764. sb.AppendLine("\t\t\t\t callback();");
  765. sb.AppendLine("\t\t\t\t}");
  766. sb.AppendLine("\t\t\t}");
  767. sb.AppendLine("\t\t}");
  768. sb.AppendLine("\t\t#endregion");
  769. return sb.ToString();
  770. }
  771. }
  772. #endregion
  773. }
  774. }