Over the next few days I will be putting together a few posts to explain how to create custom configuration block providers. In this post I will talk about creating a Labeller.
The first step is to add a reference to CCNetConfig.Core, then we add a class for the labeller. In this example I will be calling it MyLabeller.
First I inherit from the Labeller object. Then I implement the 2 abstract methods. We also call the base constructor passing the name of the labeller.
[code language="c#"]using System;
using System.Xml.Serialization;
using System.Xml;
using System.ComponentModel;
using CCNetConfig.Core;
using CCNetConfig.Core.Components;
using System.Drawing.Design;
namespace MyCCNet {
public class MyLabeller : Labeller{
///
/// Creates an instance of the myLabeller provider
///
public MyLabeller () : base("myLabeller") {
}
#region ISerialize Members
///
/// Serializes this instance.
///
///
public XmlElement Serialize() {
}
///
/// Deserializes the specified element.
///
///
The element.
public void Deserialize( XmlElement element ) {
}
#endregion
}
}[/code]
The next step is to implement ICCNetDocumentation. This interface tells the GUI that this configuration provider has a link to documentation. We set the uri to the path to the documentation and make it so it is not visible in the property grid.
[code language="c#"]using System;
using System.Xml.Serialization;
using System.Xml;
using System.ComponentModel;
using CCNetConfig.Core;
using CCNetConfig.Core.Components;
using System.Drawing.Design;
namespace MyCCNet {
public class MyLabeller : Labeller, ICCNetDocumentation {
///
/// Creates an instance of the myLabeller provider
///
public MyLabeller () : base("myLabeller") {
}
#region ISerialize Members
///
/// Serializes this instance.
///
///
public XmlElement Serialize() {
}
///
/// Deserializes the specified element.
///
///
The element.
public void Deserialize( XmlElement element ) {
}
#endregion
#region ICCNetDocumentation Members
///
/// Gets the documentation URI.
///
///
The documentation URI.
[Browsable (false)]
public Uri DocumentationUri {
get { return new Uri ("http://mydomain.com/documentation/MyLabeller"); }
}
#endregion
}
}[/code]
Now that we have a class with the basic methods and properties implemented, we can now start adding our properties for the labeller. For MyLabeller, we need to add a release date and an option to increment on failure. ReleaseDate is going to be required so we need to call a function that performs the check. We will also add some Attributes to the ReleaseDate property so the property grid uses the Datepicker to edit the value.
We add a Description Attribute so the property grid can display some helpful information to the user. We also set the Category to "Required" for the ReleaseDate. Then we set the DefaultValue Attribute to null. Then we set the Editor Attribute for the ReleaseDate to use DatePickerUIEditor.
Since IncrementOnFailure can be null, true or false we want an Editor that will allow that. we set the Editor Attribute to DefaultableBooleanUIEditor, then set the TypeConverter to DefaultableBooleanTypeConverter.
[code language="c#"]using System;
using System.Xml.Serialization;
using System.Xml;
using System.ComponentModel;
using CCNetConfig.Core;
using CCNetConfig.Core.Components;
using System.Drawing.Design;
namespace MyCCNet {
public class MyLabeller : Labeller, ICCNetDocumentation {
private DateTime? _releaseDate = DateTime.Now.Date;
private bool? _incrementOnFailure = null;
///
/// Creates an instance of the myLabeller provider
///
public MyLabeller () : base("myLabeller") {
}
///
/// The start date for the release (the start date of iteration one)
///
[Description("The start date for the release (the start date of iteration one)"),
Category("Required"), DisplayName("(ReleaseDate)"),DefaultValue(null),
Editor ( typeof ( DatePickerUIEditor ), typeof ( UITypeEditor ) )]
public DateTime ReleaseDate {
get { return this._releaseDate; }
set { this._releaseDate = Util.CheckRequired(this,"releaseDate",value); }
}
///
/// If true, the label will be incremented even if the build fails. Otherwise it will only be incremented if the build succeeds.
///
[Description ("If true, the label will be incremented even if the build fails. Otherwise it will only be incremented if the build succeeds."),
Editor (typeof (DefaultableBooleanUIEditor), typeof (UITypeEditor)),
TypeConverter (typeof (DefaultableBooleanTypeConverter)),
DefaultValue ( null ), Category ( "Optional" )]
public bool? IncrementOnFailure {
get { return this._incrementOnFailure; }
set { this._incrementOnFailure = value; }
}
#region ISerialize Members
///
/// Serializes this instance.
///
///
public XmlElement Serialize() {
}
///
/// Deserializes the specified element.
///
///
The element.
public void Deserialize( XmlElement element ) {
}
#endregion
#region ICCNetDocumentation Members
///
/// Gets the documentation URI.
///
///
The documentation URI.
[Browsable (false)]
public Uri DocumentationUri {
get { return new Uri ("http://mydomain.com/documentation/MyLabeller"); }
}
#endregion
}
}[/code]
Now we have all of our properties for the MyLabeller provider. Next we are going to write the code for the Serialize method. Since this method returns an XmlElement, there are different ways to serialize this object to xml, for this example, we are going to do it "manually".
[code language="c#"]public override System.Xml.XmlElement Serialize () {
// we create the xml document
XmlDocument doc = new XmlDocument ();
// create the root element
XmlElement root = doc.CreateElement ("labeller");
// this is a special attribute that CCNet Will ignore, this is used for loading configuration files. This allows the deserializer to know exactly what type of object to load with out having to look at all the labellers first.
root.SetAttribute ("ccnetconfigType", string.Format ("{0}, {1}", this.GetType ().FullName, this.GetType ().Assembly.GetName ().Name));
// set the type attribute to the type of labeller this is.
root.SetAttribute ("type", this.TypeName);
// create the element for the releaseDate
XmlElement ele = doc.CreateElement ("releaseDate");
// set the text of the element to the value passing it through the CheckRequired method to validate the value.
ele.InnerText = Util.CheckRequired(this,"releaseDate",this.ReleaseDate.ToString ("yyyy/MM/dd"));
// add the element to the root element.
root.AppendChild (ele);
// check if the increment on failure has a value.
if ( this.IncrementOnFailure.HasValue ) {
// since it does create the element
ele = doc.CreateElement ("incrementOnFailure");
// set the text to the value.
ele.InnerText = this.IncrementOnFailure.Value.ToString ();
// add it to the root element.
root.AppendChild (ele);
}
// return the root element.
return root;
}[/code]
Finally we will add the logic to deserialize this labeller
[code language="C#"]public override void Deserialize ( System.Xml.XmlElement element ) {
// first we set everything to a default value.
this._releaseDate = DateTime.Now.Date;
this.IncrementOnFailure = null;
// check if we can convert the element to this type of Labeller
if ( string.Compare (element.GetAttribute ("type"), this.TypeName, false) != 0 )
throw new InvalidCastException (string.Format ("Unable to convert {0} to a {1}", element.GetAttribute ("type"), this.TypeName));
DateTime dt = DateTime.Now.Date;
// this method will find the releaseDate value if it is an attribute or an element
string myValue = Util.GetElementOrAttributeValue ("releaseDate", element);
if ( !string.IsNullOrEmpty )
DateTime.TryParse (myValue, out dt);
this.ReleaseDate = dt;
// now get the incrementOnFailure value.
myValue = Util.GetElementOrAttributeValue ("incrementOnFailure",element);
if ( !string.IsNullOrEmpty(myValue) )
this.IncrementOnFailure = string.Compare ( myValue, bool.TrueString, true ) == 0;
}[/code]
And we created a provider for a custom Configuration Block that is not supported by default by CCNet.