December 2013 - 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
  • 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

2013-12-27

ASP.NET Viewstate And Controlstate Performance Enhancements - Saving Viewstate And Controlstate On Server

ASP.NET Viewstate & Controlstate

Viewstate and Controlstate are used in ASP.NET pages to keep states of pages and all controls on them between postbacks. ASP.NET framework saves these states in a container to be used throughout postbacks to maintain these states.

Also, you can explicitly add any info you need to keep between postbacks inside the page viewstate. This may seem urging but you must know that this doesn't come free of charge. Yes viewstate and controlstate are very useful and powerful but they have to be used wisely otherwise you will greatly affect your system performance badly and in an unpleasant way.

The viewstate and controlstate are both saved by the server and then retrieved to keep your page and controls state. The default behavior is that these states are saved into a hidden field on the page so that at postbacks the server will be able to read these states back from the hidden field and retrieve the sates prior the postbacks. Is this good?

This article is not meant to be a full guide about viewstate and controlstate, if you need a full guide you can have a look on the references at the end of this article. So, what is this article about?

This article will focus on how to overcome the drawbacks of heavy viewstate and controlstate and enhance the system performance by saving these states on server rather than sending them back and forth between client and server throughout postbacks.

Analysis
To know how viewstate and controlstate work, you need to know some points in brief:
  1. HTTP is stateless which means that it doesn't support by itself saving the states of requests and responses. That is why each web development platform should handle the states by its own way and system when needed
  2. For ASP.NET, when a request is initiated, the server process the request and builds the whole page and sends it back to the client. At this moment, the server forgets about the whole page object and all info related to the request. This is what is meant by stateless
  3. ASP.NET has its own way of saving the page states. It exposes some methods/events by which you can control how the page state will be saved and then retrieved, but if you didn't override these methods and provide your own implementation, there is always a default behavior which ASP.NET will use to save the states
  4. The default behavior for ASP.NET to save page states is saving/loading them into/from a hidden field on the page
  5. The states the server tends to save are the states of all the page controls before being sent back to the client. At the successive requests, the server can now retrieve these sates to know how the page looked like before the system user applied some changes on it at the client side 
But why the hassle?
I asked myself before why at every request the server needs to know the states on which the page was before the last response, does it really matter? As far as I know when a request is performed the form will be submitted to the server and the server will have all the info required to re-create and re-populate the form fields in the response, so for God's sake why????

Misunderstood about viewstate
Some developers think that viewstate is used to keep values and states of page controls so that the server is able to populate these values and states after postbacks. This is wrong. Believe me even if you disabled the viewstate on a page and its controls the values you entered in the controls will still exist after postbacks. You don't believe me, try it yourself.

Create a web application, add a page and disable viewstate on it, add a server textbox control and make it run at server, add a server button control and make it run at server. Now start the application and enter some text inside the textbox and click on the button. A postback will be performed and the textbox will be populated with the text you enterd yourself before the postback. How? this happened because when you clicked the button the whole form is submitted including the text you entered inside the textbox. So, the server didn't need anything to know the value you entered as it was already sent to it with the request. That's why I told you before viewstate is not responsible for keeping controls values and states.

Believe it or not, one of the main purposes of the viewstate is to track changes made on a page controls. Why keep track of changes? to be able to properly fire events like "ontextchanged" which are based on tracking changes made on a control to properly apply your custom code for handling such situations. Still not convinced? If yes, try the following example.

The proof
Try this:
  1. Create a web application
  2. Add a page and enable viewstate on it
  3. Add the following markup inside the form tag
    <asp:TextBox ID="box" runat="server" Text="" EnableViewState="true" ontextchanged="box_TextChanged"></asp:TextBox>
    <asp:Button ID="btn" runat="server" Text="Do Postback" onclick="btn_Click" />
    
  4. Write this code on the code behind inside the page class
    protected void btn_Click(object sender, EventArgs e)
    {
    }
    
    protected void box_TextChanged(object sender, EventArgs e)
    {
    }
    
  5. Put a breakpoint on the "box_TextChanged" event
  6. Run the application in debug mode
  7. Write "Test Test" in the textbox
  8. Click "Do Postback" button
  9. You will reach the breakpoint, hit F5 to return to client-side
  10. Delete "Test Test" from the textbox and leave it empty
  11. Click "Do Postback" button
  12. You will reach the breakpoint, hit F5 to return to client-side
  13. Stop debugging and disable the viewstate on the page
  14. Disable the viewstate on the textbox so that the markup will be as follows
    <asp:TextBox ID="box" runat="server" Text="" EnableViewState="false" ontextchanged="box_TextChanged"></asp:TextBox>
    <asp:Button ID="btn" runat="server" Text="Do Postback" onclick="btn_Click" />
    
  15. Put a breakpoint on the "box_TextChanged" event
  16. Run the application in debug mode
  17. Write "Test Test" in the textbox
  18. Click "Do Postback" button
  19. You will reach the breakpoint, hit F5 to return to client-side
  20. Notice that the textbox text is "Test Test", even without viewstate!!!
  21. Delete "Test Test" from the textbox and leave it empty
  22. Click "Do Postback" button
  23. You will not reach the breakpoint, even when the text is changed from "Test Test" to ""!!!
Confused? You have the right to. Here is what happened:
  1. When you created the textbox using the markup, the default value of the textbox is empty or ""
  2. At the first load of the page, the server loaded the textbox with its default value which was set into the markup which was "" in our case
  3. Since this was the first page load, the server already knew that whatever viewstate was enabled or disabled it would not matter as the page was in its default state
  4. At client-side, when you entered "Test Test" inside the textbox then followed by postback the server created the whole page and its controls from scratch
  5. So, the first step was to create the textbox and pre-populate it with its default value which is "" in our case
  6. At this point the viewstate may have played a role, so:
    1. When viewstate was enabled:
      1. The server checked if any viewstate was saved from before
      2. In this case, no viewstate was saved because the previous load was the first page load as we stated above in step #3
      3. So, the textbox text was not changed and it stayed ""
    2. When viewstate was disabled:
      1. The textbox text was not changed and it stayed ""
  7. Server loaded the new textbox text from the submitted form, so in our case it was found to be "Test Test"
  8. Server set the textbox text to the value retrieved in the previous step which is "Test Test"
  9. Server compared the textbox value from step #6 and #8 and figured out that the value has changed from "" to "Test Test", so the server fired the "box_TextChanged" event
  10. Before rendering the page, the server had something to do:
    1. When viewstate was enabled:
      1. The server saved the viewsate of the page controls, so the textbox state was saved and the saved value of the textbox was "Test Test"
    2. When viewstate was disabled:
      1. No state was saved
  11. Back again at client side, when you cleared the textbox text and performed a postback, the server created the whole page and its controls from scratch
  12. So, the first step was to create the textbox and pre-populate it with its default value (from the markup) which is "" in our case
  13. At this point the viewstate may have played a role, so:
    1. When viewstate was enabled:
      1. The server checked if any viewstate was saved from before
      2. In this case, viewstate was found and the saved texbox value was "Test Test"
      3. So, the server re-populated the textbox with its previous value which was saved in the viewstate, in our case, "Test Test""
    2. When viewstate was disabled:
      1. The textbox text was not changed and it stayed ""
  14. Server loaded the new textbox text from the submitted form, so in our case it was found to be ""
  15. Server set the textbox text to the value retrieved in the previous step which is ""
  16. Server compared the textbox value from step #13 and #15 to check if any changes had been applied on the textbox, so:
    1. When viewstate was enabled:
      1. A change had been applied from "Test Test" to ""
      2. The server fired the "box_TextChanged" event
    2. When viewstate was disabled:
      1. No change had been applied as both values are ""
      2. The server didn't fire the "box_TextChanged" event
That's it, I think you now got it right, right?

The last thing to mention here is

ASP.NET Page Lifecycle

Why to save viewstate and controlstate on server?
Now after we have understood what viewstate and controlstate are about, let's discuss something. We said before that ASP.NET has a default way or approach to save and load viewstate and controlstate if not other approach is set by the system developer. This default approach is saving and loading states into and from a hidden field on the page. Is this good? may be it is good for some cases but if your page controls are complex or many or you are stuffing too many objects into the viewstate, this will make the viewstate and controlstate large in size and in this case the hidden field will take too much size on the page. This will eventually cause the response size to be large. I think this is enough for a reason on why to try to find another approach for saving and loading states.

How to save viewstate and controlstate on server?
To control the way ASP.NET will save and load your page states, you need to override two events on the page class but before going deep into code let's highlight some points first.

The whole idea here is to save the states on a text file on the server. This way the server will not have to send the states back and forth between server and client which makes the request and response sizes smaller and the whole application performance better.

So, for this approach to work as it should, a state file should be created for every user so that users will not share states. This could be handled using session ids as the file names or something like that. This will work because we know that session ids are unique for all users and it is impossible for two users to have the same session id.

Problem
This is good but there is a problem with this approach. We said that session ids are unique for all users and that every user will have his own unique session id, but, for the same user, if he opens more than one page of the application on more than one tab, all these pages and tabs will share the same session id. So, now we have to differentiate between the states of pages even for the same user because we don't want to load the states of page A to page B.

Solution
We have to define an id for each page a user opens, so that the combination of this id with the user session id form a unique page id. This combined id will be used as the state file id. To do this, we will generate a unique page id at the page first load and save this id on a hidden field on the page.

Building the whole solution
As we said before, there are two events to override on the page class:
  1. The "SavePageStateToPersistenceMedium" event which is fired when the server saves the page states before the page is rendered
  2. The "LoadPageStateFromPersistenceMedium" event which is fired when the server loads the saved states from the previous response
So, we will create our own class derived from the "Page" class and customize it to look as in the code below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using DevelopmentSimplyPut;
using System.IO;
using System.Web.UI.WebControls;

namespace DevelopmentSimplyPut.CustomStatePreservePages
{
    public class InFileStatePreservePage : Page
    {
        private string pageId;
        public string PageId
        {
            get
            {
                string result = "";

                if (!string.IsNullOrEmpty(pageId))
                {
                    result = pageId;
                }
                else
                {
                    result = Request.Form["hdnPageId"];
                }

                return result;
            }
        }

        public string StatePreserveFilesFolderPath
        {
            get
            {
                return Path.Combine(Request.PhysicalApplicationPath, Constants.StatePreserveFilesFolderName);
            }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if(!IsPostBack)
            {
                pageId = Session.SessionID.ToString() + Guid.NewGuid().ToString();
                Page.ClientScript.RegisterHiddenField("hdnPageId", pageId);
            }
        }

        protected override object LoadPageStateFromPersistenceMedium()
        {
            if (Page.Session != null)
            {
                if (Page.IsPostBack)
                {
                    string filePath = Session[PageId].ToString();

                    if (!string.IsNullOrEmpty(filePath))
                    {
                        if (!File.Exists(filePath))
                        {
                            return null;
                        }
                        else
                        {
                            StreamReader sr = File.OpenText(filePath);
                            string viewStateString = sr.ReadToEnd();
                            sr.Close();

                            try
                            {
                                File.Delete(filePath);
                            }
                            catch
                            {

                            }

                            LosFormatter los = new LosFormatter();
                            return los.Deserialize(viewStateString);
                        }
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        protected override void SavePageStateToPersistenceMedium(object state)
        {
            if (state != null)
            {
                if (Page.Session != null)
                {
                    if (!Directory.Exists(StatePreserveFilesFolderPath))
                    {
                        Directory.CreateDirectory(StatePreserveFilesFolderPath);
                    }

                    string fileName = Session.SessionID.ToString() + "-" + DateTime.Now.Ticks.ToString() + ".vs";
                    string filePath = Path.Combine(StatePreserveFilesFolderPath, fileName);

                    Session[PageId] = filePath;

                    LosFormatter los = new LosFormatter();
                    StringWriter sw = new StringWriter();
                    los.Serialize(sw, state);

                    StreamWriter w = File.CreateText(filePath);
                    w.Write(sw.ToString());
                    w.Close();
                    sw.Close();
                }
            }
        }
    }
}

And now any page you create in the system should inherit from the "InFileStatePreservePage" class and in the "Page_Load" event call the base first as in the code below.
public partial class MyPage : InFileStatePreservePage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        base.Page_Load(sender, e);
    }
}

Deleting the abandoned state files
To delete the remaining state files, you need to make sure that the files you are going to delete are the outdated files only. To do that you should delete only the files that are not modified for a period greater than the session timeout period. So, to do that you can add the code below to your Global.asax file.
void Application_Start(object sender, EventArgs e) 
{
 string stateFilesDirectory = System.IO.Path.Combine(Server.MapPath("~"), DevelopmentSimplyPut.Constants.StatePreserveFilesFolderName);
 Application["stateFilesDirectory"] = stateFilesDirectory;
 
 if (!string.IsNullOrEmpty(stateFilesDirectory) && System.IO.Directory.Exists(stateFilesDirectory))
 {
  string[] files = System.IO.Directory.GetFiles(stateFilesDirectory);
  foreach (string file in files)
  {
   System.IO.FileInfo fi = new System.IO.FileInfo(file);
   fi.Delete();
  }
 }
}

void Application_End(object sender, EventArgs e) 
{
 string stateFilesDirectory = Application["stateFilesDirectory"].ToString();

 if (!string.IsNullOrEmpty(stateFilesDirectory) && System.IO.Directory.Exists(stateFilesDirectory))
 {
  string[] files = System.IO.Directory.GetFiles(stateFilesDirectory);
  foreach (string file in files)
  {
   System.IO.FileInfo fi = new System.IO.FileInfo(file);
   fi.Delete();
  }
 }
}

void Session_Start(object sender, EventArgs e) 
{
 string stateFilesDirectory = Application["stateFilesDirectory"].ToString();

 if (!string.IsNullOrEmpty(stateFilesDirectory) && System.IO.Directory.Exists(stateFilesDirectory))
 {
  string[] files = System.IO.Directory.GetFiles(stateFilesDirectory);
  int timeoutInMinutes = Session.Timeout;
  int bufferMinutes = 5;
  foreach (string file in files)
  {
   System.IO.FileInfo fi = new System.IO.FileInfo(file);
   if (fi.LastAccessTime < DateTime.Now.AddMinutes((-1 * (timeoutInMinutes + bufferMinutes))))
   {
    fi.Delete();
   }
  }
 }
}

void Session_End(object sender, EventArgs e)
{
 string stateFilesDirectory = Application["stateFilesDirectory"].ToString();
 
 if (!string.IsNullOrEmpty(stateFilesDirectory) && System.IO.Directory.Exists(stateFilesDirectory))
 {
  string[] files = System.IO.Directory.GetFiles(stateFilesDirectory);
  int timeoutInMinutes = Session.Timeout;
  int bufferMinutes = 5;
  foreach (string file in files)
  {
   System.IO.FileInfo fi = new System.IO.FileInfo(file);
   if (fi.LastAccessTime < DateTime.Now.AddMinutes((-1 * (timeoutInMinutes + bufferMinutes))))
   {
    fi.Delete();
   }
  }
 }
}

Important prerequisite
For this solution to work well you need to set your application session timeout mode to Inproc. Otherwise, the session "Session_Start" and "Session_End" events will not fire and in this case the only time you will be clearing the abandoned state files will be at "Application_Start" and "Application_End" events which is a way too late and may cause the server to have low disk space. So, to do so you have to set your application web.config file as below.
<configuration>
    <system.web>
     <sessionState cookieless="UseCookies" mode="InProc" timeout="60"/>
    </system.web>
</configuration>


That's it, hope you can find this helpful someday. For further reading you can check the resources below.
Good luck.


References
  1. TRULY Understanding ViewState - Infinities Loop 
  2. ViewState in SQL
  3. ViewState Compression - CodeProject
  4. Understanding ASP.NET View State
  5. Flesk.NET Components - Viewstate Optimizer
  6. ViewState: Various ways to reduce performance overhead - CodeProject
  7. Keep ASP.NET ViewState out of ASPX Page for Performance Improvement - CodeProject
  8. Control State vs. View State Example

2013-12-22

How To Avoid Problems Caused By Clients' Browser Cached Resource Files (JS, CSS, ....) With Every New Build


Javascript & Css

Browsers like IE, Firefox, Chrome and others have their own way to decide if a file should be cached or not. If a file link (URL) is requested more than a certain number of times the browser decides to cache this file to avoid repeated requests and their relevant responses. So, after a file is cached by the browser and a new request is performed for this file, the browser responses with the cached version of the file instead of retrieving the file for the server.

But how does the browser know if we are requesting the same file? The browser knows that by comparing the URL of the requested file to the URL of the file it had already cached before. This means that any slight change on the requested file URL will be recognized by the browser as a completely new file.

So, what is the problem?
The problem is that sometimes between builds there are some changes applied on the application javascript and style files. Although the new code is sent to the client, they get javascript errors and some of the styles are messy. Why? this is due to the client's browser caching of the javascript and styles files. Although we replaced the old files with the new ones but the client's browser is still using the old cached ones because the URLs are still the same.

What is the solution?
There are many approaches to take to fix this issue but they are not all proper ones. Let's check some of these solutions.

Some of the solutions are:
  1. Ask the client to ask all of his system users to clear the browser cache
  2. Ask the client to ask all of his system users to disable browser caching
  3. For each build rename JS and CSS file names
  4. For each build add a dummy query string to all resources URLs
Now, let's see. I think we will all agree that the first two options are not practical at all. For the third option, it will work for sure but this is not acceptable as changing the files names will require changing all references to these files in all application pages and code which is dangerous and not acceptable by any means in terms of code maintainability.

This leaves us with the fourth option which seems like the third one but believe me they are not completely the same. For sure I don't mean to do it in a manual form like browsing through the whole code and changing the extra dummy query string for all resources URLs, there is a more generic and respectable way to do it without even caring about re-visiting the URLs for each new build.

The solution is to implement a server control to be used to register the resources instead of using the regular script and link tags. This control will be responsible for generating the URLs with the dummy query strings and making sure these query strings are not changed unless a new build is deployed.

Now, let's see some code.

Server Control:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Globalization;
using System.Web.UI.WebControls;

namespace DevelopmentSimplyPut.CustomWebControls
{
    public class VersionedResourceRegisterer : WebControl, INamingContainer
    {
        JsTags js;
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public JsTags JS
        {
            get
            {
                return js;
            }
        }

        CssTags css;
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public CssTags CSS
        {
            get
            {
                return css;
            }
        }

        public VersionedResourceRegisterer()
        {
            js = new JsTags();
            css = new CssTags();
        }

        protected override void Render(System.Web.UI.HtmlTextWriter output)
        {
            string fullTag = "";
            string version = AppConstants.Version;

            if (null != JS && JS.Count > 0)
            {
                foreach (Tag js in JS)
                {
                    string path = js.path;
                    path = GetAbsolutePath(path);

                    if (!string.IsNullOrEmpty(path))
                    {
                        fullTag += string.Format(CultureInfo.InvariantCulture, "<script src=\"{0}?v={1}\" type=\"text/javascript\"></script>", path, version); 
                    }
                }
            }

            if (null != CSS && CSS.Count > 0)
            {
                foreach (Tag css in CSS)
                {
                    string path = css.path;
                    path = GetAbsolutePath(path);

                    if (!string.IsNullOrEmpty(path))
                    {
                        fullTag += string.Format(CultureInfo.InvariantCulture, "<link href=\"{0}?v={1}\" type=\"text/css\" rel=\"stylesheet\" />", path, version);
                    }
                }
            }

            output.Write(fullTag);
        }

        private string GetAbsolutePath(string path)
        {
            string result = path;

            if(!string.IsNullOrEmpty(path))
            {
                if (!path.Contains("://"))
                {
                    if (path.StartsWith("~"))
                    {
                        HttpRequest req = HttpContext.Current.Request;
                        string applicationPath = req.Url.Scheme + "://" + req.Url.Authority + req.ApplicationPath;

                        if(!applicationPath.EndsWith("/"))
                        {
                            applicationPath += "/";
                        }

                        path = path.Replace("~", "").Replace("//", "/");

                        if (path.StartsWith("/"))
                        {
                            if (path.Length > 1)
                            {
                                path = path.Substring(1, path.Length - 1);
                            }
                            else
                            {
                                path = "";
                            }
                        }

                        result = applicationPath + path;
                    }
                }
            }

            return result;
        }
    }

    public class Tag
    {
        public string path { set; get; }
    }

    public class JsTags : List<Tag>
    {
    }

    public class CssTags : List<Tag>
    {
    }
}

Version Generation:
public static class AppConstants
{
    private static string version;
    public static string Version
    {
        get
        {
            return version;
        }
    }

    static AppConstants()
    {
        version = (Guid.NewGuid()).ToString().HtmlEncode();
    }
}
As you see the AppConstants class is a static class and inside its static constructor the version is generated once. This means that with each IIS reset a new version will be generated and accordingly with each build we get a new version.

Using Control On Pages:
<ucVersionedResourceRegisterer:VersionedResourceRegisterer runat="server">
 <JS>
  <ucVersionedResourceRegisterer:Tag path="Scripts/jquery-1.10.2.min.js" />
  <ucVersionedResourceRegisterer:Tag path="Scripts/jquery-migrate-1.2.1.min.js" />
  <ucVersionedResourceRegisterer:Tag path="Scripts/jquery.alerts.min.js" />
 </JS>
 <CSS>
  <ucVersionedResourceRegisterer:Tag path="Styles/jquery.alerts.css" />
 </CSS>
</ucVersionedResourceRegisterer:VersionedResourceRegisterer>

Finally, this is not the only advantage of using the server control as you can always use it to gain more control on your resource files. One of the tasks in which I made use of this control is applying automatic minification and bundling of my resource files to enhance my application performance.

That's it. Hope you find this post helpful someday.
Good luck.


2013-12-21

Application To Generate Combined Images Of All Image-Categories Possible Combinations

[Update] The application is now updated to avoid "out of memory" exceptions.


On my last project I was working on a tree control which represents system business objects into hierarchical form. Each object has a workflow to go through and for each step in this workflow the object state changes.

So, one of the requirements was to add images beside each tree node for the system user to know the status of each tree node easily with the need to open a properties window or something like that.

This is easy but we have some points to clarify first:
  • Each system objects has three categories of status
    1. Cat01: is so business related so I will not go into details about it but let's just say that this type of object status should have one of 25 possibilities
    2. Cat02: is related to the type of change applied on the system object (added, edited, deleted, unchanged)
    3. Cat03: is also so business related but it is mainly about object change approval status. This object status should have one of 7 possibilities
  • For each image tag added on the page there will be a request to the server and for sure its related response

So, after some thinking we came up with 2 approaches to choose from:
  1. Add three image tags beside each tree node and fill these tags with appropriate images according to the status of each object
  2. Add one image tag beside each tree node and fill this tag with only one image which is all the object 3 status images combined into one image. The specific image name will be built by concatenating the keywords for each category status. This way we don't have to switch case or detect each combination from real object status, just concatenate the keywords and voila we have the right image name

After doing the math, and it was so simple, we decided to go with the second approach to eliminate the extra requests the would be sent for each tree node to get all the three status images, so instead of 3 requests it would be only 1.

So, now comes the hard part. We need to generate the combined images. We could have wrote some code to generate the combined image of each tree node on the fly at run-time but we thought that this would not be the best decision as the status images are some static images which we already have at design time. So, there was no reason to go with that much run-time processing for each node when we could already generate all the possible images combinations we need.

That's why we decided to generate all the possible combinations that we could have for each system object status. Someone said that this would be too much images but we replied that it is not a problem as these images will be static and at the end of the days we are talking about a few mega bytes of static hard desk space not RAM.

We thought that doing the combination thing manually would be a shame and actually impossible as we have 25 x 4 x 7 = 700 combinations which means 700 images. Doing all of this work manually is so bad by all means especially when at some point the client decides to replace an image with another one or add a new image.

That's why I wrote a simple windows application which does all the hard work. You just give it each category images and in just seconds you get all your images.

Hint: the code related to generating all the possible combinations is built using the "Possibilities Cube" library I had posted before. If you are interested to read about it you can find it on Possibilities Cube Library - A Library Smart Enough To Calculate All Possibilities With Logical Conditions

Now, let's see some screenshots for the windows application.


Images for the first category (Cat01)

Images for the second category (Cat02)

Images for the third category (Cat03)

Adding images for each category on the application

Finally, the generated images

As you can see it is so easy to use the application and you can apply any modifications on the code to go with your specific business needs. Currently the code is set to export images into png format but you can change it as you wish in the code.

I didn't put much care into the application UI, graphics and so on as it is only for indoor usage to serve a certain need not to be a standalone product or anything like that so don't be turned off by the UI as at the end of the day it may help you save much time and effort.

Finally, you can download the application from here


Wish you will find this useful.
Good luck.


2013-12-20

Forcing ASP.NET Control To Go Through Its Full Life Cycle On Static Methods

A few days ago I needed to use a custom control which I made as a template to generate some HTML and return it as a response for an Ajax call, but, I faced a problem which is that my WebMethod is a static method which cannot access the control instance i dropped on the same page including the WebMethosd on code behind.

To make it more clear, here was the situation. I was working on an ASP.NET tree control which represents some system objects into hierarchical form. When testing the tree on few objects everything went fine, but with increasing the number of objects the tree became heavier and its response was not that appealing.

This made me think to take another approach which is using Ajax calls to hit the database and get the only objects I need per situation and finally attach/bind/update the tree with these objects. This will save me costy round trips back and forth to the server through postbacks and for sure the heavy load of the tree HTML with each request and response.

First, the idea was ideal but then came the difficulties. The tree control I was using is a third party control. So, I didn't actually have the code and pattern on which the nodes were built, so this made me think, how would I write the full HTML of the new nodes I got from the Ajax calls?

After short thinking, an idea hit me...... why don't I use the same tree control to draw me the single nodes I need, so whenever an Ajax call is performed a new instance of the tree control on another page will return me the HTML of the new nodes and then I will just update my main tree control with the new HTML. Luckily, I found that the tree control I was using had a method which would return me the generated HTML by the tree, so I cheered up and said out loud "Found it".

For the second time, this was a promising idea but then came the troubles. I had already implemented a static WebMethod on a page to call by Ajax. This WebMethod should be responsible for getting me the new HTML. So, I created an instance of the tree control inside this method, bound the tree to the new object, called the tree method which returns the generated HTML, but my code blown up with an exception.

After debugging I found that the tree method I was calling to get the generated HTML thrown some serious exceptions as the tree control instance I created inside my WebMethod is not actually fully loaded because the control didn't go through its whole life cycle.

This made me feel bad as the other alternatives are nightmares. So, I tried to search for any way by which I can force a control to go through its life cycle inside a static method. Finally, I found the solution and that is what I am going to show you here.

First, you have to know that the "Page" class has some validation which forces you to add a "Form" tag or you will get a serious exception. Since we will need on our solution to add the control we wish to load inside a static method on a page instance, then it would be nice to know this first.

To create a Page and bypass the Form tag existence we need to override the "VerifyRenderingInServerForm" method on the "Page" class. So, we will create a new class inherited from the "Page" class and override the "VerifyRenderingInServerForm" method as shown below.
public class PageWithoutForm : Page
{
    public override void VerifyRenderingInServerForm(Control control)
    {
    }
}

Now, we get to the last part which is forcing the control to go through its life cycle. This can be done as in the code below.
[WebMethod]
public static string LoadControlInStaticMethod()
{
 CustomControl ctrl = new CustomControl();

 var page = new PageWithoutForm();
 page.Controls.Add(ctrl);
 StringWriter writer = new StringWriter();
 HttpContext.Current.Server.Execute(page, writer, false);
 
 ctrl.CustomProperty = "SomeValue";
 ctrl.FireSomeAction();
}

If you have your control in "ascx" form, you can do the same as in the code above but with just updating the line where you defined the control instance as in the code below.
[WebMethod]
public static string LoadControlInStaticMethod()
{
 CustomControl ctrl = (CustomControl)LoadControl("~/SomeFolder/CustomControl.ascx");

 var page = new PageWithoutForm();
 page.Controls.Add(ctrl);
 StringWriter writer = new StringWriter();
 HttpContext.Current.Server.Execute(page, writer, false);
 
 ctrl.CustomProperty = "SomeValue";
 ctrl.FireSomeAction();
}


That is it all. I hope this will help someone one day.
Good luck.