Serving less HTML code starting from .NET 4.0 with ClientIDMode property

原帖地址:http://devnet.kentico.com/Knowledge-Base/Web-parts-Controls/Serving-less-HTML-code-starting-from-NET-4-0-with.aspx

One of the best practices in general from performance perspective is to transfer as little data between a client and a server as possible. When it comes to web sites, we need to ensure that the final HTML code is clean and we are not transferring any extra information which is not utilized by the client’s browser at all (e.g. comments in HTML code, unnecessary long control IDs etc.). This article will help you to end up with both cleaner HTML code and optimized performance.
As you probably know, .NET of version 3.5 and previous renders control’s ID by concatenating the ID values of each parent naming container with the ID value of the control. In other words, you could end up with really long control ID:

<a id="p_lt_ctl03_MyShoppingCartPreview_lnkMyAccount" class="ShoppingCartLink" href="/70DEV/SpecialPages/User/My-account.aspx">My account</a>

In this specific example above, the yellow highlighted part is what you can configure in the “Control ID” property of  Shopping Cart Preview  web part, so keeping the value as short as possible is one of the best practices. However, the best idea would be not to render such concentrated ID at all (red + yellow part of the ID) and only keep the defined control ID (marked as green). Luckily you can change this behavior from .NET of version 4.0 with ClientIDMode property which can be configured on control or page level. Available options are  AutoID Static Predictable  and  Inherit and you can read more about each of these options  here . If we use “Static” option, then the final HTML code from the example above will look like this:

<a id="lnkMyAccount" class="ShoppingCartLink" href="/70DEV/SpecialPages/User/My-account.aspx">My account</a>

One way on how to end up with above is to simply set the ClientIDMode in your web.config file. This will however influence all the HTML code application-wise and administration interface (UI) of Kentico might not work properly. What if we would like to change this behavior for (A) all web parts on a page or (B) only for a specific web parts? 

In case we would do it on a page level (A), we can access  Page  property from any control (web part) and simply set its  ClientIDMode  property. You can create  custom web part  for this purpose on your own or simply download an existing one  here . Such web part's code could like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/// <summary>
/// OnPrerender override (Set ClientIDMode).
/// </summary>
protected override void OnPreRender(EventArgs e)
{
     base .OnPreRender(e);
 
     switch (ClientIDMode)
     {
         case "AutoID" :
             this .Page.ClientIDMode = System.Web.UI.ClientIDMode.AutoID;
             break ;
 
         case "Inherit" :
             this .Page.ClientIDMode = System.Web.UI.ClientIDMode.Inherit;
             break ;
 
         case "Predictable" :
             this .Page.ClientIDMode = System.Web.UI.ClientIDMode.Predictable;
             break ;
 
         case "Static" :
             this .Page.ClientIDMode = System.Web.UI.ClientIDMode.Static;
             break ;
     }
}


By exposing custom property (let's call it ClientIDMode), you can change this setting comfortably any time via web part configuration dialog:

screen-(1).png

In case we would like to programmatically set this property for each web part separately (B) we would need to touch the code of Kentico’s system objects (built-in web parts). This will definitely lead to hotfix/upgrade related issues in future. Let’s implement this functionality by not touching any of the system code files.
  1. First of all, we need to introduce custom web part property (let’s call it ClientIDMode) which will be system property available for all web parts. System web part properties are defined in xml files sitting on a file system in~\App_Data\CMSModules\PortalEngine\Properties\_Groups folder. Following is a default XML code (default.xml) representing two built-in properties WebPartControlID andWebPartTitle:
1
2
<field column= "WebPartControlID" fieldcaption= "Web part control ID" visible= "true" columntype= "text" fieldtype= "textbox" allowempty= "false" columnsize= "50" fielddescription= "Serves as an identifier for the web part. This ID must be unique within the context of each page template. The value of this property may only contain alphanumeric characters and the underscore character ( _ )." regularexpression= "\w+" validationerrormessage= "Control ID must match identificator format." guid= "3718b090-f3a5-464d-919a-a11e9b5eb934" />
<field column= "WebPartTitle" fieldcaption= "Web part title" visible= "true" columntype= "text" fieldtype= "textbox" allowempty= "true" columnsize= "200" fielddescription= "Title of the web part displayed on the Design tab of CMS Desk and in on-site editing mode. If empty, the value of the Web part control ID property is used for this purpose." guid= "2bf266ee-b4fb-4f3b-9a91-523cf7b55e7e" />

 
We can of course extend this file, but it would not be wise decision because it is a system file. Let’s create custom one ( ~\App_Data\CMSModules\KB102\PortalEngine\Properties\_Groups\Default.xml) and extend custom one with following code (marked as bold):

1
2
3
4
5
6
7
8
9
10
11
12
13
<field column= "WebPartControlID" fieldcaption= "Web part control ID" visible= "true" columntype= "text" fieldtype= "textbox" allowempty= "false" columnsize= "50" fielddescription= "Serves as an identifier for the web part. This ID must be unique within the context of each page template. The value of this property may only contain alphanumeric characters and the underscore character ( _ )." regularexpression= "\w+" validationerrormessage= "Control ID must match identificator format." guid= "3718b090-f3a5-464d-919a-a11e9b5eb934" />
<field column= "ClientIDMode" fieldcaption= "Client ID mode" visible= "true" columntype= "text" fieldtype= "CustomUserControl" allowempty= "true" columnsize= "11" fielddescription= "documentation.webpartproperties.clientidmode" guid= "3718b090-f3a5-464d-919a-a11e9b5eb935" >
   <settings>
     <controlname>DropDownListControl</controlname>
     <Options>
       autoid;AutoID
       static ;Static
       inherit;Inherit
       predictable;Predictable
     </Options>
   </settings>
  </field>
<field column= "WebPartTitle" fieldcaption= "Web part title" visible= "true" columntype= "text" fieldtype= "textbox" allowempty= "true" columnsize= "200" fielddescription= "Title of the web part displayed on the Design tab of CMS Desk and in on-site editing mode. If empty, the value of the Web part control ID property is used for this purpose." guid= "2bf266ee-b4fb-4f3b-9a91-523cf7b55e7e" />
  1. As a second step, we need to force Kentico to load the custom xml file instead of the original. What we can do is to implement custom storage provider overriding couple of default methods where we will ensure loading of different file instead. Let’s create a class files KB102CustomStorageProvider (inheriting from CMS.IO.StorageProvider) andKB102CustomFile (inheriting from CMS.CMSStorage.File) in ~\App_Code\CMSModules\KB102location (in case of web site project type) where we will override ReadAllText method in following way:
  
1
2
3
4
5
6
7
8
9
10
11
12
13
/// <summary>
    /// Opens a text file, reads all lines of the file, and then closes the file.
    /// </summary>
    /// <param name="path">Path to file</param>
    public override string ReadAllText( string path)
    {
        if (path.ToLower().Contains( "cmsmodules\\portalengine\\properties\\_groups\\default.xml" ))
        {
            path = path.Replace( "\\PortalEngine\\Properties\\_Groups\\Default.xml" , "\\KB102\\PortalEngine\\Properties\\_Groups\\Default.xml" );
        }
 
        return base .ReadAllText(path);
    }

 
The above code will load our   default.xml  file for web part system properties from default group / category. We also need to register our custom storage provider. We will use another class file ( KB102HandlerLoader  inheriting from   CMSLoaderAttribute) for this purpose and register the custom storage provider:
 
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public override void PreInit()
         {
             // Initialize storage provider settings - application start
             SettingsHelper.AppSettings[ "CMSStorageProviderAssembly" ] = "KB102CustomStorageProvider" ;
             SettingsHelper.AppSettings[ "CMSExternalStorageName" ] = "KB102Custom" ;
 
             // Register custom storage provider
             AbstractStorageProvider myKB102Provider = new KB102CustomStorageProvider();
             StorageHelper.MapStoragePath( "~/" , myKB102Provider);
         }
 
         private static void GetCustomClass( object sender, ClassEventArgs e)
         {
             if (e.Object == null )
             {
                 switch (e.ClassName)
                 {
                     case "KB102CustomStorageProvider" :
                         e.Object = new KB102CustomStorageProvider();
                         break ;
                 }
             }
         }


At this moment, you should be able to see a new web part property to set / configure:
 
screen.png

  1. The last step would be to make sure the selected value is implemented in code of the web part. We will register for OnBeforeUserControlPreRender event of CMSAbstractWebPartclass where we can access the web part and its properties:
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public override void Init()
         {
             CMSAbstractWebPart.OnBeforeUserControlPreRender += new CMS.ExtendedControls.BeforeEventHandler(CMSAbstractWebPart_OnBeforeUserControlPreRender);
         }
 
         bool CMSAbstractWebPart_OnBeforeUserControlPreRender( object sender, EventArgs e)
         {
             if (sender is CMSAbstractWebPart)
             {
                 CMSAbstractWebPart webPart = sender as CMSAbstractWebPart;
 
                 string clientIdMode = ValidationHelper.GetString(webPart.GetValue( "ClientIDMode" ), "" );
 
                 if (! string .IsNullOrEmpty(clientIdMode))
                 {
                     switch (clientIdMode.ToLower())
                     {
                         case "static" :
                             webPart.ClientIDMode = ClientIDMode.Static;
                             break ;
 
                         case "inherit" :
                             webPart.ClientIDMode = ClientIDMode.Inherit;
                             break ;
 
                         case "predictable" :
                             webPart.ClientIDMode = ClientIDMode.Predictable;
                             break ;
 
                         default :
                             webPart.ClientIDMode = ClientIDMode.Predictable;
                             break ;
                     }
                 }
             }
 
             return true ;
         }


Complete implementation can be downloaded as export package  here  and  imported  into Kentico of version 7.0.23 or later. Please make sure that “Import Files” option is checked during the import procedure.

NOTE: Kentico of version 7.x still supports .NET of version 3.5 and this is one of the reasons why the above functionality is not integrated in the out-of-the-box solution. Kentico version 8 will support only .NET 4.0 and later.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值