前のやつにCommandも定義出来るように追加してみました。
namespace VMDsl { using System.Collections.Generic; // 名前空間 public class NSDef { public NSDef() { this.Classes = new List<ClassDef>(); } public string NS { get; set; } public List<ClassDef> Classes { get; private set; } } // クラスの定義 public class ClassDef { public ClassDef(NSDef ns) { this.NS = ns; this.Properties = new List<PropertyDef>(); this.Commands = new List<CommandDef>(); this.NS.Classes.Add(this); } public NSDef NS { get; private set; } public string Name { get; set; } public List<PropertyDef> Properties { get; private set; } public List<CommandDef> Commands { get; private set; } } // プロパティの定義 public class PropertyDef { public PropertyDef() { this.Attributes = new List<string>(); } public string Type { get; set; } public string Name { get; set; } public List<string> Attributes { get; private set; } } // コマンド public class CommandDef { public string Name { get; set; } public string CommandClass { get; set; } public string ExecuteMethodName { get; set; } public string CommandParameterType { get; set; } } // 流れるようなインターフェースの起点 public static class Namespace { public static NSDef Make(string name) { return new NSDef { NS = name }; } } // 流れるようなインターフェースのための拡張メソッド public static class ObjectExtensions { // クラスを追加する public static ClassDef Class(this NSDef self, string name) { var clazz = new ClassDef(self) { Name = name }; return clazz; } // プロパティをクラスに追加する public static ClassDef Property(this ClassDef self, string type, string name, params string[] attrs) { var prop = new PropertyDef { Type = type, Name = name }; prop.Attributes.AddRange(attrs); self.Properties.Add(prop); return self; } // コマンドをクラスに追加する public static ClassDef Command(this ClassDef self, string name, string commandParameterType) { var command = new CommandDef { Name = name, // とりあえずBlendで用意されてるActionCommandにする CommandClass = "Microsoft.Expression.Interactivity.Core.ActionCommand", ExecuteMethodName = name + "_Execute", CommandParameterType = commandParameterType }; self.Commands.Add(command); return self; } public static ClassDef Command(this ClassDef self, string name) { return self.Command(name, "object"); } // クラスの定義を終わる public static NSDef EndClass(this ClassDef self) { return self.NS; } } }
テンプレートは以下のように変更しました。そろそろ何がなんだかわからなくなってきました・・・。
<# foreach (var n in ns) { #> namespace <#= n.NS #> { using System; using System.ComponentModel.DataAnnotations; <# foreach (var clazz in n.Classes) { #> public partial class <#= clazz.Name #> : WpfApplication15.ViewModelBase { // Properties <# foreach (var p in clazz.Properties) { #> private <#= p.Type #> _<#= p.Name #>; <# foreach (var attr in p.Attributes) { #> [<#= attr #>] <# } #> public <#= p.Type #> <#= p.Name #> { get { return _<#= p.Name #>; } set { if (Equals(value, _<#= p.Name #>)) { return; } Validator.ValidateProperty( value, new ValidationContext(this, null, null) { MemberName = "<#= p.Name #>" }); _<#= p.Name #> = value; base.OnPropertyChanged("<#= p.Name #>"); } } <# } #> // Commands <# foreach (var command in clazz.Commands) { #> private System.Windows.Input.ICommand _<#= command.Name #>; public System.Windows.Input.ICommand <#= command.Name #> { get { return _<#= command.Name #> = _<#= command.Name #> ?? new <#= command.CommandClass #>(o => <#= command.ExecuteMethodName #>((<#= command.CommandParameterType #>) o)); } } partial void <#= command.ExecuteMethodName #>(<#= command.CommandParameterType #> arg); <# } #> } <# } #> } <# } #>
こんな風にメタデータを定義します。
<#@ assembly name="$(ProjectDir)\T4\VMDsl.dll" #> <#@ import namespace="VMDsl" #> <# var ns = new[] { Namespace.Make("WpfApplication15.ViewModels") .Class("PersonViewModel") .Property("string", "Name", "Required(ErrorMessage=\"だめよ\")") .Property("int?", "Age") .Command("Hoge") .EndClass() }; #>
こんなViewModelが定義されます。パーシャルメソッドでコマンドの実行メソッドが定義されるので、パーシャルクラスを定義してメソッドを定義するだけでOKです。
// <autogenerated /> namespace WpfApplication15.ViewModels { using System; using System.ComponentModel.DataAnnotations; public partial class PersonViewModel : WpfApplication15.ViewModelBase { // Properties private string _Name; [Required(ErrorMessage="だめよ")] public string Name { get { return _Name; } set { if (Equals(value, _Name)) { return; } Validator.ValidateProperty( value, new ValidationContext(this, null, null) { MemberName = "Name" }); _Name = value; base.OnPropertyChanged("Name"); } } private int? _Age; public int? Age { get { return _Age; } set { if (Equals(value, _Age)) { return; } Validator.ValidateProperty( value, new ValidationContext(this, null, null) { MemberName = "Age" }); _Age = value; base.OnPropertyChanged("Age"); } } // Commands private System.Windows.Input.ICommand _Hoge; public System.Windows.Input.ICommand Hoge { get { return _Hoge = _Hoge ?? new Microsoft.Expression.Interactivity.Core.ActionCommand(o => Hoge_Execute((object) o)); } } partial void Hoge_Execute(object arg); } }
プロジェクトは以下からダウンロードできます。
T4VMDsl2.zip