Editor Zones contain the Editor Parts you can use to edit the properties of your Web Parts. The Web Part Framework contains a standard set of Editor Part controls that can be used to edit the standard properties of a Web Part: the AppearanceEditorPart, the BehaviorEditorPart, and the LayoutEditorPart controls. If your Web Part has custom properties, and you want to edit these properties with an Editor Part, then you have two choices. You can use the PropertyGridEditorPart control or build your own custom Editor Part. In most cases, you won't want to use the PropertyGridEditorPart because it does not provide any support for form validation or advanced layout. When using the PropertyGridEditorPart, for any property other than a Boolean or Enum property, you get a single-line text box and there isn't anything you can do about it. Note Another option is to edit a Web Part's properties inline. In other words, you can add an edit form to the Web Part itself and display the edit form only when the page is in Edit Display Mode. You can detect whether a page is in Edit Display Mode within a Web Part by retrieving the current instance of the WebPartManager control with the WebPartManager.GetCurrentWebPartManager(Page) method and checking the value of the DisplayMode property. This section explores two methods of creating custom Editor Parts. First, to clarify the concepts involved, we'll create a simple Editor Part designed to edit the properties of a particular Web Part. Next, you'll learn how to create a templated Editor Part you can use when editing any Web Part control regardless of the types of properties that it contains. How Editor Zones Work As in other types of Web Zones, three types of objects are involved in rendering an Editor Zone: the EditorZone control, the EditorPartChrome class, and the EditorPart control. The EditorZone control appears only when a page is in Edit Display Mode. When the page is in Edit Display Mode, the Editor Zone still does not display any Editor Parts until you select the Edit menu option on a particular Web Part. When you select a Web Part to edit, two types of Editor Parts are displayed. First, any Editor Parts declared in the Editor Zone are displayed. Second, if the Web Part implements the IWebEditable interface or derives from the base WebPart class, then any Editor Parts returned from the Web Part's CreateEditorParts() method are displayed. Note The BehaviorEditorPart appears only when a page is in Shared Personalization scope and the Web Part being edited is shared among all users. Finally, the EditorPartChrome class is actually responsible for rendering each Editor Part. An instance of the EditorPartChrome class is created by an Editor Zone, and then the Editor Zone calls the EditorPartChrome control's RenderEditorPart() method to render each Editor Part. If you want to modify the appearance of an Editor Zone or the chrome that appears around each Editor Part, then you need to modify the EditorZone and EditorPartChrome classes. If you want to modify the appearance of the Editor Parts themselves, then you need to modify the properties of a particular Editor Part control. Creating a Simple Custom Editor Part Let's start by creating the Web Part that we will edit. The FeaturedBookPart is contained in Listing 30.18. Listing 30.18. FeaturedBookPart.ascx [View full width] <%@ Control Language="VB" ClassName="FeaturedBookPart" %> <%@ Implements Interface="System.Web.UI.WebControls.WebParts.IWebEditable" %> <%@ Implements Interface="myControls.IFeaturedBook" %> <%@ Import Namespace="System.Collections.Generic" %> <%@ Import Namespace="myControls" %> <script runat="server"> Private _bookTitle As String Private _datePublished As DateTime Private _price As Decimal Public ReadOnly Property WebBrowsableObject() As Object Implements IWebEditable .WebBrowsableObject Get Return Me End Get End Property Public Function CreateEditorParts() As EditorPartCollection Implements IWebEditable. CreateEditorParts Dim editorParts As New List(Of EditorPart)() Dim editor As New FeaturedBookEditorPart() editor.ID = "FeaturedEditor1" editorParts.Add(editor) Return New EditorPartCollection(editorParts) End Function <Personalizable()> _ Public Property BookTitle() As String Implements IFeaturedBook.BookTitle Get Return _bookTitle End Get Set(ByVal Value As String) _bookTitle = Value End Set End Property <Personalizable()> _ Public Property DatePublished() As DateTime Implements IFeaturedBook.DatePublished Get Return _datePublished End Get Set(ByVal Value As DateTime) _datePublished = Value End Set End Property <Personalizable()> _ Public Property Price() As Decimal Implements IFeaturedBook.Price Get Return _price End Get Set(ByVal Value As Decimal) _price = Value End Set End Property Private Sub Page_PreRender() lblBookTitle.Text = _bookTitle lblDatePublished.Text = _datePublished.ToString("D") lblPrice.Text = _price.ToString("c") End Sub </script> Title: <asp:Label Runat="server" /> <br /> Published: <asp:Label Runat="server" /> <br /> Price: <asp:Label Runat="server" /> | Notice that the FeaturedBookPart control implements two interfaces: the IWebEditable and the IFeaturedBook interfaces. Implementing the first interface enables you to associate the FeaturedBookPart with a custom EditorPart control. The IWebEditable interface has two members: WebBrowsableObject This property represents the object that is edited. Normally, the property should just return a reference to the current control. CreateEditorParts This method returns the collection of EditorPart controls associated with the current Web Part. The FeaturedBookPart also implements the IFeaturedBook interface that we create in a moment. The custom EditorPart control needs some method of identifying the properties exposed by the FeaturedBookPart. The IFeaturedBook interface provides the EditorPart with this information. (You don't need to create an interface when creating a Web Part with a custom control rather than a User Control.) The custom Editor Part is contained in Listing 30.19. Listing 30.19. FeaturedBookEditorPart.vb [View full width] Imports System Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' Describes the properties of the ''' FeaturedWebPart control ''' </summary> Public Interface IFeaturedBook Property BookTitle() As String Property DatePublished() As DateTime Property Price() As Decimal End Interface ''' <summary> ''' Custom Editor for FeaturedBookPart ''' </summary> Public Class FeaturedBookEditorPart Inherits EditorPart Private _txtBookTitle As TextBox Private _calDatePublished As Calendar Private _txtPrice As TextBox Private _valPrice As CompareValidator Private _title As String = "Featured Book Editor" ''' <summary> ''' Create standard title ''' </summary> Public Overrides Property Title() As String Get Return _title End Get Set(ByVal Value As String) _title = Value End Set End Property ''' <summary> ''' Utility method that returns the child control ''' in the case of a GenericWebPart ''' </summary> Private ReadOnly Property ControlToEdit() As Control Get If TypeOf (Me.WebPartToEdit) Is GenericWebPart Then Return (CType(WebPartToEdit, GenericWebPart)).ChildControl Else Return Me.WebPartToEdit End If End Get End Property ''' <summary> ''' Called when you click OK or Apply to ''' apply the Web Part property changes ''' </summary> Public Overrides Function ApplyChanges() As Boolean Dim success As Boolean = False EnsureChildControls() Page.Validate() If Page.IsValid Then CType(ControlToEdit, IFeaturedBook).BookTitle = _txtBookTitle.Text CType(ControlToEdit, IFeaturedBook).DatePublished = calDatePublished .SelectedDate CType(ControlToEdit, IFeaturedBook).Price = Decimal.Parse(_txtPrice.Text) success = True End If Return success End Function ''' <summary> ''' Called when the Web Part Framework ''' has initialized the Web Part being edited ''' </summary> Public Overrides Sub SyncChanges() EnsureChildControls() _txtBookTitle.Text = (CType(ControlToEdit, IFeaturedBook)).BookTitle _calDatePublished.SelectedDate = (CType(ControlToEdit, IFeaturedBook)) .DatePublished _txtPrice.Text = (CType(ControlToEdit, IFeaturedBook)).Price.ToString() End Sub ''' <summary> ''' Adds the controls rendered by this Editor Part ''' to the controls collection. ''' </summary> Protected Overrides Sub CreateChildControls() ' Add Book Title _txtBookTitle = New TextBox() _txtBookTitle.ID = "txtBookTitle" Controls.Add(_txtBookTitle) ' Add Date Published _calDatePublished = New Calendar() _calDatePublished.ID = "calDatePublished" Controls.Add(_calDatePublished) ' Add Price _txtPrice = New TextBox() _txtPrice.ID = "txtPrice" _txtPrice.Columns = 5 Controls.Add(_txtPrice) ' Add Price Validator _valPrice = New CompareValidator() _valPrice.ID = "valPrice" _valPrice.ControlToValidate = _txtPrice.ID _valPrice.Operator = ValidationCompareOperator.DataTypeCheck _valPrice.Type = ValidationDataType.Currency _valPrice.Text = "(Must be currency)" Controls.Add(_valPrice) End Sub ''' <summary> ''' Renders the User Interface for the Editor Part ''' </summary> ''' <param name="writer"></param> Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) ' Render Book Title RenderLabel(writer, "Book Title:", _txtBookTitle.ClientID) writer.WriteBreak() _txtBookTitle.RenderControl(writer) writer.WriteBreak() writer.WriteBreak() ' Render Date Published RenderLabel(writer, "Date Published:", _calDatePublished.ClientID) writer.WriteBreak() _calDatePublished.RenderControl(writer) writer.WriteBreak() writer.WriteBreak() ' Render Price RenderLabel(writer, "Price:", _txtPrice.ClientID) _valPrice.RenderControl(writer) writer.WriteBreak() _txtPrice.RenderControl(writer) writer.WriteBreak() End Sub ''' <summary> ''' Renders an accessible Label for the ''' form fields ''' </summary> Private Sub RenderLabel(ByVal writer As HtmlTextWriter, ByVal labelText As String, ByVal associatedControlId As String) writer.AddAttribute(HtmlTextWriterAttribute.For, associatedControlId) writer.RenderBeginTag(HtmlTextWriterTag.Label) writer.Write(labelText) writer.RenderEndTag() End Sub End Class End Namespace | The class in Listing 30.19 inherits from the base EditorPart class. It overrides two methods from the base class: SyncChanges() and ApplyChanges(). The SyncChanges() method is called automatically when the EditorPart is displayed and the Web Part being edited has been initialized. Listing 30.19 takes advantage of this method to synchronize the editor form with the current property values of the Web Part being edited. The ApplyChanges() method is automatically called when the user clicks the Apply or OK button in the Editor Zone. This method updates the properties of the Web Part being edited with the values from the editor form. Note The ApplyChanges() and SyncChanges() methods are executed during the processing of postback data after the Page Load event and before the PreRender event. Notice that both the SyncChanges() and ApplyChanges() methods take advantage of a property named ControlToEdit, which also is defined in the class in Listing 30.19. This property returns a different control depending on whether the Web Part being edited is a GenericWebPart or a "true" Web Part. When you create a Web Part by using a User Control or any control that does not derive from the WebPart class, the Web Part Framework automatically wraps the control in a GenericWebPart control. In the case of a GenericWebPart control, you want to edit the properties of the first child control of the Web Part and not the properties of the Web Part itself. The bulk of the work in Listing 30.19 is devoted to building the custom editor form. Two TextBox controls and one Calendar control are created in the CreateChildControls() method. These controls are actually rendered in the RenderContents() method. Finally, Listing 30.20 contains a page that displays the FeaturedBookPart and FeaturedBookEditorPart controls. Listing 30.20. ShowFeaturedBookEditorPart.aspx <%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <%@ Register TagPrefix="user" TagName="FeaturedBookPart" src="/books/3/444/1/html/2/~/FeaturedBookPart.ascx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> Protected Sub Menu1_MenuItemClick(ByVal sender As Object, ByVal e As MenuEventArgs) WebPartManager1.DisplayMode = WebPartManager1.DisplayModes(e.Item.Text) End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <style type="text/css"> .column { float:left; width:30%; height:200px; margin-right:10px; border:solid 1px black; background-color: white; } .menu { margin:5px 0px; } html { background-color:#eeeeee; } </style> <title>Show Help Display Mode</title> </head> <body> <form runat="server"> <asp:WebPartManager Runat="server" /> <asp:Menu OnMenuItemClick="Menu1_MenuItemClick" Orientation="Horizontal" Css Runat="server"> <Items> <asp:MenuItem Text="Browse" /> <asp:MenuItem Text="Design" /> <asp:MenuItem Text="Edit" /> </Items> </asp:Menu> <asp:WebPartZone Css Runat="server"> <ZoneTemplate> <user:FeaturedBookPart Title="Featured Book" runat="Server" /> </ZoneTemplate> </asp:WebPartZone> <asp:WebPartZone Css Runat="server"/> <asp:EditorZone Css runat="server"> <ZoneTemplate> <asp:LayoutEditorPart runat="server" /> </ZoneTemplate> </asp:EditorZone> </form> </body> </html> | After you open the page in Listing 30.20, you can click the Edit link and then select the Edit menu option on the FeaturedBookPart. When you edit the FeaturedBookPart, both our custom FeaturedBookEditorPart and the standard LayoutEditorPart will appear in the Editor Zone (see Figure 30.7). Notice that an instance of the LayoutEditorPart control was declared in the Editor Zone contained in the page. Figure 30.7. Displaying a custom editor. It took a lot of work to create the custom Editor Part control. Too much work to simply display an editor form. In the next section, you'll learn how to avoid doing any of this work in the future. In the next section, we create a templated Editor Part. Creating a Templated Editor Part In this section, we create the last Editor Part that you'll ever need to make. We will create a Templated Editor Part, which will enable you to easily build any type of editor form that you need. You can create the custom form for the Templated Editor Part with a form defined in a user control file. The Templated Editor Part is contained in Listing 30.21. Listing 30.21. TemplatedEditorPart.vb Imports System Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' Enables you to use templates when ''' editing Web Parts ''' </summary> Public Class TemplatedEditorPart Inherits EditorPart Private _editTemplateUrl As String Private _editTemplate As ITemplatedEditorPart ''' <summary> ''' Loads the user control that contains ''' the Edit Template ''' </summary> Protected Overrides Sub CreateChildControls() _editTemplate = CType(Page.LoadControl(_editTemplateUrl), ITemplatedEditorPart) Controls.Add(CType(_editTemplate, Control)) End Sub ''' <summary> ''' Utility method that returns the child control ''' in the case of a GenericWebPart ''' </summary> Private ReadOnly Property ControlToEdit() As Control Get If TypeOf Me.WebPartToEdit Is GenericWebPart Then Return (CType(WebPartToEdit, GenericWebPart)).ChildControl Else Return Me.WebPartToEdit End If End Get End Property ''' <summary> ''' Called when the user clicks Apply or OK ''' in the Editor Zone ''' </summary> ''' <returns></returns> Public Overrides Function ApplyChanges() As Boolean EnsureChildControls() Return _editTemplate.ApplyChanges(ControlToEdit) End Function ''' <summary> ''' Called when the Editor Template ''' displays current Web Part property ''' values. ''' </summary> Public Overrides Sub SyncChanges() EnsureChildControls() _editTemplate.SyncChanges(ControlToEdit) End Sub ''' <summary> ''' Pass the Editor Part Title and Template ''' URL to the constructor ''' </summary> Public Sub New(ByVal title As String, ByVal editTemplateUrl As String) Me.Title = title _editTemplateUrl = editTemplateUrl End Sub End Class ''' <summary> ''' Defines the contract that any Edit Template ''' must satisfy ''' </summary> Public Interface ITemplatedEditorPart Function ApplyChanges(ByVal controlToEdit As Control) As Boolean Sub SyncChanges(ByVal controlToEdit As Control) End Interface End Namespace | The constructor for the TemplatedEditorPart class takes a title and editTemplateUrl parameter. The editTemplateUrl parameter specifies the location of a user control that contains the edit form used by the TemplatedEditorPart control. The user control is loaded in the CreateChildControls() method. Notice that the TemplatedEditorPart control derives from the base EditorPart class. It implements the ApplyChanges() and SyncChanges() from the base class. In this case, however, the control simply calls methods with the same name in the user control that it loads. The Web Part in Listing 30.22 uses the TemplatedEditorPart control. Listing 30.22. FeaturedVideoPart.ascx The Web Part in Listing 30.22 displays a featured video. Notice that the Web Part implements the IWebActionable interface with its CreateEditorParts() method and WebBrowsableObject property. The CreateEditorParts() method returns an instance of the TemplatedEditorPart control. The TemplatedEditorPart is initialized with the path to a user control named FeaturedVideoEditTemplate. This user control contains the template for editing the Web Part, and it is contained in Listing 30.23. Listing 30.23. FeaturedVideoEditTemplate.ascx [View full width] <%@ Control Language="VB" ClassName="FeaturedVideoEditTemplate" %> <%@ Reference Control="~/FeaturedVideoPart.ascx" %> <%@ Implements Interface="myControls.ITemplatedEditorPart" %> <%@ Import Namespace="myControls" %> <script runat="server"> Public Sub SyncChanges(ByVal controlToEdit As Control) Implements ITemplatedEditorPart .SyncChanges Dim part As ASP.FeaturedVideoPart = CType(controlToEdit, ASP.FeaturedVideoPart) txtVideoTitle.Text = part.VideoTitle txtDirector.Text = part.Director txtPrice.Text = part.Price.ToString() End Sub Public Function ApplyChanges(ByVal controlToEdit As Control) As Boolean Implements ITemplatedEditorPart.ApplyChanges Dim success As Boolean = False Page.Validate() If Page.IsValid Then Dim part As ASP.FeaturedVideoPart = CType(controlToEdit, ASP.FeaturedVideoPart) part.VideoTitle = txtVideoTitle.Text part.Director = txtDirector.Text part.Price = Decimal.Parse(txtPrice.Text) success = True End If Return success End Function </script> <asp:Label Text="Video Title:" AssociatedControl Runat="server" /> <br /> <asp:TextBox Runat="server" /> <br /><br /> <asp:Label Text="Director:" AssociatedControl Runat="server" /> <br /> <asp:TextBox Runat="server" /> <br /><br /> <asp:Label Text="Price:" AssociatedControl Runat="server" /> <br /> <asp:TextBox Runat="server" /> <asp:CompareValidator ControlToValidate="txtPrice" Display="dynamic" Text="(Must be Currency)" Type="Currency" Operator="DataTypeCheck" Runat="server" /> <asp:RequiredFieldValidator ControlToValidate="txtPrice" Display="dynamic" Text="(Required)" Runat="server" /> | Notice that the user control in Listing 30.23 implements the ITemplatedEditorPart interface with its SyncChanges() and ApplyChanges() methods. The SyncChanges() method initializes the form fields in the user control with the current values of the properties of the Web Part being edited. The ApplyChanges() method updates the Web Part being edited with changes made in the edit form. The page in Listing 30.24 uses the TemplatedEditorPart control to edit the FeaturedVideoPart control. Listing 30.24. ShowTemplatedEditorPart.aspx <%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <%@ Register TagPrefix="user" TagName="FeaturedVideoPart" src="/books/3/444/1/html/2/~/FeaturedVideoPart.ascx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> Protected Sub Menu1_MenuItemClick(ByVal sender As Object, ByVal e As MenuEventArgs) WebPartManager1.DisplayMode = WebPartManager1.DisplayModes(e.Item.Text) End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <style type="text/css"> .column { float:left; width:30%; height:200px; margin-right:10px; border:solid 1px black; background-color: white; } .menu { margin:5px 0px; } html { background-color:#eeeeee; } </style> <title>Show Templated Editor Part</title> </head> <body> <form runat="server"> <asp:WebPartManager Runat="server" /> <asp:Menu OnMenuItemClick="Menu1_MenuItemClick" Orientation="Horizontal" Css Runat="server"> <Items> <asp:MenuItem Text="Browse" /> <asp:MenuItem Text="Design" /> <asp:MenuItem Text="Edit" /> </Items> </asp:Menu> <asp:WebPartZone Css Runat="server"> <ZoneTemplate> <user:FeaturedVideoPart Title="Featured Video" runat="server" /> </ZoneTemplate> </asp:WebPartZone> <asp:WebPartZone Css Runat="server" /> <asp:EditorZone Css Runat="server" /> </form> </body> </html> | After you open the page in Listing 30.24, you can view the TemplatedEditorPart by clicking the Edit link and selecting a Web Part to edit (see Figure 30.8). Figure 30.8. Using the Templated Editor Part. The nice thing about the TemplatedEditorPart control is that you can associate a different edit template with each of the different types of Web Parts in a page. Just pass the path to a different template when initializing the TemplatedEditorPart in each Web Part's CreateEditorParts() method. |