|
|
| Telerik OpenAccess ORM |
Send comments on this topic. |
| How to: Implement INotifyPropertyChanging/ed Interface (C#) |
|
Programmer's Guide > Developer's Guide > Code Generation > Customizing Code Generation > How to: Implement INotifyPropertyChanging/ed Interface (C#) |
This topic describes how to modify the code generation templates used by OpenAccess Visual Designer, in order to implement automatically INotifyPropertyChanging/ed interfaces for C# persistent classes.
 |
The complete code snippets are located at the end of the topic. |
To implement INotifyPropertyChanging/ed interfaces:
- Copy/paste the code generation templates in your Visual Studio project as it is described in the Best Practices for Customizing the Code Generation Templates section.
- Open the General and Specific templates.
- The INotifyPropertyChanging/ed interfaces are part of the System.ComponentModel namespace. By default that namespace is not included in the generated persistent classes. The first step is include that namespace. Open the General template. Navigate to the InitializeDefaultUsings method (in the original version of the template, the beginning of the method should be located at line 48). The purpose of this method is to initialize the default usings. Add the System.ComponentModel namespace like the code snippet below:
| C# |
Copy Code |
|
private void InitializeDefaultUsings() { if (Usings.Count < 1) { Usings.Add("System"); Usings.Add("System.Data"); Usings.Add("System.Linq"); Usings.Add("System.Linq.Expressions"); Usings.Add("System.Data.Common"); Usings.Add("System.Collections.Generic"); Usings.Add("Telerik.OpenAccess");
Usings.Add("System.ComponentModel"); } } |
- Open the Specific template. Navigate to the GenerateClass method (in the original version of the template, the beginning of the method should be located at line 133). This method is responsible for the class generation. First you need to indicate that the class implements INotifyPropertyChanging and INotifyPropertyChanged. At the beginning of the method locate the following code:
| C# |
Copy Code |
|
string classSignature = GetClassSignature(codeClass); | After that line, you should add the code indicating that the class implements INotifyPropertyChanging and INotifyPropertyChanged. That part of the GenerateClass method should look like:
| C# |
Copy Code |
|
string classSignature = GetClassSignature(codeClass); classSignature += " : INotifyPropertyChanged, INotifyPropertyChanging"; |
- The next step is to add PropertyChanged and PropertyChanging events to the class definition, as well as to add methods rising those events. In the GenerateClass method, find the following code. In the original version of the template, the beginning of the if statement should be located at line 144 (or after the previous modifications, the line should be 145).
|
|
Copy Code |
|
if (!codeClass.DoubleDerived) { this.GenerateProperties(codeClass.Properties); //GenerateAssociations(@class); //GenerateOperations(codeClass.BaseClass.Functions); if(codeClass.NestedClasses.Count > 0) { this.GenerateNestedOIDClasses(codeClass); } }
this.WriteLine(classCloseToken); | Between the If and the this.WriteLine(classCloseToken); statements you should implement both of the interfaces. That part of the method should look like the code snippet below:
|
Copy Code |
|
if (!codeClass.DoubleDerived) { this.GenerateProperties(codeClass.Properties); //GenerateAssociations(@class); //GenerateOperations(codeClass.BaseClass.Functions); if(codeClass.NestedClasses.Count > 0) { this.GenerateNestedOIDClasses(codeClass); } }
this.PushIndent("\t"); this.WriteLine( "public event PropertyChangedEventHandler PropertyChanged;"); this.WriteLine( "public event PropertyChangingEventHandler PropertyChanging;"); this.WriteLine( "protected virtual void OnPropertyChanged( string info )"); this.WriteLine("{"); this.PushIndent("\t"); this.WriteLine("if( this.PropertyChanged != null)"); this.PushIndent("\t"); this.WriteLine("this.PropertyChanged( this, new PropertyChangedEventArgs( info ) );"); this.PopIndent(); this.PopIndent(); this.WriteLine("}");
this.WriteLine( "protected virtual void OnPropertyChanging( string info )"); this.WriteLine("{"); this.PushIndent("\t"); this.WriteLine("if( this.PropertyChanging != null)"); this.PushIndent("\t"); this.WriteLine("this.PropertyChanging( this, new PropertyChangingEventArgs( info ) );"); this.PopIndent(); this.PopIndent(); this.WriteLine("}"); this.PopIndent();
this.WriteLine(classCloseToken); |
- Finally, you need to raise the changing/ed events before/after the property is changed. Again in the Specific template. Navigate to the GenerateProperty method. In the original version of the template, the beginning of the method should be located at line 194. Or, if we consider the previous modifications, the beginning of the method should be located around line 221. Find the following code (see the code snippet below). In the original version of the template, it should be located at line 266 (or after the previous modification, the line should be around 293). Note that there are two property.HasSetter statements, you need to modify the first one:
| C# |
Copy Code |
|
if(property.HasSetter) { if (isPropertyAbstract) { #> set; <#+ } else { #> set { this.<#= property.FieldName #> = value; } <#+ } } | Modify the code, so it looks like:
|
Copy Code |
|
if(property.HasSetter) { if (isPropertyAbstract) { #> set; <#+ } else { #> set { if( <#=property.Name#> == value ) return; this.OnPropertyChanging("<#=property.Name#>"); <#= property.FieldName #> = value; this.OnPropertyChanged("<#=property.Name#>"); } <#+ } } |
- Save both of the templates.
- Change the code generation templates in the Visual Designer, as it is described in Changing the Code Generation Templates Used by the Visual Designer.
- Save your domain model. Note that this time all generated persistent classes implement INotifyPropertyChanged and INotifyPropertyChanging.
| C# |
Copy Code |
|
public partial class Category : INotifyPropertyChanged, INotifyPropertyChanging { private int categoryID; public virtual int CategoryID { get { return categoryID; } set { if ( CategoryID == value ) return; this.OnPropertyChanging( "CategoryID" ); categoryID = value; this.OnPropertyChanged( "CategoryID" ); } } } |
Complete Code Snippets
Below you could find the complete code snippets for methods in the Specific and General template files modified in this example.
- General
- InitializeDefaultUsings
| C# |
Copy Code |
|
private void InitializeDefaultUsings() { if (Usings.Count < 1) { Usings.Add("System"); Usings.Add("System.Data"); Usings.Add("System.Linq"); Usings.Add("System.Linq.Expressions"); Usings.Add("System.Data.Common"); Usings.Add("System.Collections.Generic"); Usings.Add("Telerik.OpenAccess"); Usings.Add("System.ComponentModel"); } } |
-
Specific
| C# |
Copy Code |
|
private void GenerateProperty(Telerik.OpenAccess.CodeGeneration.CodeProperty property) { GenerateSummary(property.Summary); bool isPropertyVirtual = (property.PropertyInheritanceModifier == Telerik.OpenAccess.CodeGeneration.MemberInheritanceModifier.None); bool isPropertyAbstract = (property.PropertyInheritanceModifier == Telerik.OpenAccess.CodeGeneration.MemberInheritanceModifier.Abstract); bool isPropertyPrivate = (property.PropertyAccessModifier == Telerik.OpenAccess.CodeGeneration.MemberAccessModifier.Private); bool areModifiersCompatible = !(isPropertyAbstract && isPropertyPrivate); string accessModifier = this.utilities.Context.GetMemberAccessModifier(property.PropertyAccessModifier); string inheritanceModifier = this.utilities.GetMemberInheritanceModifier(property.PropertyInheritanceModifier);
// private access modifier is not compatible with virtual inheritance modifier // by default the inheritance modifier is None which is handled as virtual if (isPropertyPrivate && isPropertyVirtual) { inheritanceModifier = string.Empty; } string modifiers = string.Format("{0}{1}",accessModifier, inheritanceModifier); string propertyType = this.GetTypeStringPresentation(property); string errorMessage = string.Empty; string propertyErrorMessage = string.Empty; if(!areModifiersCompatible) { errorMessage = this.utilities.Context.GetIncompatibleModifiersErrorMessage(property, this.modelSettings, this.defaultExtension, this.Host.TemplateFile); propertyErrorMessage = this.utilities.Context.GetPropertyErrorMessageForIncompatibleModifiers(property.Name, "private", "abstract"); }
if(!string.IsNullOrEmpty(property.FieldName)) { string initialValue = string.Empty; if(!string.IsNullOrEmpty(property.DefaultValue)) { initialValue = string.Concat(" = ", property.DefaultValue); } string fieldModifier = "private"; if(property.UserData.Contains("isFieldProtected") && (bool)property.UserData["isFieldProtected"]) { fieldModifier = "protected"; } if (!isPropertyAbstract) { #> <#= fieldModifier #> <#= propertyType #> <#= property.FieldName #><#= initialValue #>; <#+ } if (!areModifiersCompatible) { this.WriteLine("//TODO: Please, resolve the following error: {0}", propertyErrorMessage); this.Error(errorMessage); } GenerateCustomAttributes(property.Attributes); #> <#= modifiers #> <#= propertyType #> <#= property.Name #> { <#+ if(property.HasGetter) { if (isPropertyAbstract) { #> get; <#+ } else { #> get { return this.<#= property.FieldName #>; } <#+ } } if(property.HasSetter) { if (isPropertyAbstract) { #> set; <#+ } else { #> set { if( <#=property.Name#> == value ) return; this.OnPropertyChanging("<#=property.Name#>"); <#= property.FieldName #> = value; this.OnPropertyChanged("<#=property.Name#>"); } <#+ } } #> } <#+ } else { if (!areModifiersCompatible) { this.WriteLine("//{0}", errorMessage); this.Error(errorMessage); } GenerateCustomAttributes(property.Attributes); #> <#= modifiers #> <#= propertyType #> <#= property.Name #> { <#+ if(property.HasGetter) { #> get; <#+ } if(property.HasSetter) { #> set; <#+ } #> } <#+ } } |
- GenerateClass
| C# |
Copy Code |
|
private void GenerateClass(Telerik.OpenAccess.CodeGeneration.CodeClass codeClass) { //System.Diagnostics.Debugger.Break(); this.PushIndent("\t"); this.GenerateSummary(codeClass.Summary); this.GenerateComments(codeClass.Comments);
string classSignature = GetClassSignature(codeClass); classSignature += " : INotifyPropertyChanged, INotifyPropertyChanging"; this.GenerateCustomAttributes(codeClass.Attributes); this.WriteLine(classSignature); this.WriteLine(classOpenToken); if (!codeClass.DoubleDerived) { this.GenerateProperties(codeClass.Properties); //GenerateAssociations(@class); //GenerateOperations(codeClass.BaseClass.Functions); if(codeClass.NestedClasses.Count > 0) { this.GenerateNestedOIDClasses(codeClass); } }
this.PushIndent("\t"); this.WriteLine( "public event PropertyChangedEventHandler PropertyChanged;"); this.WriteLine( "public event PropertyChangingEventHandler PropertyChanging;"); this.WriteLine( "protected virtual void OnPropertyChanged( string info )"); this.WriteLine("{"); this.PushIndent("\t"); this.WriteLine("if( this.PropertyChanged != null)"); this.PushIndent("\t"); this.WriteLine("this.PropertyChanged( this, new PropertyChangedEventArgs( info ) );"); this.PopIndent(); this.PopIndent(); this.WriteLine("}"); this.WriteLine( "protected virtual void OnPropertyChanging( string info )"); this.WriteLine("{"); this.PushIndent("\t"); this.WriteLine("if( this.PropertyChanging != null)"); this.PushIndent("\t"); this.WriteLine("this.PropertyChanging( this, new PropertyChangingEventArgs( info ) );"); this.PopIndent(); this.PopIndent(); this.WriteLine("}"); this.PopIndent();
this.WriteLine(classCloseToken); // Doing the double derivation if (codeClass.DoubleDerived) { string baseClassSignature = GetBaseDerivationClassSignature(codeClass);
this.GenerateSummary(codeClass.BaseClass.Summary); this.GenerateComments(codeClass.BaseClass.Comments); this.WriteLine(baseClassSignature); this.WriteLine(classOpenToken); this.GenerateProperties(codeClass.DoubleDerivationBaseClass.Properties); //GenerateAssociations(@class); //GenerateOperations(codeClass.BaseClass.Functions); this.WriteLine(classCloseToken); } this.PopIndent(); } |
|