How to: Create a Custom Field Control

This topic describes how to create a custom field control that renders a Windows Media player on a Publishing site, which plays a Windows Media Player–compatible media file stored in a Microsoft Office SharePoint Server 2007 document library in your site collection, or in another external location. You can include this field control in any of the page layouts in your Publishing site collection if the page layout is assigned to a content type that includes a Publishing Hyperlink column. The Publishing Hyperlink column stores the URL that points to the media file.

The presentation and design of this field control is similar to other Publishing field controls available in Office SharePoint Server 2007, such as the RichHtmlField and RichImageField controls. Office SharePoint Server 2007 achieves the consistent look and feel by using the SimpleToolbar class to create similar HTML for the edit-time toolbar, and it uses the AssetUrlSelector class to launch a similar Asset Picker dialog box.

The main pieces of this field control are:

  • MediaPlayerFieldControl control. This control inherits from Microsoft.SharePoint.WebControls.BaseFieldControl. This is the field control itself, and is responsible for storing the URL of the media file specified by the user in the underlying Windows SharePoint Services 3.0 list (the Page library), retrieving that URL, and rendering it inside the markup of a Windows Media player in a Web browser window.

  • MediaSelector control. This control inherits from System.Web.UI.WebControls. It is the edit control for the MediaPlayerFieldControl control, which appears only when the page is in edit mode. When the page is in edit mode, this class controls both the viewing and editing experience for the field control.

  • System.Web.UI.WebControls.TextBox (mediaUrlTextBox) control. The system displays a System.Web.UI.WebContols.TextBox (mediaUrlTextBox) control that contains the URL that points to the current Windows Media file. Additionally, a toolbar button is provided and an AssetUrlSelector object creates its onclick JavaScript. To select a media file to play, the user can either type the URL directly into the mediaUrlTextBox control or click the toolbar button. Clicking the toolbar button opens an Asset Picker dialog box that allows the user to navigate through the hierarchy of the current site collection to find a file.

  • HtmlValidationContext object. The MediaPlayerFieldControl control uses an HtmlValidationContext object in its Validate method to ensure that the URL is valid before the control saves it. Optionally, the HtmlValidationContext object can ensure that it is stored in the current site collection and not on an external server.

  • SimpleToolbar. This toolbar inherits from Microsoft.SharePoint.WebControls.RepeatedControls, which provides the markup required to render the field control toolbar that displays when the page is in edit mode.

The field control stores the URL of the Windows Media file in a Publishing Hyperlink column in your Pages library. Therefore, you can use the field control only in page layouts that are assigned to a content type that has a spare Publishing Hyperlink column.

The namespace, MediaPlayerControlAssembly, version, and token parameters must match the appropriate values for your compiled assembly, and MediaFileURL must match the name of your Publishing Hyperlink column.

The following procedures and example show you how to set up your computer to use the sample, how to modify a page layout and the Publishing Hyperlink column to support the sample, and the C# code needed to include a Windows Media player on a Publishing site.

To set up your computer to use this sample

  1. Open Visual Studio and create a new project.

  2. Copy the C# code in the example below into a new CS file in the project.

  3. Compile the project as a strong assembly.

  4. Add the assembly to the global assembly cache or to a bin directory that is accessible to Office SharePoint Server 2007.

  5. Open the web.config file, add a <SafeControl/> entry for the assembly and namespace as shown, and save the file.

    <SafeControl Assembly="MediaPlayerControlAssembly" 
    Namespace="Microsoft.SDK.SharePointServer.Samples" TypeName="*" />
    
  6. Open one or more page layout files and add the field control. See the next procedure to learn how.

To add the field control to a page layout

  1. Include an @Register directive at the top of the page layout file. This directive registers the namespace of the control and the assembly:

    <%@ Register Tagprefix="CustomFieldControl" Namespace="namespace" 
    Assembly="MediaPlayerControlAssembly, Version=version, 
    Culture=neutral, PublicKeyToken=token" %>
    
  2. Add the field control markup at the position in the page where the movie file should appear.

  3. Manually set the FieldName property of the tag to the name of the Publishing Hyperlink column:

    <CustomFieldControl:MediaPlayerFieldControl 
    id="SampleMediaPlayerFieldControl" FieldName="MediaFileURL" 
     />
    

Example

This example demonstrates how to create a custom field control that renders a Windows Media player on a Publishing site. The field control plays a Windows Media Player–compatible media file stored in either a Microsoft Office SharePoint Server 2007 document library in your site collection or another external location.

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Publishing.WebControls;
using Microsoft.SharePoint.Publishing.Fields;

namespace Microsoft.SDK.SharePointServer.Samples

/// A Field control that binds to fields of type LinkField and is 
/// specialized to select and render embedded media files.
/// The RenderFieldForDisplay function generates the HTML markup to 
/// display the media file.  The MediaSelector control is
/// used at edit time to allow authors to select a media file in
/// the Asset Picker dialog box.

{
    public class MediaPlayerFieldControl : BaseFieldControl
    {
        private MediaSelector mediaSelector = new MediaSelector();

        public MediaPlayerFieldControl()
        {
        }

        /// Gets and sets the value in the edit controls
        public override object Value
        {
            get
            {
                LinkFieldValue mediaUrlValue = new LinkFieldValue();
                mediaUrlValue.href = 
                  this.mediaSelector.MediaUrl;
                mediaUrlValue.Text = 
                  LinkFieldValue.GetDefaultDisplayText(mediaUrlValue.href);

                return mediaUrlValue;
            }
            set
            {
                LinkFieldValue mediaLinkFieldValue = 
                  value as LinkFieldValue;
                if (null != mediaLinkFieldValue)
                {
                    this.mediaSelector.MediaUrl = 
                      mediaLinkFieldValue.href;
                }
                else
                {
                    this.mediaSelector.MediaUrl = String.Empty;
                }
            }
        }

        /// Get the default name used to find the template and 
        /// control for the MediaPlayerSelector in the control 
        /// template ASCX files.
        protected override string DefaultTemplateName
        {
            get { return "MediaPlayerFieldControl"; }
        }

        private const string AllowExternalUrlsViewStateKey = "AllowExternalUrls";
        /// A flag that determines whether to allow saving of external 
        /// media URLs.
        public bool AllowExternalUrls
        {
            get
            {
                // Return true by default if not already in view state.
                if (ViewState[AllowExternalUrlsViewStateKey] == null)
                {
                    return true;
                }
                return (bool)ViewState[AllowExternalUrlsViewStateKey];
            }
            set
            {
                ViewState[AllowExternalUrlsViewStateKey] = value;
            }
        }

        /// Creates the edit control when not in display mode.
        protected override void CreateChildControls()
        {

            base.CreateChildControls();

            if (this.ControlMode != SPControlMode.Display)
            {
                MediaSelector mediaSelectorInTemplate = 
                  this.TemplateContainer.FindControl(this.TemplateName) 
                  as MediaSelector;

                if (null == mediaSelectorInTemplate)
                {
                    // No media selector was found in the control
                    // template ASCX files. Add the default selector.
                    this.Controls.Add(this.mediaSelector);
                }
                else
                {
                    // Get the media selector from the control 
                    // template ASCX file.
                    mediaSelectorInTemplate.MediaUrl = 
                      this.mediaSelector.MediaUrl;
                    this.mediaSelector = mediaSelectorInTemplate;
                }
            }
        }

        /// Gets the current value for the media URL as stored
        /// in the list item.
        private string itemFieldValueMediaUrl
        {
            get
            {
                LinkFieldValue currentLinkValue = 
                  this.ItemFieldValue as LinkFieldValue;
                if (null != currentLinkValue)
                {
                    return currentLinkValue.href;
                }
                else
                {
                    return String.Empty;
                }
            }
        }

        /// Renders the current list item value for the media URL
        /// with embedded media player markup.
        /// <param name="output"></param>
        protected override void 
          RenderFieldForDisplay(System.Web.UI.HtmlTextWriter output)
        {
            if (!String.IsNullOrEmpty(this.itemFieldValueMediaUrl))
            {
                output.Write(MediaRenderingUtilities.GetMediaPlayerHtmlMarkup(this.itemFieldValueMediaUrl));
            }
        }

        /// Verifies that the MediaUrl is valid.
        public override void Validate()
        {
            base.Validate();
            if (this.IsValid)
            {
                LinkFieldValue currentMediaUrlValue = 
                  this.Value as LinkFieldValue;

                if (currentMediaUrlValue == 
                  null || String.IsNullOrEmpty(currentMediaUrlValue.href))
                {
                    // Ensure the field is not required.
                    if (this.Field != null && this.Field.Required)
                    {
                        this.IsValid = false;
                        this.ErrorMessage = 
                          "This field is required and must contain a media file URL.";
                        return;
                    }
                    else
                    {
                        // The field is empty and not required.
                        // The data is valid.
                        return;
                    }
                }

                // Perform validation on the media file URL.
                HtmlValidationContext validationContext = 
                  new HtmlValidationContext();

                if (!this.AllowExternalUrls)
                {
                    // Restrict URLs to be either from the current site
                    // collection or server-relative.
                    validationContext.RestrictUrlsToSiteCollection = true;
                    validationContext.GuidOfThisSiteCollection = 
                      SPContext.Current.Site.ID;
                }

                bool droppedTags;
                bool droppedUrls;
                LinkFieldValue validatedValue =
                    validationContext.ValidateLinkValue(
                        currentMediaUrlValue,
                        out droppedTags,
                        out droppedUrls);

                if (droppedUrls || String.IsNullOrEmpty(validatedValue.href))
                {
                    // The media file URL in the link field value was
                    // not valid so report the error message.
                    // Setting IsValid to false stops saving the page.
                    this.IsValid = false;
                    this.ErrorMessage = 
                      "The URL for the media file was invalid.";
                    if (!this.AllowExternalUrls)
                    {
                        this.ErrorMessage += 
                          "  You must select a URL within the current site collection.";
                    }
                }
            }
        }
    }

    /// This edit control for the MediaPlayerFieldControl has 
    /// a toolbar and text box for selecting a media file URL.
    /// This example intentionally uses a separate toolbar button
    /// and text box for the AssetUrlSelctor to show a more complex
    /// example. You can use an AssetUrlSelector control instead of 
    /// a TextBox child control, which displays its own browse button.
    public class MediaSelector : WebControl
    {
        private TextBox mediaUrlTextBox = new TextBox();

        public MediaSelector()
        {
        }

        /// This is the media URL value that you can edit in the text
        /// box or Asset Picker dialog box.
        public string MediaUrl
        {
            get { return this.mediaUrlTextBox.Text; }
            set { this.mediaUrlTextBox.Text = value; }
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            // This ensures that the TextBox child control receives
            // its postback.
            EnsureChildControls();
        }

        /// Gets JavaScript required to launch an Asset Picker dialog
        /// box for choosing a media file URL.
        private string GetAssetPickerButtonScript()
        {
            AssetUrlSelector mediaAssetSelector = 
              new AssetUrlSelector();

            // When the AssetUrlSelector control is not added to the
            // page control tree, the Page and ID properties are
            // required because
            // AssetUrlSelector.GetClientLaunchPickerReference() 
            // needs register script in the page.
            mediaAssetSelector.Page = this.Page;
            mediaAssetSelector.ID = "MediaUrlAssetSelector";

            // Uses the TextBox client ID to connect the Asset Picker
            // dialog box to the text box.
            mediaAssetSelector.AssetUrlClientID = 
              this.mediaUrlTextBox.ClientID;

            // Autopostback to see the new media file rendered after
            // clicking OK on the Asset Picker dialog box.
            mediaAssetSelector.AutoPostBack = true;

            mediaAssetSelector.OverrideDialogTitle = "Select a media file";
            mediaAssetSelector.OverrideDialogDescription = 
              "Select a media file to embed in this page";
            mediaAssetSelector.UseImageAssetPicker = false;

            return mediaAssetSelector.GetClientLaunchPickerReference();
        }

        private Literal mediaPlayerOutput = new Literal();
        protected override void CreateChildControls()
        {
            SimpleToolbar mediaSelectorToolbar = new SimpleToolbar();
            mediaSelectorToolbar.ID = "ToolBar";

            this.Controls.Add(mediaSelectorToolbar);

            Label mediaUrlLabel = new Label();
            mediaUrlLabel.Text = "Selected media file URL: ";
            mediaUrlLabel.AssociatedControlID = "MediaUrlTextBox";
            this.Controls.Add(mediaUrlLabel);

            this.mediaUrlTextBox.ID = "MediaUrlTextBox";
            this.mediaUrlTextBox.CssClass = 
              "ms-input ms-lactiontable sample-mediaselector-urltextbox";
            this.Controls.Add(this.mediaUrlTextBox);

            // Add the button after the rest so that the text box
            // ClientID is already determined and can be connected
            // in the Asset Picker dialog box client script.
            mediaSelectorToolbar.AddToolbarButton(
                "SelectMediaFile",
                "Select a media file",
                this.GetAssetPickerButtonScript(),
                "Open a picker to select a media file URL");

            // Add a refresh button to perform a basic postback to
            // to update the MediaUrl rendering.
            mediaSelectorToolbar.AddToolbarButton(
                "RefreshMediaFile",
                "Refresh",
                this.Page.ClientScript.GetPostBackEventReference(this, 
                  String.Empty),
                  "Refresh the page to reload the current media file URL",
                  "/_layouts/IMAGES/refresh.gif");

            // If there is a media file URL, this code creates
            // the media player markup.
            this.Controls.Add(this.mediaPlayerOutput);
        }

        protected override void OnPreRender(EventArgs e)
        {
            string mediaFileOutputHtml = 
              MediaRenderingUtilities.GetMediaPlayerHtmlMarkup(this.MediaUrl);
            if (String.IsNullOrEmpty(mediaFileOutputHtml))
            {
                this.mediaPlayerOutput.Text = 
                  "<BR>{There is no valid media file URL to display}<BR>";
            }
            else
            {
                this.mediaPlayerOutput.Text = 
                  "<BR>" + mediaFileOutputHtml + "<BR>";
            }

            base.OnPreRender(e);
        }
    }

    /// A simple toolbar class that matches the styles of the
    /// publishing field control toolbars.
    public class SimpleToolbar : RepeatedControls
    {
        public SimpleToolbar()
        {
            this.HeaderHtml = 
              "<div class=\"ms-toolbarContainer\" width=\"100%\">";
            this.FooterHtml = "</div>";
            this.SeparatorHtml = "";
        }

        public void AddToolbarButton(
            string buttonId,
            string buttonText,
            string clientOnClick,
            string tooltipText)
        {
            Literal buttonMarkupLiteral = new Literal();

            buttonMarkupLiteral.Text = String.Format(
                    SimpleToolbarButtonHtmlFormat,
                    SPHttpUtility.HtmlEncode(buttonText),
                    SPHttpUtility.HtmlEncode(clientOnClick),
                    SPHttpUtility.HtmlEncode(tooltipText));
            buttonMarkupLiteral.ID = buttonId;

            this.Controls.Add(buttonMarkupLiteral);
        }

        public void AddToolbarButton(
            string buttonId,
            string buttonText,
            string clientOnClick,
            string tooltipText,
            string buttonImageSrc)
        {
            Literal buttonMarkupLiteral = new Literal();

            buttonMarkupLiteral.Text = String.Format(
                    SimpleToolbarButtonImageHtmlFormat,
                    SPHttpUtility.HtmlEncode(buttonText),
                    SPHttpUtility.HtmlEncode(clientOnClick),
                    SPHttpUtility.HtmlEncode(tooltipText),
                    SPHttpUtility.HtmlUrlAttributeEncode(buttonImageSrc));
            buttonMarkupLiteral.ID = buttonId;

            this.Controls.Add(buttonMarkupLiteral);
        }

        // {0} = Button text
        // {1} = onclick script
        // {2} = Tooltip text
        private const string SimpleToolbarButtonHtmlFormat = @"
        <DIV class=""ms-toolbarItem ms-selectorlink"">
           <A href=""#"" onclick=""{1}"" title=""{2}"">&nbsp;{0}</A>
        </DIV>";

        // {0} = Button text
        // {1} = onclick script
        // {2} = Tooltip text
        // {3} = Button image markup
        private const string SimpleToolbarButtonImageHtmlFormat = @"
        <DIV class=""ms-toolbarItem ms-selectorlink"">
           <A href=""#"" onclick=""{1}"" title=""{2}"">
           <IMG alt=""{2}"" src=""{3}"" border=""0"">{0}</A>
        </DIV>";
    }

    public static class MediaRenderingUtilities
    {
        /// Take a media file URL and generate HTML markup
        /// for playing the file.
        /// <param name="mediaUrl"></param>
        public static string GetMediaPlayerHtmlMarkup(string mediaUrl)
        {
            // HtmlUrlAttributeEncode returns an empty string if the 
            // URL protocol is not allowed (e.g., JavaScript:)
            string encodedUrl =
                SPHttpUtility.HtmlUrlAttributeEncode(mediaUrl);

            if (String.IsNullOrEmpty(encodedUrl))
            {
                return String.Empty;
            }
            else
            {
                return String.Format(MediaPlayerHtmlMarkupFormat, encodedUrl);
            }
        }

        // Currently, this code includes only a parameter for the media
        // file URL, but it could also include parameters for the
        // width, height, and other rendering properties from field
        // control properties or authored data value properties.
        private const string MediaPlayerHtmlMarkupFormat = @"
<object type=""video/x-ms-wmv""
  data=""{0}""
  width=""300"" height=""450"">
  <param name=""src"" value=""{0}"" />
  <param name=""autostart"" value=""true"" />
  <param name=""controller"" value=""true"" />
</object>
";
    }
}

See Also

Tasks

How to: Display Custom Fields in a Content Query Web Part

Concepts

How to: Customize the Asset Picker
How to: Customize the HTML Editor Field Control