This article is based on material originally published on DNN Developer Zone (www.dnndevzone.com).  This version has been edited slightly.

Introduction

Version 4.3 of the DotNetNuke Web Application Framework introduced a suite of Property Editors. These Property Editors dynamically inject the appropriate Edit Control depending on the Data Type to be edited. There are 5 types of editor and 47 class or enum files that make up the Property Editor suite of controls. This article describes how to create your own simple EditControl, and how you go about registering your control in your DotNetNuke site.

The FolderEditControl

As an example of building your very own EditControl, this article will take you step-by-step through building a Folder EditControl. While this is a somewhat arbitrary choice, we can envisage a Use Case for this control.

A module developer wishes to build a Gallery module where the images are saved to a specific folder for each user. The module developer decides that the obvious place to assign this folder is in the User's profile. The administrator of the site would assign the User a folder in the Profile Editor. By setting the Profile Properties Visible property to visible, the user cannot edit the property themselves, but Administrators can still see it, and edit it.

In the module's code the module developer would just determine the current user's folder using the following C# code:

Listing 1: Getting the userFolder path
string userFolder = UserInfo.Profile.GetPropertyValue("UserFolder")

The first step in creating your own custom EditControl, is deciding which existing EditControl to start from as your base class. In the case of the FolderEditControl, we will be saving the data (in the Profile) as a relative path. We could save the data as an integer by using the FolderID of the folder in question, but this would require a second call to detemine the folderPath.

Listing 2: Getting the userFolder path from the Id
   1:  int userFolderID = UserInfo.Profile.GetPropertyValue("UserFolder");
   2:  FolderController controller = new FolderController();
   3:  FolderInfo folder = controller.GetFolderInfo(PortalId, userFolderID);
   4:  string userFolder = folder.FolderPath;

As we have decided to store the data as text, the obvious base class candidate is the TextEditControl.

Figure 1: The FolderEditControl
 FolderEditControl

Figure 1 shows the definition of the FolderEditControl. The control has three properties and one method. All the other aspects of the controls behaviour are handled either by the abstract base class (EditControl) or by the immediate base class (TextEditControl).

Thus, the abstract base class (EditControl) provides

  • all the standard properties (Name, Value, OldValue etc) that the Property Editor Framework expects
  • the RenderViewMode method to render the value of the control in View mode (as in View Profile)
  • the IPostBackDataHandler Interface implementation, to manage post backs, and determine whether the value has changed
  • and the ValueChanged event and OnValueChnaged method to inform the Property Editors that the value of the control has changed.

The TextEditControl meanwhile provides

  • two extra helper properties (StringValue and OldStringValue) that provide strongly-typed versions of Value and OldValue,
  • and the OnDataChanged method (see Listing 3), which we can use, since while we will be displaying a list of folders to the user, we are still just working with text
Listing 3: The OnDataChanged method of the TextEditControl
   1:  Protected Overrides Sub OnDataChanged(ByVal e As EventArgs)
   2:      Dim args As New PropertyEditorEventArgs(Name)
   3:      args.Value = StringValue
   4:      args.OldValue = OldStringValue
   5:      args.StringValue = StringValue
   6:      MyBase.OnValueChanged(args)
   7:  End Sub

Those of you that have read the Part 4 of this series will note the similarity between this method and the same method in the TrueFalseEditControl (see Listing 7 in An Introduction to Edit Controls) .

Creating the FolderEditControl

The first thing we will need to do to create our FolderEditControl is to create a new class file (see Listing 4).. While the specific namespace used is not important, it is important to use a namespace that should be unique.

Listing 4: Creating the new FolderEditControl
   1:  namespace Keydance.DotNetNuke.Controls
   2:  {
   3:      public class FolderEditControl : TextEditControl
   4:      {
   5:      }
   6:  }

The three properties in the FolderEditControl are provided as "helper" properties. They are all protected read-only properties (see Listing 5).

Listing 5: The three properties of the FolderEditControl
   1:  protected bool IsHost
   2:  {
   3:      get
   4:      {
   5:          bool _IsHost = Null.NullBoolean;
   6:          if (PortalSettings.ActiveTab.ParentId == PortalSettings.SuperTabId)
   7:              _IsHost = true;
   8:          return _IsHost;
   9:      }
  10:  }
  11:   
  12:  protected int PortalId
  13:  {
  14:      get
  15:      {
  16:          int _PortalId = Null.NullInteger;
  17:          if (!IsHost)
  18:              _PortalId = PortalSettings.PortalId;
  19:          return _PortalId;
  20:      }
  21:  }
  22:   
  23:  protected PortalSettings PortalSettings
  24:  {
  25:      get
  26:      {
  27:          return PortalController.GetCurrentPortalSettings();
  28:      }
  29:  }

The IsHost property determines whether we are dealing with Portal folders or Host folders. While this code is C# code it is clearly similar to code used in the URLControl and FileManager to determine whether we should be using Portal or Host folders. The PortalId returns either Null.NullInteger when IsHost is true or the Id of the current Portal if IsHost is false, and the PortalSettings returns the the current Portal's settings. There is nothing really different in these last two properties from the code found in the PortalModuleBase base class.

In reality the only property that we will use in the RenderEditMode method is the PortalId - the other properties just provide a convenient way to factorise out the code needed.

So, lets now take a look at the RenderEditMode method. This method will render our list of folders as a drop-down list (ie an XHTML <select> tag. The code is shown in Listing 6.

Listing 6: The RenderEditMode method
   1:  protected override void RenderEditMode(HtmlTextWriter writer)
   2:  {
   3:      //Declare variables
   4:      string folderName;
   5:      string folderPath;
   6:      string viewRoles = Null.NullString;
   7:   
   8:      //Render the Style for the control
   9:      ControlStyle.AddAttributesToRender(writer);
  10:   
  11:      //Render the Select Tag
  12:      writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
  13:      writer.RenderBeginTag(HtmlTextWriterTag.Select);
  14:   
  15:      //Get the folders
  16:      ArrayList folders = FileSystemUtils.GetFolders(PortalId);
  17:   
  18:      //Iterate through the folders
  19:      foreach (FolderInfo folder in folders)
  20:      {
  21:          if (folder.FolderPath == Null.NullString)
  22:          {
  23:              folderName = Localization.LocalizeString(this, "Root");
  24:              viewRoles = FileSystemUtils.GetRoles("", PortalId, "READ");
  25:          }
  26:          else
  27:          {
  28:              folderName = folder.FolderPath;
  29:              viewRoles = FileSystemUtils.GetRoles(folderName, PortalId, "READ");
  30:          }
  31:          folderPath = folder.FolderPath;
  32:   
  33:          //Check if the user has View access to this folder
  34:          if (PortalSecurity.IsInRoles(viewRoles) && 
(folder.StorageLocation ==
(int)FolderController.StorageLocationTypes.InsecureFileSystem))
  35:          {
  36:              //Add the Value Attribute
  37:              writer.AddAttribute(HtmlTextWriterAttribute.Value, folderPath);
  38:   
  39:              //Add the Selected Attribute if this folder is the currently
  40:              //selected folder
  41:              if (folderPath == StringValue)
  42:                  writer.AddAttribute(HtmlTextWriterAttribute.Selected, "selected");
  43:   
  44:              //Render Option Tag
  45:              writer.RenderBeginTag(HtmlTextWriterTag.Option);
  46:              writer.Write(folderName);
  47:              writer.RenderEndTag();
  48:          }
  49:      }
  50:   
  51:      //Close Select Tag
  52:      writer.RenderEndTag();
  53:  } 

Lines 4-6 declare the variables we will need in this method. Lines 9, 12 and 13 render the beginning of the XHTML <select> tag. As was discussed in Part 2 of this series, the name attribute of the <select> tag is set to the UniqueID of the control, so the .NET Framework can match up the control with the correct name/value pair returned from the client. Line 52 renders the closing part of the <select> tag.

The rest of the code (lines 16-49) detemine which folders to include and then renders each folder as an <option> tag, which represents a single row in the drop-down list. In line 16 we call the static (Shared in Visual Basic) GetFolders method of FileSystemUtils. This returns an ArrayList of FolderInfo items representing all the folders for a given portal (or the Host folders if PortalId is -1 (Null.NullInteger). We then iterate through the ArrayList of folders (line 19). Lines 21-31 determine the value of the folderName and the folderPath and build a ";" delimted list of roles with "READ" permission (viewRoles) .

Line 34 then checks whether the current user can "READ" the current folder. If the current user does have the appropriate permission the value attribute (of the <option> tag) is rendered (line 37). If the current folder's folderPath is the current Value of the property then the <option> tags selected value is set (lines 41 & 42). Finally, in lines 45-47 the <option> tag is rendered displaying the name of the foler (folderName)

Thats all there is to it. As I have emphasised a number of times, the important points to note are the the name attribute is set to the UniqueID property and the value part of the control is set to the Value of the property.

Registering the FolderEditControl

Now that we have our own FolderEditControl, we will need to register the control in our DotNetNuke site. Obviously the first step is to make sure that the control compiles. Next we need to figure out the Assembly Qualified Full Name for the Control. It is assumed here that the control was created in a Class Library.

Figure 2: Detmermining the AssemblyName
 AssemblyName

To determine the Assembly Qualified Full Name, we need to know the Assembly's name. To determine this name we right-click on the class Library project in Solution Explorer, and select the Properties menu item (its probably at the bottom of the list).

The Assembly Name is on the Application Tab of the Properties page for the project (see Figure 2). So in this example the Assembly Name is "Keydance.DNNLibrary" which means that the executable created will be Keydance.DNNLibrary.dll.

The next thing we need to know is the Namespace. In Listing 2, we used the namespace Keydance.DotNetNuke.Controls. This means that our Assembly Qualified Full Name is "Keydance.DotNetNuke.Controls.FolderEditControl, Keydance.DNNLIbrary" ( or Namespace.ClassName, AssemblyName).

Now that we have determined what the Assembly Qualifed Full Name for the control is, we can use the List Editor in DotNetNuke to register this control as a new DataType. To do this we log in to the DotNetNuke site as Host, select the Lists menu item on the Host menu, and then click on DataType in the Lists Tree. The list of current DataTypes will be shown on the right (see Figure 3)

Figure 3: The DataTypes List
 DataTypes

To add a new DataType we click on AddEntry, enter the Assembly Qualified Full Name in the Entry Text field and a short name (Folder) in the Value field. (see Figure 4)

Figure 4: Adding the new DataType
 AddDataType
Figure 5: Creating the new UserFolder property
 AddUserFolder

The short name we choose will be displayed in the DataType drop-down list in the Profile Property Editor.

After filling in both fields we click the Save button to save the new DataType. We can now use this new DataType in the Profile Property editor to create a new UserFolder property.

Note that the new property is near the bottom of the List of DataTypes.

Once this property has been created we can now see it an action by viewing a User's profile. This is shown in Figure 6.

Figure 6: The new UserFolder Property
 UserFolderProperty

The administrator can now assign a folder to the User and the module developer of our fictitious Gallery module can determine the current user's assigned Folder, and use it to save all the current user's images.

This example was fairly straightforward. Not all EditControls are this simple. It does however illustrate the process required to develop your own EditControl.


This article is based on material originally published on DNN Developer Zone (www.dnndevzone.com).  This version has been edited slightly.

Introduction

Version 4.3 of the DotNetNuke Web Application Framework introduced a suite of Property Editors. These Property Editors dynamically inject the appropriate Edit Control depending on the Data Type to be edited. There are 5 types of editor and 47 class or enum files that make up the Property Editor suite of controls. Previous articles in this series introduced the Property Editors. This article introduces the 14 EditControls included with the core distribution. Future articles in this series will describe how to create your own EditControl.

The Abstract EditControl

Figure 1: The Abstract EditControl Base Class
EditControl  

Figure 1 shows the definition of the abstract base class EditControl. Every EditControl used by the Property Editors is derived from this base class.

Lets take a look at this abstract class. There are 9 Properties in this class and one abstract Property (StringValue).

  • CustomAttributes - An array of Attributes that can be used by the derived classes to add customisation (for instance the DNNListEditControl uses the Attributes collection to determine which list to display).
  • EditMode - Determines whether the control is in Edit or View mode.
  • LocalResourceFile - This property is used by the EditControl to reference the current LocalResourceFile.
  • Name - The name of the property being edited.
  • OldValue - the old value of the property.
  • Required - whether the property is required (used by drop-down lists to determine whether to add a < None Specified > option).
  • StringValue - the Value expressed as a String , each derived class must override this property.
  • SystemType - the underlying System.Type of the value of this control.
  • Value - the value of the property.

The abstract class also defines a number of protected methods. Three methods control the rendering process.

  • Render - the Render method is called by the .NET Framework at Render time and this method calls RenderEditMode or RenderViewMode to render the control, based on the value of the EditMode property.
  • RenderViewMode - this method renders the control when the EditMode is set to View, as is the case in "View Profile" .
  • RenderEditMode - this method renders the control when the EditMode is set to Edit, as is the case when editing a Profile. This method is shown in Listing 1 below.

 

 

 

Listing 1: The RenderEditMode method
   1:  Protected Overridable Sub RenderEditMode(ByVal writer As HtmlTextWriter)
   2:      Dim propValue As String = CType(Me.Value, String)
   3:      ControlStyle.AddAttributesToRender(writer)
   4:      writer.AddAttribute(HtmlTextWriterAttribute.Type, "text")
   5:      writer.AddAttribute(HtmlTextWriterAttribute.Value, propValue)
   6:      writer.AddAttribute(HtmlTextWriterAttribute.Name, Me.UniqueID)
   7:      writer.RenderBeginTag(HtmlTextWriterTag.Input)
   8:      writer.RenderEndTag()
   9:  End Sub

These methods provide a default implementation that is called if the derived class does not provide its own rendering methods. Later in this article we will show how a derived EditControl overrides these methods. In the abstract base class a text box is rendered (an XHTML <input /> tag), the value is set to the value of the property, and the name is set to the UniqueID of the control. This last value is very important, as it is used by the .NET Framework to identify the control on PostBack.

IPostBackDataHandler Interface

If you look at Figure 1 you will see that the EditControl class implements the IPostBackDataHandler Interface. This interface provides two methods that are called automatically by the .NET Framework LoadPostData (see Listing 2) and RaisePostDataChangedEvent (see Listing 3)

Listing 2: LoadPostData method
   1:  Public Overridable Function LoadPostData(ByVal postDataKey As String, _ 
ByVal
postCollection As NameValueCollection) As Boolean
   2:      Dim dataChanged As Boolean = False
   3:      Dim presentValue As String = CStr(Value)
   4:      Dim postedValue As String = postCollection(postDataKey)
   5:      If Not presentValue.Equals(postedValue) Then
   6:          Value = postedValue
   7:          dataChanged = True
   8:      End If
   9:      Return dataChanged
  10:  End Function

The LoadPostData method is called by the .NET Framework for any Control that implements IPostBackDataHandler. It is called just after the View State is loaded (LoadViewState) and just before the Load phase of the Control Life Cycle. The postCollection represents all the name/value pairs returned to the Server by the PostBack, and the postDataKey is the key for the current control. (Remember above when I said the UniqueID was rendered to the Name attribute of the <input/> tag). The LoadPostData method returns a boolean that lets the Framework know whether the data was changed since it was rendered.

Listing 3: The RaisePostDataChangedEvent method
   1:  Public Sub RaisePostDataChangedEvent()
   2:      'Raise the DataChanged Event
   3:      OnDataChanged(System.EventArgs.Empty)
   4:  End Sub

Once the Load phase is complete, the .NET Framework then calls the RaisePostDataChangedEvent for every control that had previously reported that their data had changed. This allows the control to raise an event indicating its value has changed, In our case the RaisePostDataChangedEvent method calls the abstract OnDataChanged method. An abstract must be implemented in the derived class, and when the RaisePostDataChangedEvent calls the OnDataChanged method it actually calls the derived classes version, so the derived class can determine what to do next.

The base class also provides the ValueChanged event and associated OnValueChanged method (see Listing 4)

Listing 4: The Value Changed Event and the OnValueChanged Method
   1:  Public Event ValueChanged As PropertyChangedEventHandler
   2:   
   3:  Protected Overridable Sub OnValueChanged(ByVal e As PropertyEditorEventArgs)
   4:      RaiseEvent ValueChanged(Me, e)
   5:  End Sub
A derived class can use these events to indicated to the Editors that their values have been changed, passing the relevant information in a custom PropertyEditorEventArgs object.
Figure 2: PropertyEditorEventArgs Class
PropertyEditorEventArgs

EditControls included with DotNetNuke 4.3

As mentioned in the Introduction 14 EditControls are included with DotNetNuke (1 abstract base class and 13 derived classes). Figure 3 shows how 9 of the controls derive from the abstract base class. In general the controls can be divided into 4 groups:

  • Text controls - which derive from the TextEditControl
  • Integer controls - which derive from the IntegerEditControl
  • True False controls - either True/False (using radio buttons) or a check Edit Control
  • Lists - derived from DNNListEditControl
Figure 3: Some of the EditControls included in DotNetNuke v4.3.
 EditControls

The TrueFalseEditControl

As an example of a derived EditControl let's look at the TrueFalseEditControl. This control can be used to edit a boolean value using two radio buttons (True and False). As can be seen in Figure 4, this control has three properties and 2 methods.

Figure 4: The TrueFalseEditControl
 TrueFalseEditControl

The StringValue property is abstract in the base class so every derived class must implement this property.

The BooleanValue and OldBooleanValue properties are ReadOnly typed versions of the base classes Value and OldValue properties. They are not neccessary but are provided as helper properites that can be used in the methods of the class and any classes derived from it.

Listing 5: The three properties of the TrueFalseEditControl
   1:  Protected ReadOnly Property BooleanValue() As Boolean
   2:      Get
   3:          Dim boolValue As Boolean = Null.NullBoolean
   4:          Try
   5:              'Try and cast the value to an Boolean
   6:              boolValue = CType(Value, Boolean)
   7:          Catch ex As Exception
   8:          End Try
   9:          Return boolValue
  10:      End Get
  11:  End Property
  12:   
  13:  Protected ReadOnly Property OldBooleanValue() As Boolean
  14:      Get
  15:          Dim boolValue As Boolean = Null.NullBoolean
  16:          Try
  17:              'Try and cast the value to an Boolean
  18:              boolValue = CType(OldValue, Boolean)
  19:          Catch ex As Exception
  20:          End Try
  21:          Return boolValue
  22:      End Get
  23:  End Property
  24:   
  25:  Protected Overrides Property StringValue() As String
  26:      Get
  27:          Return BooleanValue.ToString
  28:      End Get
  29:      Set(ByVal Value As String)
  30:          Dim setValue As Boolean = Boolean.Parse(Value)
  31:          Me.Value = setValue
  32:      End Set
  33:  End Property

The TrueFalseEditControl overrides the RenderEditMode method of the base class as we are using two radio buttons (rather than a text box) to edit the data. This method is shown in Listing 6.

Listing 6: The RenderEditMode method of the TrueFalseEditControl
   1:  Protected Overrides Sub RenderEditMode(ByVal writer As HtmlTextWriter)
   2:      writer.AddAttribute(HtmlTextWriterAttribute.Type, "radio")
   3:      If (BooleanValue) Then
   4:          writer.AddAttribute(HtmlTextWriterAttribute.Checked, "checked")
   5:      End If
   6:      writer.AddAttribute(HtmlTextWriterAttribute.Value, "True")
   7:      writer.AddAttribute(HtmlTextWriterAttribute.Name, Me.UniqueID)
   8:      writer.RenderBeginTag(HtmlTextWriterTag.Input)
   9:      writer.RenderEndTag()
  10:   
  11:      ControlStyle.AddAttributesToRender(writer)
  12:      writer.RenderBeginTag(HtmlTextWriterTag.Span)
  13:      writer.Write("True")
  14:      writer.RenderEndTag()
  15:   
  16:      writer.AddAttribute(HtmlTextWriterAttribute.Type, "radio")
  17:      If (Not BooleanValue) Then
  18:          writer.AddAttribute(HtmlTextWriterAttribute.Checked, "checked")
  19:      End If
  20:      writer.AddAttribute(HtmlTextWriterAttribute.Value, "False")
  21:      writer.AddAttribute(HtmlTextWriterAttribute.Name, Me.UniqueID)
  22:      writer.RenderBeginTag(HtmlTextWriterTag.Input)
  23:      writer.RenderEndTag()
  24:   
  25:      ControlStyle.AddAttributesToRender(writer)
  26:      writer.RenderBeginTag(HtmlTextWriterTag.Span)
  27:      writer.Write("False")
  28:      writer.RenderEndTag()
  29:  End Sub

This is a much more complicated method than the one in the base class (compare with Listing 1), as it needs to render two radio buttons, and make one of them selected (or "checked") depending on the vale of the BooleanValue property. It is important to note also that the name attribute for both buttons is given the same value, the UniqueID property of the EditControl, so that the .NET Framework can correctly identify the control.

As mentioned above the RaisePostDataChangedEvent method of the base class calls the abstract OnDataChanged method, so the TrueFalseEditControl has to provide an implementation of this method. This is shown in Listing 7.

Listing 7: The OnDataChanged method of the TrueFalseEditControl
   1:  Protected Overrides Sub OnDataChanged(ByVal e As EventArgs)
   2:      Dim args As New PropertyEditorEventArgs(Name)
   3:      args.Value = BooleanValue
   4:      args.OldValue = OldBooleanValue
   5:      args.StringValue = StringValue
   6:      MyBase.OnValueChanged(args)
   7:  End Sub

The OnDataChanged method creates a new PropertyEditorEventArgs object, and sets its Value, OldValue and StringValue properties - the Name property is created in the constructor, and the controls Name value is passed as a parameter of the constructor.

This PropertyEditorEventArgs object is then passed to the base classes OnValueChanged method, which raises the ValueChanged event.

As can be seen from this discussion, there really isn't very much to the derived EditControls. In future articles in this series we will show how to develop new Edit Controls, and how to register them in your DotNetNuke installation.


 Search Blog

 Adsense

 Calendar

«  September 2010  »
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
View posts in large calendar

 Tags

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010 Thoughts from the Wet Coast