+1-888-365-2779
Try Now
More in this section
Categories
Bloggers
Blogs RSS feed

Using Sitefinity's Selectors in Custom Field Controls - Part 1 - Page Selector Field

by Liubomir Velkov

This blog post is the first part of a series that will describe how to use the various Sitefinity selectors in your own custom field controls. Selectors are very useful when you need to pick an existing item from your project - page, user, role, image, etc.

The first part will describe how to use the GenericPageSelector control to be able to select a single existing page from your sitemap.

At the end of this post there is a link to the full working project that contains the different field controls. This file will be updated periodically to include the new field controls in the next parts of this series.

Basics

The base class for all field controls is Telerik.Sitefinity.Web.UI.Fields.FieldControl. Our PageSelectorField inherits directly from it. When you define a new field control you need to specify its "DefinitionElement" and "FieldDefinition" classes. Using the

[FieldDefinitionElement(typeof(PageSelectorFieldElement))]

attribute you assign PageSelectorFieldElement to the field control. This class inherits from FieldControlDefinitionElement and is very simple. Basically you need a constructor, you need to override

public override DefinitionBase GetDefinition()

where you return a new instance of your "FieldDefinition" class - in this case

return new PageSelectorFieldDefinition(this);

and you need to override

public override Type DefaultFieldType

to return the type of your field -

get
{
    return typeof(PageSelectorField);
}

The PageSelectorFieldDefinition inherits from the base FieldControlDefinition class and is very simple as well. You can check the code for details.

Your field control needs an .ascx template and a JavaScript file that will hold the client-side logic. These files are marked as Embedded resources and are named PageSelectorField.ascx and PageSelectorField.js. In the template file you define two conditional templates and in the "Write" template you put the controls that will be needed to edit your field's value. There is an instance of RadWindowManager and one RadWindow dialog, since we'll be using RadWindow as a popul dialog to select the page.

In order to register this additional dialog add a Global.asax file and add the following lines to it:

protected void Application_Start(object sender, EventArgs e)
{
    Bootstrapper.Initialized += new EventHandler<Telerik.Sitefinity.Data.ExecutedEventArgs>(Bootstrapper_Initialized);
}
 
void Bootstrapper_Initialized(object sender, Telerik.Sitefinity.Data.ExecutedEventArgs e)
{
    Telerik.Sitefinity.Web.UI.Dialogs.RegisterDialog<SelectorFields.PageSelector.PageSelectorFieldDialog>();
}

Field Specifics

We'll be storing the selected page in the form <Page_Guid>;<Page_Titles_Path>. So a tipical value saved in the database would be "0f642308-4191-4fd7-9a91-8d007b5b7c67;Home Page > Products > Sitefinity". This is needed to prevent additional retrieving of the breadcrumb titles on the client side when retrieving the field's value. You can change that to your liking.

The field control overrides the following methods:

public override void Configure(IFieldDefinition definition)
 
protected override void InitializeControls(GenericContainer container)
 
public override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
 
public override IEnumerable<ScriptReference> GetScriptReferences()

that are part of the abstract FieldControl class. In the template there is a SitefinityLabel control that shows the currently selected page and a HyperLink that opens the popup dialog. You can see how the different events are hooked in PageSelectorField.js.

The popup dialog has its own .ascx template and JavaScript file - PageSelectorFieldDialog.ascx and PageSelectorFieldDialog.js, which are also marked as EmbeddedResource. Its class - PageSelectorFieldDialog - inherits from a base class for such dialogs - AjaxDialogBase. This class also overrides GetScriptReferences() and GetScriptDescriptors() to register the client side fields and scripts that are needed for it.

In the dialog template we use the GenericPageSelector, which we register with the following markup:

<sitefinity:GenericPageSelector
                id="GenericPageSelector1"
                runat="server"
                RootNodeID="F669D9A7-009D-4d83-DDAA-000000000002"
                WebServiceUrl="~/Sitefinity/Services/Pages/PagesService.svc/"
                ShowOnlySelectedAsGridOnLoad="true"
                   MarkItemsWithoutTranslation="true"
                AllowMultipleSelection="false" />

You can customize this according to your needs. The RootNodeID is constant and is the page ID of site maps front-end root.

There are some events hooked for the Done and Cancel buttons. The currently selected page is may be retrieved using the following code:

var selectedValue = this.get_pageSelector().get_selectedItem(); 

Then this value is returned after closing the dialog using

dialogBase.close(selectedValue);

Back in the field control's JavaScript file, the

_windowClosed: function (sender, args) { }

event is raised and you can retrieve the selected page with

var selectedPage = args.get_argument();

The selectedPage object contains various properties about the page you have selected, of which we will use two - .Id and .TitlesPath. We'll assign a composed value the field's .value property, which will be persisted to the database after saving.

Since the field binds client-side, when it is loaded with the value from the database, the client-side method

set_value: function (value) { }

is called, where we extract the Titles part of the value and set the Text of the while label that shows the currently selected page with

jQuery(this._selectedPageLabel).html(value.substring(37));

The field is basically ready. You can now register a new custom field of this type for some Content type - e.g. News. Go to the "Custom Fields for news" screen and add a new field with the following settings:

Registering a Page Selector Field

After saving the changes you can go edin a News item and you will get the new field in action that should look like this:

Page Selector Field in Action

An finally a link to the full project - SelectorFields.zip

P.S. I attached the same project but without using RadWindow for the popup dialog. This is a more lightweight version that does not require to register the dialog in Global.asax and is less prone to errors when using the field control in custom modules - SelectorFieldsNoRadWindow 

25 comments

Leave a comment
  1. Ronald Dec 08, 2011
    Great tutorial,

    but i can't get it to work....

    I have tried to the following:
    - extract the zip file into:
        * my root web folder: "~/SelectorFields"
        * Selectorfields folder inside rootfolder: "~/SelectorFields/SelectorFields"
    - include it as an stand alone project ( reference )
    - include it as an stand alone project ( reference ) placed the resource folder inside root folder of sitefinity project.
    - include it as an stand alone project ( reference ) placed the resource folder inside the SelectorFields folder inside the root folder of sitefinity project. ( "~/SelectorFields/Resources")

    i'm kind of puzzled what I do wrong...
    the error I get all the time: 

    Cannot find template "~/SelectorFields/SelectorFields.Resources.PageSelectorField.ascx".

    With Reflector i can look inside the compiled DLL and see that the resource files are there.

    Please tell me what to do.

  2. Ronald Dec 08, 2011
    Great tutorial,

    but i can't get it to work....

    I have tried to the following:
    - extract the zip file into:
        * my root web folder: "~/SelectorFields"
        * Selectorfields folder inside rootfolder: "~/SelectorFields/SelectorFields"
    - include it as an stand alone project ( reference )
    - include it as an stand alone project ( reference ) placed the resource folder inside root folder of sitefinity project.
    - include it as an stand alone project ( reference ) placed the resource folder inside the SelectorFields folder inside the root folder of sitefinity project. ( "~/SelectorFields/Resources")

    i'm kind of puzzled what I do wrong...
    the error I get all the time: 

    Cannot find template "~/SelectorFields/SelectorFields.Resources.PageSelectorField.ascx".

    With Reflector i can look inside the compiled DLL and see that the resource files are there.

    Please tell me what to do.

  3. Lubomir Velkov Dec 09, 2011
    Did you properly register your virtual path setting? Go to Administration -> Settings -> Advanced -> VirtualPathSettings -> Virtual paths and add new with the following:

    VirtualPath - ~/SelectorFields/*
    ResourceLocation - SelectorFields
    ResolverName - EmbeddedResourceResolver

    Save changes, recycle the app pool and it should work. Do not do the steps that you described in your comment
  4. Ronald Dec 12, 2011
    Thanks for your reply,

    i've done what you have suggested, and now the page is shown correctly.

    but....
    If I try to select a page i get the following error.

    the resource cannot be found:
    Requested URL: /Sitefinity/Dialog/PageSelectorFieldDialog

    I am using sitefinity 4.3.1873.0
    is this the same version as the one used for this tutorial?
  5. Haroon Dec 21, 2011
    it doesn't work with me either ??
  6. Dieter Dec 22, 2011
    Hello! I have the same problem (ressource cannot be found). Greetings, Dieter
  7. Lubomir Velkov Dec 23, 2011
    One step I forgot to mention in the blog post is that you need to register the new dialog in Global.asax.cs. Here is the code:

    protected void Application_Start(object sender, EventArgs e)
    {
    Bootstrapper.Initialized += new EventHandler<Telerik.Sitefinity.Data.ExecutedEventArgs>(Bootstrapper_Initialized);
    }

    void Bootstrapper_Initialized(object sender, Telerik.Sitefinity.Data.ExecutedEventArgs e)
    {
    Telerik.Sitefinity.Web.UI.Dialogs.RegisterDialog<SelectorFields.PageSelector.PageSelectorFieldDialog>();
    }

    I have attached the code in the archive, but it really isn't very obvious that you need to include it. I will update the blog post now.
  8. Ronald Dec 29, 2011
    Thanks for your update.

    please note that,
    Global.ascx and Global.ascx.cs are placed within the sitefinity web app project instead of the Selector fields project.

    after that it works fine.

    but now the following question:
    on the front-end, how can I use this Page field?
  9. Ben Dec 29, 2011
    I get "Could not load file or assembly 'SelectorFields' or one of its dependencies. The system cannot find the file specified." I have the files in a ~/SelectorFields folder and have set the virtual path settings accordingly. Any idea what I might be doing wrong?

    Thanks,

    -Ben
  10. Lubomir Velkov Dec 30, 2011
    Did you reference the SelectorFields project from your main Sitefinity web project? If you did then you should get a copy of SelectorFields.dll in the /bin folder of your application and it should be working properly.
  11. Lubomir Velkov Jan 21, 2012
    Attached at the end of the post is an alternative version that uses jQuery UI Dialog instead of RadWindow to load the popup dialog.
  12. Jörg Feb 21, 2012
    Does not work for me. I tried both, with and w/o RadWindow. I get "IVirtualFileResolver is an interface and cannot be constructed". Well, thats obviously true, but where it comes from?
  13. Ben Mar 15, 2012
    The jQuery UI Dialog version gives me javascript errors in 4.4
  14. Ben Mar 15, 2012
    The jQuery UI Dialog version gives me javascript errors in 4.4
  15. Ben Mar 15, 2012
    I'm able to get the SelectorFields.zip version working but is there any way to deselect a page after its been previously selected?
  16. Anton Nov 28, 2012
    I have an exception in JS:
    SitefinityWebApp is not defined.

    It seems that my namespace (SiteifnityWebApp) are loading after another JS resource...

    Can you help me to resolve this issue?
  17. Dennis Dec 02, 2012
    I tried the lightweight version "SelectorFieldsNoRADWindow" and it was working.

    However i dont see the full path of the sub-page (e.g parent page > parent page > selected page), its only showing the name of the sub-page.
     
    And when I try to use the value, its giving me something like this "465379b7-8e4c-469f-b87e-e65339cd565c;SelectedPageName" which cant be used directly.

    Wondering if this is the reason why the full path is not shown
  18. Bob Apr 12, 2013
    The current type, Telerik.Sitefinity.Abstractions.VirtualPath.IVirtualFileResolver, is an interface and cannot be constructed. Are you missing a type mapping?

    Is what I am getting. Can you please help me with this.
  19. Bert Jun 21, 2013
    I had to add the following line under the sf:ResourceLinks node in the ascx file, to get this to work.

    <sf:ResourceFile JavaScriptLibrary="JQueryUI" />

    When this line is not there, jQuery UI is not loaded (except if it needs to be loaded for another field on the page), and the javascript crashes on "initialize" when trying to bind the dialog behavior.
  20. Bert Jun 21, 2013
    I do run into problems when trying to run this example in a multilanguage site. 

    Upon "_onLoad" method the "_pageSelector" is NULL. Allthough it was set in the "set_pageSelector" method. Any thoughts on what needs to be changed to make this work in a multilingual site?
  21. Jason Sep 17, 2013
    Did anyone ever find a solution to the IVirtualFileResolvererror?

    The current type, Telerik.Sitefinity.Abstractions.VirtualPath.IVirtualFileResolver, is an interface and cannot be constructed. Are you missing a type mapping?
  22. Herby Dec 06, 2013
    In answer to Bob and Jason, getting exception 'The current type, Telerik.Sitefinity.Abstractions.VirtualPath.IVirtualFileResolver, is an interface and cannot be constructed. Are you missing a type mapping?:

    I also got this opening a user's profile while fiddling around with a custom field control; it seemed I'd set the ResolverName  in the virtual path settings  for the control to 'Embedded'  instead of 'EmbeddedResourceResolver'. After correcting this the exception was gone...
  23. diwi Jan 11, 2014
    Great job, thank you.

    I'm trying to use the PageSelectorNoRadWindow in a custom widget, not in Designer. Open the "window" selecting the page, closing the window and showing the selected page ist working fine. However, on saving the form, the value of the PageSelector is always empty. Do I have to do some more Javascript work in my widget to get the value from PageSelector?

    Thanks, diwi

  24. Sabina Feb 10, 2014
    Hi,

    Thank you for the tutorial!
    The only way I got this working though, was by creating a Sitefinity  Field Control using Sitefinity Thunder and then customize it. Also, added the JQueryUI reference in Bert's comment. Thank you!

    Good luck!
  25. Kamran Shahid Apr 15, 2014

    What i tried was

    1, Take  SelectorFieldsNoRadWindow compiled it with sitefinity 6.3 dlls

    2, Reference the dll in my web project.

    3,In my web project added a folder  SelectorFields and copy PageSelectorField.ascx and PageSelectorField.js in that folder.include those files in my project.

    4, made virtual path entries as 

    VirtualPath - ~/SelectorFields/*
    ResourceLocation - SelectorFields
    ResolverName - EmbeddedResourceResolver

    5, In my dynamic module added field as mentioned in the snaps http://www.sitefinity.com/sitefinityImages/lubomir-velkovs-images/PageSelectorField.png?sfvrsn=0

    Now when i were trying to add contents the user interface of the item is gone mad and not working properly.

    Please let me know what wrong i have done

     

    Leave a comment