Monday, February 21, 2011

Writing custom properties: #3 Edit control

In this post I will present how to create initial version of edit control for Guid property, which was introduced in previous posts.

Features
Created control will have:
  • Text input for Guid
  • Button for creating new Guid
This post is first step in more complex implementation which will come with AJAX functionality and On-Page-Edit support.

Controls render types
EPiServer properties can be rendered in one of the following modes (defined by EPiServer.Core.RenderType):
  • Default – when viewing property in View Mode. It’s used e.g. when you display property using <episerver:property PropertyName=”..”/> . In this mode CreateDefaultControls method is invoked.
  • Edit – when editing page in Edit Mode or in Quick Edit Mode. In this mode CreateEditControls method is invoked.
  • OnPageEdit – if PropertyDataControl.SupportsOnPageEdit returns true and <episerver:property PropertyName=”..”/> is used  property is rendered in this mode. Otherwise uses default view. These mode will be covered in more details soon.

Basic server control implementation
All controls for editing custom properties in EPiServer must inherit from PropertyDataControl or derived class.

In simplest scenario when supporting only edit mode functionally CreateEditControls must create controls which are used in edit mode. Edited values should be saved in ApplyEditChanges method.
Additionaly SetupEditControls updates visual representation value based on data from PropertyData
public class GuidPropertyControl : PropertyDataControl
{
    protected TextBox EditBox { get; set; }

    public GuidProperty GuidProperty
    {
        get { return (GuidProperty)this.PropertyData; }
    }

    public override void CreateEditControls()
    {
        //create text box
        this.EditBox = new TextBox { MaxLength = 0xff };
        this.ApplyControlAttributes(this.EditBox);
        this.Controls.Add(this.EditBox);

        //button for generating new guid
        //consider using LanguageManager for texts
        var button = new Button { Text = "New" };
        this.ApplyControlAttributes(button);
        button.Click +=  
              (sender, e) => { this.EditBox.Text = FormatGuid(Guid.NewGuid()); };
        this.Controls.Add(button);

        this.SetupEditControls();
    }


    private static string FormatGuid(Guid guid)
    {
        return guid.ToString();
    }

    //Saves value in underlying PropertyData 
    public override void ApplyEditChanges()
    {
        Guid value;
        if (string.IsNullOrEmpty(this.EditBox.Text))
        {
            value = Guid.Empty;
        }
        else
        {
            try
            {
                value = new Guid(this.EditBox.Text);
            }
            catch (FormatException e)
            {
                this.AddErrorValidator(e.Message);
                return;
            }
        }
        this.SetValue(value);
    }

    protected override void SetupEditControls()
    {
        this.EditBox.Text = FormatGuid(this.GuidProperty.Guid);
    } 
    //representation in View Mode
    public override  void CreateDefaultControls()
    {
        Label target = new Label();
        target.Text = FormatGuid(GuidProperty.Guid);
        this.CopyWebAttributes(target);
        this.Controls.Add(target);
    }

}


Post back support
If you would try to click “New button” in edit mode you would notice strange behavior: Message box warning about leaving page and loosing unsaved value.



The reason for this is that Javascipt OnBeforeUnload event is raised and EPiServer warns you that pages is going to be left with possible data loss.
There are two solutions for this:
  • Use ToolButton from EPiServer namespace instead of regular ASP Button and set DisablePageLeaveCheck to true

public override void CreateEditControls()
{
    //same code creating TextBox


    var button = new ToolButton
                        {
                            Text = "New", 
                            DisablePageLeaveCheck = true
                        };

   //same code using button
   
    SetupEditControls()
}

  • Use ScriptDisablePageLeaveEvent to disable checking specific event raised from our control. This approach is more flexible because not only button is supported and it’s possible to specify which event should suppress checking.

public override void CreateEditControls()
{
    //same code creating TextBox

    //same code creating Button

    ScriptDisablePageLeaveEvent scriptDisablePageLeaveEvent =
            new ScriptDisablePageLeaveEvent
            {
                EventTarget = button,
                EventType = EventType.Click
            };
        this.Controls.Add(scriptDisablePageLeaveEvent);
   
    SetupEditControls()
}

Page leave check considerations
You may wonder how it happed that without writing any code EPiServer can discover that value of Guid property has changed and warn about it. In fact EPiServer PageLeaveCheck attach to each html’s input event onchange and whenever this event is raise IsPageChanged is set to true. No magic!

This approach is working really well in most scenarios, since it’s common to store value in text boxes (sometimes in read-only mode like for Page Reference property). However, if you go into scenario where values are stored only in hidden field and displayed to user directly (without textbox) you will notice that Page Leave Check does not work anymore.

In that case you may manually mark page as changed using following Javascript

savePropetyValue: function (value) {
    //change hidden field value
    $("#hidden_field").val("...") ;

    //mark page as changed
    if (EPi.PageLeaveCheck.enabled) {
        EPi.PageLeaveCheck.SetPageChanged(true);
    }
};

Please be patient: more advanced Javascript usage in custom controls will be covered in future posts. It will include using JQuery, packaging javacripts, AJAX support, etc.

No comments: