December 2012 - Development Simply Put

A blog simplifies main concepts in IT development and provides tips, hints, advices and some re-usable code. "If you can't explain it simply, you don't understand it well enough" -Albert Einstein

  • Development Simply Put

    If you can't explain it simply, you don't understand it well enough.

    Read More
  • Integrant

    Based in the U.S. with offshore development centers in Jordan and Egypt, Integrant builds quality custom software since 1992. Our staff becomes an extension of your team to eliminate the risks of software development outsourcing and our processes...

    Read More
  • ITWorx

    ITWorx is a global software professional services organization. Headquartered in Egypt, the company offers Portals, Business Intelligence, Enterprise Application Integration and Application Development Outsourcing services to Global 2000 companies.

    Read More
  • Information Technology Institute

    ITI is a leading national institute established in 1993 by the Information and Decision Support Centre.

    Read More

2012-12-31

How To Change Sharepoint PageLayout Icon To Custom Image

In Sharepoint, when you create a new article page, you have to choose a page layout for this article to control the way the article page will appear. So, to differentiate between different page layouts from UI, each page layout has its own title and an image. Thus, once you click on any page layout, its corresponding image appears.

This image should somehow reflect the basic structure of the layout so that you can differentiate between all of the layouts based on what you see in the image. So, if the page layout consists of three columns, the image should reflect this. Also, if the page layout consists of two columns, the image should reflect this and so on.

There are two places in system UI where you can see the page layout image.

When creating a new article page and selecting its page layout

When changing the page layout of an existing article page

So, when you work on your custom page layouts, the OOTB page layout images may not be suitable for your layouts. So, you may need to assign your custom descriptive images to these page layouts to maintain the same concept.

When I first decided to do this while working on my project, I thought that I will just need to set the "PublishingPreviewImage" property of my page layouts in the "Elements.xml" file. But, when I tried to do it, I found that the images of my custom layouts are still the default ones.

After some searching, I found out that some of the page layouts properties are not updated once they are set and deployed by upgrading the solution. The only way to update these images is to retract then deploy your solution or to do it using some code executed at the feature activation.

Sure I decided to take the second approach. So, now I need to write some code to be executed when the feature -provisioning my custom page layouts- is activated to set the image of my page layouts explicitly by code.

The "Elements.xml" file looks as follows;
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <Module Name="pagelayouts" Url="_catalogs/masterpage">
  <File Path="pagelayouts\layout1.aspx" Url="MyLayouts/layout1.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 1" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout1.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
  <File Path="pagelayouts\layout2" Url="MyLayouts/layout2.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 2" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout2.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
 </Module>
</Elements>

So, now in the feature activation, I can set the "PublishingPreviewImage" property of each of these two page layouts to the path of my custom images. This is easy, but, I thought I can achieve the same result without depending on some hard-coded values in my code, instead, I can depend on the values already set in the "Elements.xml" file.

So, to do this, I used the approach I already explained before in a previous post. It is the post called "How Get Real-time Info Of Sharepoint Module Files On Feature Activation Event Listener". If you didn't read this post, please try to read it before you move to the next step.

So, now the code will be as follows;
using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using DevelopmentSimplyPut.CommonUtilities.Helpers;

namespace DevelopmentSimplyPut.News.Features.pagelayouts
{
    [Guid("e882ad84-444a-421a-961c-bb945556c3f8")]
    public class pagelayoutsEventReceiver : SPFeatureReceiver
    {
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            PageLayoutsHelper.UpdatePageLayoutsPublishingPreviewImage(properties);
        }
    }
}

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Web;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
using System.Globalization;
using System.Xml;
using Microsoft.SharePoint.Publishing;
using System.Collections.ObjectModel;

namespace DevelopmentSimplyPut.CommonUtilities.Helpers
{
    public static class PageLayoutsHelper
    {
  public static void UpdatePageLayoutsPublishingPreviewImage(SPFeatureReceiverProperties properties)
  {
   SPSite site = null;

   if (properties.Feature.Parent is SPSite)
   {
    site = properties.Feature.Parent as SPSite;
   }
   else
   {
    site = (properties.Feature.Parent as SPWeb).Site;
   }

   if(null != site)
   {
    PublishingSite publishingSite = new PublishingSite(site);
    PageLayoutCollection layouts = publishingSite.PageLayouts;
    List<ModuleInfo> moduleInfoCollection = FeaturesHelper.GetModuleFilesInfo(properties);
    foreach (PageLayout layout in layouts)
    {
     string layoutIdentifier = layout.ServerRelativeUrl;
     ModuleInfo moduleInfo = moduleInfoCollection.DefaultIfEmpty(null).FirstOrDefault(module => module.Url == "_catalogs/masterpage");
     if (null != moduleInfo)
     {
      ModuleFile fileInfo = moduleInfo.Files.DefaultIfEmpty(null).FirstOrDefault(file => layoutIdentifier.Contains(file.Url));
      if (null != fileInfo)
      {
       string imagePath = string.Empty;

       if (fileInfo.Properties["PublishingPreviewImage"].Contains(","))
       {
        imagePath = fileInfo.Properties["PublishingPreviewImage"].Split(',')[0].Trim().ToUpperInvariant();  
       }
       else
       {
        imagePath = fileInfo.Properties["PublishingPreviewImage"].Trim().ToUpperInvariant();
       }

       if (imagePath.Contains("~SITECOLLECTION"))
       {
        string siteCollectionPath = site.ServerRelativeUrl.ToUpperInvariant();
        if (!siteCollectionPath.EndsWith("/"))
        {
         siteCollectionPath += "/";
        }
        imagePath = imagePath.Replace("~SITECOLLECTION/", siteCollectionPath).Replace("~SITECOLLECTION", siteCollectionPath);
       }

       if (!string.IsNullOrEmpty(imagePath))
       {
        SPFile layoutFile = layout.ListItem.File;
        SPFileHelper.PrepareFileForChanges(layoutFile);
        layout.PreviewImageUrl = imagePath;
        layout.Update();
        SPFileHelper.FinalizeFile(layoutFile);
       }
      }
     }
    }
   }
  }
 }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;

namespace DevelopmentSimplyPut.CommonUtilities.Helpers
{
    public static class SPFileHelper
    {
        public static void PrepareFileForChanges(SPFile file)
        {
            if (null != file)
            {
                bool checkOutEnabled =
                    file.Item == null
                        ? file.ParentFolder.Item.ParentList.ForceCheckout
                        : file.Item.ParentList.ForceCheckout;

                if (checkOutEnabled)
                {
                    if (file.CheckOutType != SPFile.SPCheckOutType.None)
                    {
                        file.UndoCheckOut();
                    }
                    file.CheckOut();
                }
            }
        }
        public static void FinalizeFile(SPFile file)
        {
            if (null != file)
            {
                bool checkOutEnabled =
                        file.Item == null
                            ? file.ParentFolder.Item.ParentList.ForceCheckout
                            : file.Item.ParentList.ForceCheckout;

                bool needsApproval =
                    file.Item == null
                        ? file.ParentFolder.Item.ParentList.EnableModeration
                        : file.Item.ParentList.EnableModeration;

                if (checkOutEnabled)
                {
                    file.CheckIn(string.Empty, SPCheckinType.MajorCheckIn);
                }

                if (needsApproval)
                {
                    file.Approve(string.Empty);
                }

                file.Update();
            }
        }
    }
}

So, now every time the feature is activated, the page layouts images will be updated with the ones set in the "Elements.xml".


That's it. Hope this will help you someday.
Good Luck.


 

2012-12-28

How To Get Real-time Info Of Sharepoint Module Files On Feature Activation Event Listener

Sometimes you need to run some code at the moment of certain feature activation. This is for sure doable by creating an event receiver on the desired feature and then overriding the code of the "FeatureActivated" method. This topic is not the main topic here so I will assume that you already know how to do this.

Ok, but what I will be talking about in this post is how to use the module "Elements.xml" file to get live or real-time data to be used inside your "FeatureActivated" code. Let me explain first what I really mean here.

Sometimes in your "FeatureActivated" code, you need to use some info you already provided in the module "Elements.xml" file. This info could be some files names, some properties or some URLs. For sure the code you write in the "FeatureActivated" code is somehow case specific and you may have no problem to duplicate this type of info between the "Elements.xml" and the "FeatureActivated" code, I know that. But, this introduces an new problem when you apply some changes on the "Elements.xml" and you forget to apply the same changes on the "FeatureActivated" code. This discrepancy in info will for sure cause you problems.

So, the best approach is to centralize your info in one place for you to be more easy and stable to apply further changes and I believe that the best place to use is the "Elements.xml" file cause the main concept behind this file is to make configurations more easy and manageable, so let's keep it that way.

Actually, it is more than that. By centralizing your info in the "Elements.xml" file, you provide system admins with the ability to apply some changes on these deployed "Elements.xml" files to reflect some changes on the live system and this will not need applying any changes on code cause your code uses the "Elements.xml" file as its source of info.

I think now we know what we need to achieve and this is the point where we go deep into some code.

Now, we need some code to access the "Elements.xml" file of certain feature inside the "FeatureActivated" code of this feature to get some info about the modules and their files to be able to use this info in the rest of the "FeatureActivated" code.

Before jumping into the code for this part, let's refresh our memory and have a look onto a sample "Elements.xml" file.
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
 <Module Name="pagelayouts" Url="_catalogs/masterpage">
  <File Path="pagelayouts\layout1.aspx" Url="MyLayouts/layout1.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 1" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout1.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
  <File Path="pagelayouts\layout2" Url="MyLayouts/layout2.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE" >
    <Property Name="Title" Value="Layout 2" />
    <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
    <Property Name="PublishingPreviewImage" Value="~SiteCollection/_layouts/IMAGES/MyLayouts/layout2.png" />
    <Property Name="PublishingAssociatedContentType" Value=";#First Content Type;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D003ac3c10715a04460946daa43bdfc1294002defb7a9b25f4735aa77637a948b471d;#"/>
  </File>
 </Module>
</Elements>

This is an "Elements.xml" file for a module having two files. These files are page layouts and each of them has some info like "title", "content type" and "publishing preview image". The files themselves have some info like "path" and "url". Also, the module has info like "name" and "url".

So, now let's see the code for opening this xml file and extracting these info to be used.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using System.Linq;
using System.Text;
using System.Globalization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace DevelopmentSimplyPut.Helpers
{
    public static class FeaturesHelper
    {
        public static List GetModuleFilesInfo(SPFeatureReceiverProperties instance)
        {
            List modules =
                    (from SPElementDefinition element in instance.Feature.Definition.GetElementDefinitions(CultureInfo.CurrentCulture)
                     where element.ElementType == "Module"
                     let xmlns = XNamespace.Get(element.XmlDefinition.NamespaceURI)
                     let module = XElement.Parse(element.XmlDefinition.OuterXml)
                     select new ModuleInfo
                     {
                         Url = module.Attribute("Url").Value,
                         Path = Path.Combine(element.FeatureDefinition.RootDirectory, module.Attribute("Url").Value),
                         Files = (from file in module.Elements(xmlns.GetName("File"))
                                  select new ModuleFile
                                  {
                                      Url = file.Attribute("Url").Value,
                                      Properties = (from property in file.Elements(xmlns.GetName("Property"))
                                                    select property).ToDictionary(
                                                          n => n.Attribute("Name").Value,
                                                          v => v.Attribute("Value").Value)
                                  }).ToList()
                     }).ToList();

            return modules;
        }
    }
    public class ModuleFile
    {
        public string Url { set; get; }
        public Dictionary Properties { set; get; }
    }
    public class ModuleInfo
    {
        public string Url { set; get; }
        public string Path { set; get; }
        public List Files { set; get; }
    }
}

So, now in the "FaeatureActivated" code;
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
 List moduleInfoCollection = FeaturesHelper.GetModuleFilesInfo(properties);
 
 /*
 foreach(ModuleInfo moduleInfo in moduleInfoCollection)
 {
  //moduleInfo.Url
  //moduleInfo.Path
  foreach(ModuleFile fileInfo in moduleInfo.Files)
  {
   //fileInfo.Url
   foreach(KeyValuePair property in fileInfo.Properties)
   {
    //property.Key
    //property.Value
   }
  }
 }
 */
}

You can use the info you get about the modules and files to do what you really want. For sure you may need to use some hard-coded files properties keys but at least these keys should be fixed in the first place and they are not subject to frequent changes (or any changes maybe). But, you have the advantage of dynamic files properties values and this is the most important thing. Now, when you apply changes on these values in the "Elements.xml" file you don't have to track these values in the code to apply matching changes.

Some may think that this is too much code to run for the sake of this purpose and that this approach will affect the overall performance. May be they are right but let's face it, this code will run only once every time the feature is activated and this doesn't happen frequently. Actually, if the system admin needs to activate and deactivate your feature frequently, you may need to reconsider the design.


That's it, hope you find this post helpful.
Good Luck.


 

2012-12-26

How To Make Sure jQuery Is Loaded And Only Once

While working with Sharepoint you will notice that adding jQuery to the main masterpage is not a good idea cause sometimes and with some jQuery versions problems happen just after adding jQuery even without using it. May be some of you encountered this problem or not but at least it happened to me and some other colleagues.

So, the first lesson here is, don't add jQuery on every page except when you really need it.

Also, when working with webparts, you may be using jQuery on more than one webpart, so you need to make sure that jQuery is added to whatever page including one of these webparts. Any page may include one or multiple of these webparts, so you can't depend on making only one of them responsible for adding jQuery to the page cause in case of absence of this webpart any jQuery code will fail.

Another thing, suppose that a page includes two webparts which require jQuery to be added to the page. One is loaded at the header of the page, lets call it "A", while the other is loaded at the footer of the same page, lets call it "B". If "A" extends jQuery object with some custom code, then "B" added jQuery again to the page, this will override the custom code introduced by "A" and jQuery will be reverted to its original source code. I am sure this is not what you really need.

So, the second lesson here, add jQuery only once to the page.

Another thing, you can add jQuery to the page using some JavaScript code which will add a "script" tag to the DOM, but you can't start using jQuery just after running this JavaScript code because jQuery library itself is not yet fully loaded.

So, the third lesson here, before firing jQuery code, you need to make sure that jQuery is fully loaded and ready to perform.


So, now before going through the practical approach to overcome all of these issues, lets summarize what we really need to achieve here.

We need to:
  1. Add jQuery to pages only when needed
  2. In case of adding jQuery to a page, make sure to add it only once
  3. Before firing any jQuery code, make sure jQuery is fully loaded


After searching for a while, I found some tips and came up with the code below.

This is a method called "RunWithJQuerySupport" which you can add to a common utilities js file. This method takes care of all the three points above.
function RunWithJQuerySupport(success) {
    if (typeof jQuery == 'undefined') {
        function getScript(success1) {
            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');
            script.src = "_layouts/1033/JS/ITWorx.UTCNow/jquery-1.8.1.min.js";
            done = false;
            script.onload = script.onreadystatechange = function () {
                if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
                    done = true;
                    if (typeof (success1) != 'undefined' && success1 != null) { success1(); }
                    script.onload = script.onreadystatechange = null;
                };
            };
            head.appendChild(script);
        };

        getScript(function () {
            if (typeof jQuery == 'undefined') {
                // Super failsafe - still somehow failed...
            } else {
                //jQuery.noConflict();
                if (typeof (success) != 'undefined' && success != null) { success(); }
            }
        });
    } else {
        if (typeof (success) != 'undefined' && success != null) { success(); }
    };
}

So, suppose that somewhere on a page or a webpart you need to call a method called "StartAjaxCall"
and you want to make sure that your call will follow the three points above. To achieve this, you can use the code below.
function StartAjaxCall(){
 alert("Ajax Call Started");
}

RunWithJQuerySupport(StartAjaxCall);

This will make sure that the three points above are followed and finally fire your method “StartAjaxCall”.

If you need to only add jQuery to the page without calling any other method, you can call "RunWithJQuerySupport" without passing any parameters as follows.
RunWithJQuerySupport();

Please note that in the "RunWithJQuerySupport" method, the url for the jQuery.js file is hard-coded on the 6th line. You can update it or even add it as a parameter to the “RunWithJQuerySupport” method to be passed with each call.


That's all, wish you find this helpful.


2012-12-23

Using "EditModePanel" To Show/Hide Webparts In Edit/Display Mode in Sharepoint 2007/2010

While working on Sharepoint pagelayouts I needed to control whether an element will appear on edit/display mode. I specifically needed a webpart zone to appear in the edit mode but not appear in the display mode.

So, after searching, I found an OOTB control called "EditModePanel" which has an attribute to decide the mode in which the control children will be rendered. So, if this attribute -which is called "PageDisplayMode" by the way- is set to "Display", then the control contents will be rendered in the display mode of the page. While, if it is set to "Edit", then the control contents will be rendered in the edit mode of the page.

Then, I thought that I found what I need. But, after using this control, I figured out a problem with it. In the days of Sharepoint 2007, this control would have done exactly what I needed, but the case is different with Sharepoint 2010.

In Sharepoint 2010, Microsoft decided to introduce a security change to this control which I really can't understand till now. Microsoft added a part to this control which checks for the privilege of the logged in user. If this user has edit privilege on the page, then everything is ok and the control works as supposed to. But, if the user doesn't have edit privilege on the page, the control will never render its children whatever its display mode is set to.

So, to imagine the whole thing;

In Sharepoint 2007
<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Display">
 <!-- content here will be rendered in page display mode -->
</PublishingWebControls:EditModePanel>

<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Edit">
 <!-- content here will be rendered in page edit mode -->
</PublishingWebControls:EditModePanel>

In Sharepoint 2010
<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Display">
 <!-- if user has edit privilege, content here will be rendered in page display mode -->
</PublishingWebControls:EditModePanel>

<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Edit">
 <!-- if user has edit privilege, content here will be rendered in page edit mode -->
</PublishingWebControls:EditModePanel>

So, this didn't help me achieve what I needed for my case. So, after searching, I found some other ways to do it using a combination between the "EditModePanel" and "AuthoringContainer". The "AuthoringContainer" is a panel which also has the two modes but I didn't like this solution cause it has its limitations beside it is somehow complex.

This lead me to another idea, let's reflect the "EditModePanel" to find the newly introduced security change then inherit from the control and override this method. So, I reflected the control and found the line which causes the problem. The method I need to override is called "CalculateShouldRender" and the line causing me troubles is like the following
if (!ConsoleUtilities.CheckPermissions(SPBasePermissions.EditListItems))
{
 this.shouldRender = false;
}

This is good, I can now override this method. But, I noticed that the control is sealed which means I can't inherit from it, so, I had to make one extra step. I copied the same code of the control as it is except for the "CalculateShouldRender" method, I removed the security check.

This was the hard part. I started using my new control and used it to wrap the webpart zone which I wanted to display only into the edit mode. Then I started testing my work, I opened the page into edit mode, found the zone, added a webpart to the zone, crossed my fingers and switched to display mode. What?!!!!

I found that the zone itself is not displayed into the display mode, but, the webpart -which was located into the zone- is moved to another zone on the page and it is displayed!!!!!

So, after some searching, I figured out that Sharepoint is somehow sensitive against webparts that are found on the page but will not be displayed. In such occasion, Sharepoint decides to move this webpart to the nearest visible zone. Why meeeeeeeeee.......

So, I decided to take another approach, instead of wraping the zone inside my control, I will leave the zone as it is and I will write some css to hide my webpart and locate this css inside the control while setting the control to display mode. So, now, in the display mode, the css will be rendered and the webpart will disappear. For sure I know that the webpart actually exists on the page and its code will be running but in my case this is enough.


Wish you find this helpful.
Bye.


 

2012-12-21

Sharepoint "ListItemPropertyField" WebControl To Render Any Subproperty Of ListItem Field

I was working on a Sharepoint 2010 project and I had a task to modify a custom page layout to include the e-mail of the article owner. I found that this article owner field is a custom field in the pages library and its type is "Person or Group".

So, I started to trace this field to find how I can get the email. I found that I can use an OOTB Sharepoint webcontrol called "BaseFieldControl" which has a property called "FieldName". When this property is set to the name of the field in a Sharepoint listitem, it fires its ToString() to get a string value for this field to be rendered.

But, this couldn't help me cause the email property I need is a subproperty of the listitem field. So, knowing that the field name is "ContentOwner", then to get the user email I will need to get "ContentOwner.User.Email". Actually, I will need to get "ContentOwner[0].User.Email" cause ContentOwner is a collection.

So, I searched for something OOTB to provide such capability but I couldn't and that's why I decided to make my own WebControl. This control should provide some powerful capabilities to be an asset to keep and reuse whenever needed.

So, I decided to prepare this control to have the following features:
  1. Get subproperty of a listitem field
  2. This field may be a collection, so need the ability to provide an index, if not provided assume zero (ie.: ContentOwner[1])
  3. Get multilevel subproperty of the field which could be represented by a properties pipeline (ie.: User.Email of ContentOwner[0])
  4. Be able to provide indexes for subproperties if they are collections or assume zero if indexes not provided (ie.: SubPropertyA[1].SubPropertyB[0].SubPropertyC[3] of Property)
  5. Be able to provide a type converter for the final subproperty in case of required custom conversion or manipulation code (ie.: like converting bool from true/false to yes/no or good/bad ......)
  6. Be able to provide a pattern to format the final output

So, I gave it a thought and then went deep into code -which I really enjoyed by the way :)- and I came out with the code below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Reflection;
using System.Collections;
using System.Web.UI;
using System.ComponentModel;
using Microsoft.SharePoint.WebControls;

[assembly: TagPrefix("DevelopmentSimplyPut.Sharepoint.WebControls", "DevelopmentSimplyPutControls")]
namespace DevelopmentSimplyPut.Sharepoint.WebControls
{
    [ParseChildren(ChildrenAsProperties = true)]
    [PersistChildren(false)]
    [ToolboxData("<{0}:ListItemPropertyField runat=\"server\"></{0}:ListItemPropertyField>")]
    public class ListItemPropertyField : BaseFieldControl, INamingContainer
    {
        public string Property
        {
            get;
            set;
        }

        public string RenderTemplate
        {
            get;
            set;
        }

        public string ItemIndex
        {
            get;
            set;
        }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public TypeConverter PropertyTypeConverter
        {
            get;
            set;
        }

        public override void UpdateFieldValueInItem()
        {

        }

        protected override void Render(System.Web.UI.HtmlTextWriter output)
        {
            try
            {
                object fieldValue = this.ListItem[this.FieldName];
                fieldValue = GetItemFromCollectionByIndex(fieldValue, ItemIndex);

                string subPropertyValue = string.Empty;
                object subPropertyObject = null;
                if (!string.IsNullOrEmpty(Property))
                {
                    subPropertyObject = GetSubPropertyValue(fieldValue, Property);
                }
                else
                {
                    subPropertyObject = fieldValue;
                }

                if (null != PropertyTypeConverter)
                {
                    subPropertyObject = ApplyTypeConverter(subPropertyObject, PropertyTypeConverter);
                }

                if (!string.IsNullOrEmpty(RenderTemplate))
                {
                    subPropertyValue = string.Format(CultureInfo.InvariantCulture, RenderTemplate, subPropertyObject);
                }
                else
                {
                    subPropertyValue = subPropertyObject.ToString();
                }
                output.Write(subPropertyValue);
            }
            catch (NullReferenceException ex)
            {
                //Do something
            }
            catch (ArgumentOutOfRangeException ex)
            {
                //Do something
            }
            catch (ArgumentNullException ex)
            {
                //Do something
            }
            catch (ArgumentException ex)
            {
                //Do something
            }
        }

        private static object GetSubProperty(object item, string property, string index)
        {
            Type type = item.GetType();
            PropertyInfo propertyInfo = type.GetProperty(property);
            object value = propertyInfo.GetValue(item, null);

            return GetItemFromCollectionByIndex(value, index);
        }

        private static object GetSubPropertyValue(object item, string property)
        {
            object parentItem = item;
            if (!string.IsNullOrEmpty(property))
            {
                string[] parts = property.Split('.');
                for (int i = 0; i < parts.Length; i++)
                {
                    string[] subParts = parts[i].Split('[');
                    string propertyName = parts[i];
                    string index = "0";

                    if (subParts.Length > 1)
                    {
                        propertyName = subParts[0];
                        index = subParts[1].Replace("]", "");
                    }
                    else
                    {
                        propertyName = parts[i];
                    }

                    parentItem = GetSubProperty(parentItem, propertyName, index);
                }
            }

            return parentItem;
        }

        private static object GetItemFromCollectionByIndex(object searchCollection, string index)
        {
            ICollection collection = searchCollection as ICollection;
            object result = searchCollection;
            if (collection != null)
            {
                int requestedIndex = 0;
                int itemsCount = collection.Count;

                if (!string.IsNullOrEmpty(index))
                {
                    if (!int.TryParse(index, out requestedIndex))
                    {
                        requestedIndex = 0;
                    }
                }

                IEnumerator enumObject = collection.GetEnumerator();
                requestedIndex = Math.Max(0, requestedIndex);
                requestedIndex = Math.Min(itemsCount, requestedIndex);

                for (int x = 0; x < itemsCount; x++)
                {
                    enumObject.MoveNext();

                    if (x == requestedIndex)
                    {
                        result = enumObject.Current;
                        break;
                    }
                }
            }

            return result;
        }

        private static object ApplyTypeConverter(object property, TypeConverter converter)
        {
            object result = property;

            if (null != property && null != converter)
            {
                if (!string.IsNullOrEmpty(converter.AssemblyFullyQualifiedName) && !string.IsNullOrEmpty(converter.ClassFullyQualifiedName) && !string.IsNullOrEmpty(converter.MethodName))
                {
                    AssemblyName assemblyName = new AssemblyName(converter.AssemblyFullyQualifiedName);
                    Assembly assembly = Assembly.Load(assemblyName);
                    Type classType = assembly.GetType(converter.ClassFullyQualifiedName);
                    object[] parametersArray = new object[] { property };
                    result = classType.InvokeMember(converter.MethodName, System.Reflection.BindingFlags.InvokeMethod, System.Type.DefaultBinder, "", parametersArray);
                }
            }

            return result;
        }
    }

    public class TypeConverter
    {
        public string AssemblyFullyQualifiedName
        {
            get;
            set;
        }
        public string ClassFullyQualifiedName
        {
            get;
            set;
        }
        public string MethodName
        {
            get;
            set;
        }
    }
}

Notes
  1. The control is inherited from "BaseFieldControl" for extension
  2. The method "UpdateFieldValueInItem" is overridden by an empty method to prevent the control from updating the value of the field in the listitem. It took me some time to figure this out :) 
  3. I used reflection to get subproperties of the field property
  4. Some string manipulation is used to get indexes of subproperties
  5. The type converter is provided by setting three properties
    1. AssemblyFullyQualifiedName: this is the assembly fully qualified name (see the example below)
    2. ClassFullyQualifiedName: this is the fully qualified name of the class which includes the conversion method (see the example below)
    3. MethodName: this is the name of the conversion method (see the example below)
  6. To calculate the last value to be rendered, I first get the final subproperty value by reflection, then pass it to the type converter (if provided) and finally apply the formatting pattern (if provided) 
  7. I used the "Render" method to render the final value


So, now to use this control on a page layout, first we need to register the control on the page as follows:
<%@ Register Tagprefix="DevelopmentSimplyPutControls" Namespace="DevelopmentSimplyPut.Sharepoint.WebControls" Assembly="DevelopmentSimplyPut.Sharepoint.WebControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7a0271768eefdc39" %>

Then we can place the control on the page as follows:
<DevelopmentSimplyPutControls:ListItemPropertyField runat="server" id="ContentOwnerEmail" FieldName="ContentOwner" Property="User.Email" RenderTemplate="Email:{0}" ItemIndex="0">
 <PropertyTypeConverter MethodName="EmailCustomConverter" ClassFullyQualifiedName="DevelopmentSimplyPut.Utilities.TypeConverters" AssemblyFullyQualifiedName="DevelopmentSimplyPut.Utilities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7a0271768eefdc39"/>
</DevelopmentSimplyPutControls:ListItemPropertyField>

Notes
  1. The "Property" attribute is optional. If not provided, then the value returned will be the value of the field property, no subproperties
  2. The "ItemIndex" attribute is optional. If not provided, in case of collection field property, it will be assumed to be zero. I provided it here just for clarification but it could be ignored
  3. The "RenderTemplate" attribute is optional. If provided, it will be used to format the final value. Every "{0}" in the template will be replaced by the final value
  4. The "PropertyTypeConverter" inner property is optional. If provided, the control will load the conversion method from its assembly provided by reflection and apply this method on the final value
  5. For "PropertyTypeConverter" to work:
    1. The assembly provided should be in the GAC
    2. The class including the method should be static
    3. The conversion method should be static


Hope you find this control useful.
Good Luck