Mike's Blog


Business Intelligence keeps me live and communicating!

Building my own UserControl WebPart part V

In mine last post about my own UserControl WebPart, I ended the article with creating a simple editor part. The only thing I didn't show, was how you really could use an editor part to personalize / customize its WebPart. In this article I will show the code and than explain why I coded it this way. So here is the code of the WebPartEditor, which gives a list of all available user controls in de UserControls directory.

using System;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
namespace My.WebParts.Editors
{
    public class WebPartEditor : BaseEditorPart
    {
        public WebPartEditor()
        {
            this.ID = "WebPartEditor";
            this.Title = "Web Part Properties";
        }
 
        private string[] _errorMessages = null;
        private string[] _propertyDisplayNames = null;
        private string[] _propertyDescriptions = null;
 
 
        protected override void CreateChildControls()
        {
            this.Controls.Clear();
 
            UserControlWebPart part = this.WebPartToEdit as UserControlWebPart;
            this._errorMessages = new string[1] { null };
            this._propertyDisplayNames = new string[1] { "User control to display" };
            this._propertyDescriptions = new string[1] { "UserControl" };
 
            if (string.IsNullOrEmpty(part.UserControlPath) || (!Directory.Exists(HttpContext.Current.Server.MapPath(part.UserControlPath))))
            {
                TextBox userControlTextBox = new TextBox();
                userControlTextBox.ID = part.ID + "UserControlWebPartInput";
                userControlTextBox.AutoPostBack = true;
                userControlTextBox.TextChanged += new EventHandler(userControlTextBox_TextChanged);
 
                userControlTextBox.Text = part.UserControl;
                this.Controls.Add(userControlTextBox);
            }
            else
            {
                DropDownList userControlList = new DropDownList();
                userControlList.ID = part.ID + "UserControlWebPartSelect";
                userControlList.AutoPostBack = true;
                userControlList.SelectedIndexChanged += new EventHandler(userControlList_SelectedIndexChanged);
 
                DirectoryInfo directory = new DirectoryInfo(this.Page.Request.MapPath(part.UserControlPath));
                FileInfo[] files = directory.GetFiles("*.ascx");
 
                if (files.Length > 0)
                {
                    userControlList.DataSource = files;
                    userControlList.DataTextField = "Name";
                    userControlList.DataValueField = "Name";
                    userControlList.DataBind();
                    userControlList.Items[0].Selected = true;
 
                    if (!string.IsNullOrEmpty(part.UserControl))
                    {
                        ListItem item = userControlList.Items.FindByValue(part.UserControl.Substring(part.UserControlPath.Length + 1));
                        if (item != null)
                        {
                            userControlList.Items[0].Selected = false;
                            item.Selected = true;
                        }
                        else
                        {
                            this._errorMessages[0] = string.Format("The file {0} does not exist.", part.UserControl);
                        }
                    }
 
                    if (string.IsNullOrEmpty(part.SelectedUserControl))
                    {
                        part.SelectedUserControl = part.UserControlPath + "/" + userControlList.SelectedValue;
                    }
                }
 
                this.Controls.Add(userControlList);
 
            }
        }
 
        protected override void RenderContents(HtmlTextWriter writer)
        {
            if (this.Page != null)
            {
                this.Page.VerifyRenderingInServerForm(this);
            }
            this.EnsureChildControls();
 
            WebControl[] propertyEditors = new WebControl[] { (WebControl)this.Controls[0] };
            RenderPropertyEditors(writer, this._propertyDisplayNames, this._propertyDescriptions, propertyEditors, this._errorMessages);
        }
 
        private string RequestUserControl(string id, string path)
        {
            if (Array.IndexOf<string>(this.Page.Request.Form.AllKeys, this.UniqueID + "$" + id + "UserControlWebPartInput") > -1)
            {
                return this.Page.Request.Form[this.UniqueID + "$" + id + "UserControlWebPartInput"];
            }
            else
            {
                if (this.Page.Request.Form[this.UniqueID + "$" + id + "UserControlWebPartSelect"] == "None")
                {
                    return null;
                }
                else
                {
                    return path + "/" + this.Page.Request.Form[this.UniqueID + "$" + id + "UserControlWebPartSelect"];
                }
            }
        }
 
        void userControlList_SelectedIndexChanged(object sender, EventArgs e)
        {
            UserControlWebPart webPart = this.WebPartToEdit as UserControlWebPart;
 
            webPart.SelectedUserControl = RequestUserControl(webPart.ID, webPart.UserControlPath);
        }
 
        void userControlTextBox_TextChanged(object sender, EventArgs e)
        {
            UserControlWebPart webPart = this.WebPartToEdit as UserControlWebPart;
 
            webPart.SelectedUserControl = RequestUserControl(webPart.ID, webPart.UserControlPath);
        }
 
        public override bool ApplyChanges()
        {
            UserControlWebPart webPart = this.WebPartToEdit as UserControlWebPart;
 
            this._errorMessages = new string[1] { null }; 
            webPart.UserControl = RequestUserControl(webPart.ID, webPart.UserControlPath);
            webPart.SelectedUserControl = webPart.UserControl;
            webPart.LoadUserControl();
 
            return true;
        }
 
        public override void SyncChanges()
        {
        }
 
    } 
}

In the AboutEditor I only added the method RenderContents, which is responsible for rendering the about info as content. Nothing fancy in the code and everything looked rather simple. In this control I will not only add the method RenderContents but also CreateChildControls. The last method is needed, because I'm not only rendering content, I'm creating events as well. As I said before "To know how you should create a good WebPart you must be very confident with the Web load- and event handlers model." Because this is hard to explain, I'm not discussing these principles here.

The first thing which is different comparing to the other editor part. It doesn't inherit from EditorPart but it inherits from BaseEditorPart. This class is an abstract class and has some default implementations for rendering properties. Another difference is the reference between the WebPart and the EditorPart. This can be accomplished by the following line of code.

UserControlWebPart part = this.WebPartToEdit as UserControlWebPart;

CreateChildControls
This method will create the child controls for the editor part. In this method I will detect if the given user controls directory exists and display the user control files in it in a combobox. When it can't find the directory it will give the user a textbox to type the full path to the user control. I used the CreateChildControls method because I want to some eventhandling as well. In this method I add to either of the selected choice an event which will be called after the value of the user control to display changes.

RenderContents
For the rendering I wrote my self a method named RenderPropertyEditors which is stored in the base class BaseEditorPart. This method handles all properties and depending on their type it will be rendered as TextBox (String, DateTime), DropDownList (Enum) or a RadioButton (Boolean). By doing this I can reuse the code for my other editor part which will be displaying the properties of a user control.

RequestUserControl
This method requests the form which UserControl is given or selected by the user. This depends on if the user types the user control in a textbox or selects it from a combobox.

ApplyChanges
Saves the values in an EditorPart control to the corresponding properties in the associated WebPart control. When the values are applied, it loads the selected user control. I set the WebBrowsable property of the UserControl property to false and added a new property SelectedUserControl. The UserControl property will be stored, so the next time the WebPart is rendered it uses te stored UserControl. The SelectedUserControl is only used for the editor parts. By doing so I can eventually reload the selected UserControl for accessing its properties without rendering it to the browser. 

[WebBrowsable(false), Personalizable(true)]
public string UserControl
{
  get
  {
    return this._userControl;
  }
  set
  {
    this._userControl = value;
  }
}
 
[WebBrowsable(false)]
public string SelectedUserControl
{
  get
  {
    string editorUserControl = ViewState["EditorUserControl"] as string;
    if (string.IsNullOrEmpty(editorUserControl))
    {
      return UserControl;
    }
    else
    {
      return editorUserControl;
    }
  }
  set
  {
    ViewState["EditorUserControl"] = value;
  }

When using this WebPart in my test portal it will give the image below.


UserControl in ASP.NET 2.0 Portal using my own UserControl WebPart

As you can see in the image, I now have create my an editor part which customizes  / personalizes a WebPart.

Summary
Hopefully you're even more impressed as I am, because it's so simple to create such a powerfull EditorPart. Microsoft did a great job and made it possible with the ASP.NET 2.0 Framework to create simple but cool WebParts. In the next part I'm going to create another editor part which will display the properties of the user control.

Comments

Mike's Blog said:

I recently checked a couple of blogs about using dynamically loaded User Controls. Some of the blogs
# December 5, 2006 11:13 PM

Leroy said:

Mike -

What a great post.

Do you have a functioning demo that I can download somewhere?

Thanks,

L

# February 23, 2007 10:45 AM

Mike Glaser said:

Leroy,

Lately I didn't had the time to finish the posts. (See http://bloggingabout.net/blogs/mglaser/archive/2006/10/06/Using-dynamically-loaded-User-Controls-in-SharePoint-2007-_2F00_-ASP.NET-2.0-Portals.aspx) Still two post left, which I'll try to finish next month. Keep in touch. Mike

# February 23, 2007 11:29 AM

Petr D. said:

I have one problem - when I have User Control like WebPart and in EditMode got some value from my custom EditorPart (e.g.some text from textbox), how to apply this change in method ApplyChanges to underlaying UserControl? When I use this #UserControlWebPart part = this.WebPartToEdit as UserControlWebPart;#, it invokes compilation error "Cannot convert type WebPart to UserControlWebPart". How to map UserControl type on WebPart?

Thank you in advance

Petr

# January 18, 2008 8:55 PM

Mike Glaser said:

Petr,

It's hard to see what you mean. Maybe you can send me some code.

# January 30, 2008 4:46 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)