Visual Studio 2010でサービス参照を追加したときに作成されるクラスは、INotifyPropertyChangedまで実装してくれてる。
(VS2008のは未確認。実装してたっけか?)
このT4 Template使えばIEditableObjectが動くように出来るかも。
リフレクション使ってるので、速度的にはハードコーディングするのより遅いけど、お手軽なので。
(因みに未実行コードなので、動くかどうかは謎だけど、それっぽいコードが生成されてた)
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ output extension=".cs" #> <#@ assembly name="System.Xml.Linq.dll" #> <#@ assembly name="System.Xml.dll" #> <#@ assembly name="System.Core.dll" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Xml.Linq" #> <#@ import namespace="System.Collections.Generic" #> <# Init(); #> <# foreach (var clazz in classes) { #> namespace <#= ns #>.<#= clazz.Namespace #> { using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using System.Linq; public partial class <#= clazz.Name #> : IEditableObject { // プロパティと名前のマップ private Dictionary<string, PropertyInfo> _propertyCache; // 編集開始時点の値を確保しておくためのマップ private Dictionary<string, object> _cacheValues = new Dictionary<string, object>(); // プロパティと名前のマップを取得する private Dictionary<string, PropertyInfo> Properties { get { // 一応キャッシュ if (_propertyCache == null) { _propertyCache = GetType().GetProperties().ToDictionary(p => p.Name); } return _propertyCache; } } #region IEditableObject Members // 編集中かどうかのフラグ private bool _isEditing; public void BeginEdit() { if (_isEditing) return; // プロパティの現在の値をバックアップ foreach (var props in Properties) { _cacheValues[props.Key] = props.Value.GetValue(this, null); } } public void CancelEdit() { if (!_isEditing) return; _isEditing = false; // キャンセルなので、編集開始時点の値に書き戻す foreach (var props in Properties) { props.Value.SetValue( this, _cacheValues[props.Key], null); } _cacheValues.Clear(); } public void EndEdit() { if (!_isEditing) return; _isEditing = false; // 編集確定なので、編集開始時点の値は捨てる _cacheValues.Clear(); } #endregion } } <# } #> <#+ // 名前空間だけは指定しておく string ns = "Okazuki.WPF.TestApp"; // ここに、Service References以下で作成されたクラスの情報が入る List<ClassInfo> classes = new List<ClassInfo>(); // クラス class ClassInfo { public string Namespace { get; set; } public string Name { get; set; } } public void Init() { // テンプレートのあるフォルダの下のService Referencesの下にあるフォルダを取得 var path = Path.Combine(Path.GetDirectoryName(Host.TemplateFile), "Service References"); var dirs = Directory.GetDirectories(path); foreach (var dir in dirs) { // xsdファイルに色々定義されてるので、それを取得 var files = Directory.GetFiles(dir, "*.xsd"); ProcessFiles(dir, files); } } void ProcessFiles(string dir, string[] files) { foreach (var file in files) { // 多分*1.xsdというファイルにWCFのサービスの引数や戻り値で使うクラスが定義されてる if (file.EndsWith("1.xsd")) { ProcessFile(dir, file); } } } void ProcessFile(string dir, string file) { var elm = XElement.Load(file); XNamespace xs = "http://www.w3.org/2001/XMLSchema"; classes.AddRange( from complexType in elm.Descendants(xs + "complexType") where // ArrayOfで始まる奴は配列なので見ない !complexType.Attribute("name").Value.StartsWith("ArrayOf") select new ClassInfo { Namespace = Path.GetFileName(dir), Name = complexType.Attribute("name").Value }); } #>
適当な、WCFサービスを作ってサービス参照に追加してみたら、以下のようなコードが生成されました。
namespace Okazuki.WPF.TestApp.EmployeesManagement { using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using System.Linq; public partial class Employees : IEditableObject { // プロパティと名前のマップ private Dictionary<string, PropertyInfo> _propertyCache; // 編集開始時点の値を確保しておくためのマップ private Dictionary<string, object> _cacheValues = new Dictionary<string, object>(); // プロパティと名前のマップを取得する private Dictionary<string, PropertyInfo> Properties { get { // 一応キャッシュ if (_propertyCache == null) { _propertyCache = GetType().GetProperties().ToDictionary(p => p.Name); } return _propertyCache; } } #region IEditableObject Members // 編集中かどうかのフラグ private bool _isEditing; public void BeginEdit() { if (_isEditing) return; // プロパティの現在の値をバックアップ foreach (var props in Properties) { _cacheValues[props.Key] = props.Value.GetValue(this, null); } } public void CancelEdit() { if (!_isEditing) return; _isEditing = false; // キャンセルなので、編集開始時点の値に書き戻す foreach (var props in Properties) { props.Value.SetValue( this, _cacheValues[props.Key], null); } _cacheValues.Clear(); } public void EndEdit() { if (!_isEditing) return; _isEditing = false; // 編集確定なので、編集開始時点の値は捨てる _cacheValues.Clear(); } #endregion } } namespace Okazuki.WPF.TestApp.EmployeesManagement { using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using System.Linq; public partial class Sample : IEditableObject { // プロパティと名前のマップ private Dictionary<string, PropertyInfo> _propertyCache; // 編集開始時点の値を確保しておくためのマップ private Dictionary<string, object> _cacheValues = new Dictionary<string, object>(); // プロパティと名前のマップを取得する private Dictionary<string, PropertyInfo> Properties { get { // 一応キャッシュ if (_propertyCache == null) { _propertyCache = GetType().GetProperties().ToDictionary(p => p.Name); } return _propertyCache; } } #region IEditableObject Members // 編集中かどうかのフラグ private bool _isEditing; public void BeginEdit() { if (_isEditing) return; // プロパティの現在の値をバックアップ foreach (var props in Properties) { _cacheValues[props.Key] = props.Value.GetValue(this, null); } } public void CancelEdit() { if (!_isEditing) return; _isEditing = false; // キャンセルなので、編集開始時点の値に書き戻す foreach (var props in Properties) { props.Value.SetValue( this, _cacheValues[props.Key], null); } _cacheValues.Clear(); } public void EndEdit() { if (!_isEditing) return; _isEditing = false; // 編集確定なので、編集開始時点の値は捨てる _cacheValues.Clear(); } #endregion } }
本当は、Entityというような名前のクラスを作って、それを継承させようと思ったらサービス参照の追加で生成されるコードが、object型を明示的に継承してるので、怒られた。残念。