in

ASPXWizard.net

.net and Ajax Community

Mina Labib

  • ASP.NET MVC handle exceptions

    /* You can also find more posts on my technical blog, and follow me at twitter @minalabib */

    ASP.NET MVC has a cool and neat way to catch and handle exceptions, I will show here a quick review for handling exceptions in MVC framework and I will present small addition I added too.

    The main idea here is all processes and work flow should go through a Controller and an Action method inside that controller, so if any exception or error happened in any step of the work flow (DAl, BL, action, model, server side validation, etc … ) it should end up at the Controller/Action level, ASP.NET MVC provide a nice technique with OnException even handler which can be overridden in your controller, and thus all exception that is bubbled up to the action will be caught and handled in this method.

    Ok, enough talk let’s code, I have a base controller that all my controllers inherit from and I believe it good practice:

    public class ApplicationControllerBase : Controller

     

    and inside that base controller I override the OnException event handler

    protected override void OnException(ExceptionContext filterContext)
    {
        base.OnException(filterContext);
        // TODO: Handle exception
        // Log in file or database for example
       
    ArrayList TempArrayList = new ArrayList();
        TempArrayList.AddRange(ConfigurationSettings.AppSettings["ToBeViewed"].Split(','));
        // to pass a toggle that tells
        // the view to show the exception message or not.
       
    if (TempArrayList.Contains(filterContext.Exception.GetType().Name))
            filterContext.Exception.Data.Add("IsViewable", true);
        else
           
    filterContext.Exception.Data.Add("IsViewable", false);
         // to tell .NET framework the exception is
       
    // handled and does not show yellow exception page.
       filterContext.ExceptionHandled = true;
        // to direct to the error view,
        // passing the exception as a model.
       
    this.View("Error", filterContext.Exception).ExecuteResult(this.ControllerContext);
    }

    In the method we should handle exception according to our needs (log the exception for example), and then tells the framework that the exception is handled and not to show the ugly yellow exception page of .NET by using (filterContext.ExceptionHandled = true; ).

    Also I built a comma separated list of exception names in the web.config file, and if the caught exception is one of these specified exceptions, system will send a flag to the error page to show the exception message or not.

    And then the context will redirect to the error view (by default in MVC this page is generated Views/Shared/Error.aspx), I edited this view to accept System.Exception as model and view the error message according to toggle preset in the exception object:

    <%@PageLanguage="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<System.Exception>"%>
    <asp:ContentID="errorTitle"ContentPlaceHolderID="TitleContent"runat="server">
      
    Error Page      
    </asp:Content>
    <
    asp:ContentID="errorContent"ContentPlaceHolderID="MainContent"runat="server">
        <
    h2>
       Sorry, an error occurred while processing your request.
     
        </h2>
      
    <% if ((bool)Model.Data["IsViewable"]){ %>
        <div>
          
    <%=Model.Message %>
        </div>
      
    <% } %>
    </asp:Content>

    Thanks for this post for help in that.
  • Adding double quote in NAnt task

    I was struggling this morning with issue in simple NAnt script, I was trying to uses <exec> task to call msbuild tool.

    Ok quick brief on the issue, NAnt 0.86 Beta 1 does not able to run its " href="http://nant.sourceforge.net/release/0.86-beta1/help/tasks/solution.html" target=_blank>" href="http://nant.sourceforge.net/release/0.86-beta1/help/tasks/solution.html" target=_blank><solution> task to build Microsoft Visual Studio solutions against Visual Studio 2008 solution files, however it can build solution for .NET 3.5 Framework. so I found two solutions –till now-, both of them are using MSBuild tool to build my solution with two different ways; first one is to use NAntContrib as extension for NAnt and use its " href="http://nantcontrib.sourceforge.net/release/0.85-rc4/help/tasks/msbuild.html" target=_blank>" href="http://nantcontrib.sourceforge.net/release/0.85-rc4/help/tasks/msbuild.html" target=_blank><msbuild> task to fire msbuild tool – I did not try that yet –, and the other one is to use NAnt " href="http://nant.sourceforge.net/release/latest/help/tasks/exec.html" target=_blank>" href="http://nant.sourceforge.net/release/latest/help/tasks/exec.html" target=_blank><exec> task to call msbuild tool and throw to it my solution file as parameter; and while I am try that I found issue, I need to add /p:Platform=”Any CPU” as msbuild command switch but how to add double quote to task attributes, it fails if I wrote:

    <exec program="${framework::get-framework-directory(framework::get-target-framework())}\msbuild.exe" commandline="mysolution.sln /p:Platform=”Any CPU”"/>

    Fortunately, I found nice simple post to solve this issue by adding property to NAnt script with double quote as value and use it in my command as following:

    <property name="dbl_quote" value='"'/>
    <exec program="${framework::get-framework-directory(framework::get-target-framework())}\msbuild.exe" commandline="mysolution.sln /p:Platform=”${dbl_quote}Any CPU${dbl_quote}"/>
    It is nice and quick solution.
    Posted Dec 18 2009, 04:49 AM by minalabib with no comments
    Filed under: ,
  • ASP.NET MVC 2 RC

    About month ago the Beta version was released, and now ASP.NET MVC team is announcing to day that ASP.NET MVC 2 RC is out there and that release candidate is ready to be downloaded and play around with.

    Mainly it is focused in bug fixing, improving current features, and performance tuning and you can as usual read the release notes for more details. Mainly the focus in Beta and RC releases are in the validation, validation localization, HTML.RenderAction and HTML.Action, you will find more details in this post.

    This version is for Visual Studio 2008 and should come in the RTM of Visual studio 2010 which should come out sometime in next March.

  • Steps to enable NAnt intellisense in Visual Studio

    NAnt is helpful, open source, and free tool for automated .NET-build process, same as Ant but it is targeting .NET frameworks rather than Java, and according to Wikipedia the name NAnt is coming from Not Ant :)

    NAnt allows you to build a script (XML Script) configuring tasks and dependencies with reach NAnt functions, expressions, data types etc…

    Alright, going back to this post purpose; here you are the steps to allow NAnt intellisense in VS 2008 (I believe for other VS versions too) in Windows operating systems.

    • Download Nant binaries or source code from here, I will go with latest one NAnt 0.86 Beta 1.
    • Extract the compressed folder and follow NAnt installation steps as explained here.
    • Copy nant.xsd file from extracted folder (<Base installation directory>\schema\nant.xsd)
    • Paste this file into XML schema folder in Visual Studio installation directory; it should be as following in x32 bit operating systems (C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas) or (C:\Program Files (x86)\Microsoft Visual Studio 9.0\Xml\Schemas) in x64 bit operating systems.
      You should be done now with those steps, do start new NAnt XML script and get use of Intellisense, create new XML file in VS 2008, and in the root node <project> add the attribute xmlns=” now you should see NAnt schema in the drop down list as bellow:
      NAnt01
      And now you have NAnt XML intellisense enabled in VS 2008 as bellow:
      NAnt02 
    Posted Dec 16 2009, 02:26 AM by minalabib with no comments
    Filed under: , ,
  • Extending Cache to Get Or Insert data

    In current project I was planning to build caching functionality to keep some shared and not frequently changed data, I figured out that I will be using a lot of Cache[string Key], Cache.Get(), and/or Cache.Insert() methods. and that means I have to check if the data is in the cache already and if not I have to insert the data to the cache object.

    I was thinking about having a method to check and update the cache without rewriting that over and over again, but that means I have to know what is the data type of the data needed to be stored, that brings us to Generics and Extensions to build extension methods to check the Cache object and get or insert data.

    I am using ASP.NET MVC 1.0 in this project, below is the extension method to get or insert into Cache object:

    using System;
    using System.Web.Caching;
    
    namespace CacheExtensions
    {
        public static class CasheExtension
        {
            public static T GetOrInsert<T>(this Cache Cache, string key, Func<T> generator)
            {
                var result = Cache[key];
                if (result != null)
                    return (T)result;
                result = (generator != null) ? generator() : default(T);
                Cache.Insert(key, result);
                return (T)result;
            }
        }
    }

    now you can consume it easily by calling the extended method as following:

    object o = HttpRuntime.Cache.GetOrInsert<object>("key1",GenObj);

    supposing GenObj is a pointer  to a method to generate an instance of type object.

    Actually I used the same technique to get or add data to the session object too.

    References: http://stackoverflow.com/questions/445050/how-can-i-cache-objects-in-asp-net-mvc

  • Enable JQuery Intellisense in Visual Studio 2008

    I was expecting that Visual studio will do the magic for me and enable Intellisense for JQuery javascript library automatically once I add the library and referenced it in my page – naive, right?- anyways it did not happen of course, so with a quick look to the internet and beloved Scott Guthrie blog I found that I need couple of steps to enable it; It is easy and quick steps so I liked to share it with other naive developers :).

    1. Install Visual Studio 2008 SP1.
      This service pack adds richer javascript intellisense support for Visual Studio 2008, you will find it here.
    2. Install Visual Studio 2008 Patch KB958502 to support “-vsdoc.js” Intellisense files
      This patch apply for VS2008 SP1 and VWD Express SP1, enables Visual Studio to detect “-vsdoc.js” files when javascript library is referenced and use it to enable Javascript Intellisense. This patch can be found here.
    3. Download the JQeury Visual Studio doc file.
      You can find this file with ASP.NET MVC package as it is included by default or you can find it in JQuery official web site as Visual Studio documentation.VS08 JQuery
    4. Save “vsdoc.js” file in the same directory
      Save the Visual Studio documentation file in the same directory of the JQuery library, and then you can reference the library in your page as below:
      <script src="../../Scripts/jquery-1.3.2.js" type="text/javascript"></script>
      start now to use the Javascript Intellisense feature.
      Important Trick:
      Sometimes we use partial views or user controls in our project and we do not to refer to Javascript library within it, we usually refer to it in Master page or the parent page; Visual Studio has no way to know this script is available in for the user control or the partial view, thus Javascript Intellisense will NOT be enabled in these files, to enable Javascript Intellisense in the partial/user control there is a useful trick by reference the library surrounded with If(false) statement which will never be rendered in the run time, but still provide Javascript intellisense in the design time.
      <% if (false)
         { %>
         <script src="../../Scripts/jquery-1.3.2.js" type="text/javascript"></script>
      <% } %>


    References:



  • Adding web reference in class library – ASP.NET MVC

     

    It is change in .NET 3.5 using ASP.NET MVC you can’t find ‘Add Web Reference’ option in right click context menu inside a class library (if you do not have web references in your class library).

    But I found how to add .NET 2.0 based web reference through adding Service Reference, I quoted the following sentence from a forum thread.

    As for the "Add Service Reference", it is used for creating client proxy of WCF service(it will generate a client proxy class). Also, since WCF include webservice communication ability, you can use it to create  client proxy for webservice too.

     

    Below are steps I used to add web reference to a web service in class library in my ASP.NET MVC solution.

    1. Right click ‘References’ folder under class library project (there is no Add Web Reference option) instead select ‘Add Service Refrence’
      1
    2. In Add Service Reference window click Advanced… button
      2
    3. in Service Reference Setting Window click Add Web Reference button
      3
    4. Now you are landing on regular .NET 2.0 Add Web Reference window and you can provide a web service URL to be added to your class library.

    by adding web reference and from now on you can see ‘Add Web Reference’ option in the right click context menu of Reference folder

    4

     

     

     

     

    and also Visual studio will create a Web References folder underneath your class library.



  • Generic editable GridView - ASP.NET MVC

    The goal is to build editable data grid in a project uses ASP.NET MVC 1.0, it is know that ASP.NET MVC does not use controls with ( runat=”server” ) attribute, which means I can’t use regular DataGrid or GridView in my code. and HTMLHelper class in ASP.NET MVC does provide such HTML component.

    So, there are two solutions:

    1. Using extension to extend HTMLHelper functionality and build HTML using code
         1: public static class GridViewExt
         2:     {
         3:         public static string GridView(this HtmlHelper html, IEnumerable DataSource, object HTMLAtrributes)
         4:         {
         5:             string htmlAttributesString = ConvertToAtrributes.ConvertObjectToAttributeList(HTMLAtrributes).Trim();
         6:             StringBuilder resultBuilder = new StringBuilder();
         7:             resultBuilder.AppendFormat("<table{0}{1}>", string.IsNullOrEmpty(htmlAttributesString) ? "" : " ", htmlAttributesString).AppendLine();
         8:             IEnumerator sourceEnumerator = DataSource.GetEnumerator();
         9:             Type itemType;
        10:             if (sourceEnumerator.MoveNext() == false)
        11:             {
        12:                 return "";
        13:             }
        14:             else
        15:             {
        16:                 itemType = sourceEnumerator.Current.GetType();
        17:             }
        18:             PropertyInfo[] properties = itemType.GetProperties();
        19:             resultBuilder.AppendLine("\t<tr>");
        20:             foreach (PropertyInfo property in properties)
        21:             {
        22:                 resultBuilder.AppendFormat("\t\t<th>{0}</th>", property.Name).AppendLine();
        23:             }
        24:             resultBuilder.AppendLine("\t</tr>");
        25:             foreach (object item in DataSource)
        26:             {
        27:                 resultBuilder.AppendLine("\t<tr>");
        28:                 foreach (PropertyInfo property in properties)
        29:                 {
        30:                     resultBuilder.AppendFormat("\t\t<td>{0}</td>", property.GetValue(item, null)).AppendLine();
        31:                 }
        32:                 resultBuilder.AppendLine("\t</tr>");
        33:             }
        34:             resultBuilder.Append("</table>");
        35:             return resultBuilder.ToString();
        36:         }
        37:     }
        38: public class ConvertToAtrributes
        39:     {
        40:         public static string ConvertObjectToAttributeList(object value)
        41:         {
        42:             StringBuilder builder = new StringBuilder();
        43:             if (value != null)
        44:             {
        45:                 IDictionary<string, object> dictionary = value as IDictionary<string, object>;
        46:                 if (dictionary == null)
        47:                 {
        48:                     dictionary = new RouteValueDictionary(value);
        49:                 }
        50:                 string format = "{0}=\"{1}\" ";
        51:                 foreach (string str2 in dictionary.Keys)
        52:                 {
        53:                     object obj2 = dictionary[str2];
        54:                     if (dictionary[str2] is bool)
        55:                     {
        56:                         obj2 = dictionary[str2].ToString().ToLowerInvariant();
        57:                     }
        58:                     builder.AppendFormat(format, str2.Replace("_", "").ToLowerInvariant(), obj2);
        59:                 }
        60:             }
        61:             return builder.ToString();
        62:         }
        63:     }
             now you should be able to call to call HTMLHelper extended method inside your View
        1: <%= this.Html.GridView(Model.Products)%>
      but it is still not editable and to add editing functionality it will be so hard to write a lot of HTML code inside C# code and will not be easy to maintain or fix issues; so let’s consider the second option.
    2. Building User control and get advantage of writing HTML inside the user control HTML (one thing to remember using ASP.NET MVC no use for page life cycle you can get rid of that).
      Now let me build a user control ascx page using fantastic JQuery fetures 
         1: <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="GridView.ascx.cs" Inherits="Shared.UserControls.GridView" %>
         2: <%@ Import Namespace="System.Reflection" %>
         3: <%
         1: -- style code --
      %>
         4: <style type="text/css">
         5:     .gridView
         6:     {
         7:         border-collapse: collapse;
         8:     }
         9:     .gridView th, .gridView td
        10:     {
        11:         padding: 5px;
        12:     }
        13:     .gridView th
        14:     {
        15:         border-bottom: double 3px black;
        16:     }
        17:     .gridView td
        18:     {
        19:         border-bottom: solid 1px black;
        20:     }
        21:     .alternatingItem
        22:     {
        23:         background-color: lightgrey;
        24:     }
        25: </style>
        26: <%
         1: -- Javascripts code --
      %>
        27: <script src="../../Scripts/jquery-1.3.2.js" type="text/javascript"></script>
         1:  
         2: <%--<script src="../../Scripts/jquery-1.3.2-vsdoc.js" type="text/javascript">
         1: </script>--%>
         2: <script type="text/javascript">
         3:     var controller = '';
         4:     var SaveAction = '';
         5:     <% GetController(); GetSaveAction(); %>
         6:     function IsValidAction(ctrl, actn)
         7:     {
         8:         if(($.trim(ctrl) == '') || ($.trim(actn) == ''))
         9:             return false;
        10:         else
        11:             return true;
        12:     }
        13:     function SaveAllClick() {
        14:         //debugger;
        15:         if(!IsValidAction(controller,SaveAction))
        16:         {
        17:             alert("Error while saving data");
        18:             return;
        19:         }
        20:         var i = 0;
        21:         var inputs = new Array();
        22:         $("#BaseTable").find("input").each(function() {
        23:             inputsIdea = [this.id, this.value];
        24:             i++;
        25:         });
        26:  
        27:         var columnsCount = $("#BaseTable").find("th").length;
        28:         $.post("/"+controller+"/"+SaveAction, { inputs: inputs, columnsCount: columnsCount });
        29:         Border();
        30:         $("#ChangeFlag").text("");
        31:         $("#SaveAll").attr("disabled", "disabled");
        32:         alert("Saved");
        33:     }
        34:     var prevCell;
        35:     function Border(cell) {
        36:         if (prevCell != null) {
        37:             $(prevCell).attr("style", "border-style: none");
        38:         }
        39:         if (cell != null) {
        40:             var i = $(cell); //.find("input");
        41:             i.attr("style", "border-style: inset");
        42:         }
        43:         prevCell = cell;
        44:     }
        45:     function Unborder(cell) {
        46:         if (cell != null) {
        47:             var i = $(cell); //.find("input");
        48:             i.attr("style", "border-style: none");
        49:         }
        50:     }
        51:     function MarkChanges() {
        52:         $("#ChangeFlag").text("Page should be saved");
        53:         $("#SaveAll").removeAttr("disabled");
        54:     }
      </script>
        28: <%
         1: -- Show the Headers --
      %>
        29: <table class="gridView" id="BaseTable">
        30:     <thead>
        31:         <tr>
        32:             <%
         1:  foreach (PropertyInfo prop in this.Columns)
         2:                { 
      %>
        33:             <th>
        34:                 <%
         1: = prop.Name 
      %>
        35:             </th>
        36:             <%
         1:  } 
      %>
        37:         </tr>
        38:     </thead>
        39:     <%
         1: -- Show the Rows --
      %>
        40:     <tbody>
        41:         <%
         1:  foreach (object row in this.Rows)
         2:            { 
      %>
        42:         <tr class="<%= this.FlipCssClass( "item", "alternatingItem") %>">
        43:             <%
         1: -- Show Each Column --
      %>
        44:             <%
         1:  foreach (PropertyInfo prop in this.Columns)
         2:                { 
      %>
        45:             <td>
        46:                 <%
         1:  var typeCode = Type.GetTypeCode(prop.PropertyType); 
      %>
        47:                 <%
         1: var str_disabled = "";
         2:                   if (this.GetAtt(prop.Name))
         3:                       str_disabled = "disabled=\"disabled\"";
         4:                  
      %>
        48:                 <%
         1: -- String Columns --
      %>
        49:                 <%
         1:  if (typeCode == TypeCode.String)
         2:                    { 
      %>
        50:                 <input id="<%=prop.Name %>" type="text" style="border-style: none" value="<%= GetColumnValue(row, prop.Name)%>"
        51:                     onmousedown="Border(this)" onselect="Border(this)" onblur="Unborder(this)" onchange="MarkChanges()"
        52:                     &lt;%= str_disabled %&gt; />
        53:                 <%
         1:  } 
      %>
        54:                 <%
         1: -- DateTime Columns --
      %>
        55:                 <%
         1:  if (typeCode == TypeCode.DateTime)
         2:                    { 
      %>
        56:                 <input id="<%=prop.Name %>" type="text" style="border-style: none" value="<%= GetColumnValue(row, prop.Name, "{0:D}")%>"
        57:                     onmousedown="Border(this)" onselect="Border(this)" onblur="Unborder(this)" onchange="MarkChanges()"
        58:                     &lt;%= str_disabled %&gt; />
        59:                 <%
         1:  } 
      %>
        60:                 <%
         1: -- Decimal Columns --
      %>
        61:                 <%
         1:  if (typeCode == TypeCode.Decimal)
         2:                    { 
      %>
        62:                 <input id="<%=prop.Name %>" type="text" style="border-style: none" value="<%= GetColumnValue(row, prop.Name, "{0:f}") %>"
        63:                     onmousedown="Border(this)" onselect="Border(this)" onblur="Unborder(this)" onchange="MarkChanges()"
        64:                     &lt;%= str_disabled %&gt; />
        65:                 <%
         1:  } 
      %>
        66:                 <%
         1: -- Decimal Columns --
      %>
        67:                 <%
         1:  if (typeCode == TypeCode.Double)
         2:                    { 
      %>$
        68:                 <input id="<%=prop.Name %>" type="text" style="border-style: none" value="<%= GetColumnValue(row, prop.Name, "{0:f}") %>"
        69:                     onmousedown="Border(this)" onselect="Border(this)" onblur="Unborder(this)" onchange="MarkChanges()"
        70:                     &lt;%= str_disabled %&gt; />
        71:                 <%
         1:  } 
      %>
        72:                 <%
         1: -- Boolean Columns --
      %>
        73:                 <%
         1:  if (typeCode == TypeCode.Boolean)
         2:                    { 
      %>
        74:                 <%
         1:  if ((bool)(this.GetColumnValue(row, prop.Name)))
         2:                    { 
      %>
        75:                 <input type="checkbox" disabled="disabled" checked="checked" />
        76:                 <%
         1:  }
         2:                    else
         3:                    { 
      %>
        77:                 <input type="checkbox" disabled="disabled" />
        78:                 <%
         1:  } 
      %>
        79:                 <%
         1:  } 
      %>
        80:                 <%
         1: -- Integer Columns --
      %>
        81:                 <%
         1:  if (typeCode == TypeCode.Int32)
         2:                    { 
      %>
        82:                 <input id="<%=prop.Name %>" type="text" style="border-style: none" value="<%= GetColumnValue(row, prop.Name)%>"
        83:                     onmousedown="Border(this)" onselect="Border(this)" onblur="Unborder(this)" onchange="MarkChanges()"
        84:                     &lt;%= str_disabled %&gt; />
        85:                 <%
         1:  } 
      %>
        86:             </td>
        87:             <%
         1:  } 
      %>
        88:         </tr>
        89:         <%
         1:  } 
      %>
        90:     </tbody>
        91: </table>
        92: <p>
        93:     <input id="SaveAll" disabled="disabled" type="button" value="Save" onclick="SaveAllClick()" />
        94:     <span id="ChangeFlag"></span>
        95: </p>
      and building code behind code for this user control using last post idea of TypeDescriptor class to make the user control more generic accepting any type of collection and render editable properties according to custom attributes associated with it.
         1: using System;
         2: using System.Collections;
         3: using System.Collections.Generic;
         4: using System.Linq;
         5: using System.Web;
         6: using System.Web.UI;
         7: using System.Web.Mvc;
         8: using System.Reflection;
         9: using System.ComponentModel;
        10: using CustomAtrributes;
        11:  
        12: namespace Shared.UserControls
        13: {
        14:     public partial class GridView : System.Web.Mvc.ViewUserControl<IEnumerable>
        15:     {
        16:         protected string GetController()
        17:         {
        18:             
        19:             if (ViewContext.RouteData.Values["Controller"] != null)
        20:             {
        21:                 Response.Write("controller = '" + ViewContext.RouteData.Values["Controller"] as string + "';");
        22:                 return ViewContext.RouteData.Values["Controller"] as string;
        23:             }
        24:             else
        25:                 return string.Empty;
        26:         }
        27:         protected string GetSaveAction()
        28:         {
        29:             if (ViewData.Model.GetType().GetProperty("SaveAction") != null)
        30:             {
        31:                 Response.Write("SaveAction = '" + DataBinder.Eval(ViewData.Model, "SaveAction") as string + "';");
        32:                 return DataBinder.Eval(ViewData.Model, "SaveAction") as string;
        33:             }
        34:             else
        35:                 return string.Empty;
        36:         }
        37:         protected PropertyInfo[] Columns
        38:         {
        39:             
        40:             get 
        41:             {
        42:                 var e = ViewData.Model.GetEnumerator();
        43:                 e.MoveNext();
        44:                 object firstRow = e.Current;
        45:                 if (firstRow == null)
        46:                 {
        47:                     throw new Exception("No data passed to GridView User Control.");
        48:                 }
        49:                 return firstRow.GetType().GetProperties();
        50:             }
        51:         }
        52:         protected IEnumerable Rows
        53:         {
        54:             get { return ViewData.Model; }
        55:         }
        56:         protected object GetColumnValue(object row, string columnName)
        57:         {
        58:             return DataBinder.Eval(row, columnName);
        59:         }
        60:         protected object GetColumnValue(object row, string columnName, string format)
        61:         {
        62:             return DataBinder.Eval(row, columnName, format); 
        63:         }
        64:         bool flip = false;
        65:         protected string FlipCssClass(string className, string alternativeClassName)
        66:         {
        67:             flip = !flip;
        68:             return flip ? className : alternativeClassName;
        69:         }
        70:         
        71:         protected bool GetAtt(string Name)
        72:         {
        73:             
        74:             bool readonlyatt = false;
        75:             UIAttributes uiatt = TypeDescriptor.GetAttributes(Model).Cast<Attribute>().SingleOrDefault(a => a.GetType().Name == typeof(UIAttributes).Name) as UIAttributes;
        76:             if ((uiatt != null) && uiatt.IsReadOnly)
        77:                 return uiatt.IsReadOnly;
        78:             PropertyInfo propInfo = Model.GetType().GetProperty(Name);
        79:                 //(propInfo =>
        80:                     {
        81:                         PropertyDescriptor propDescriptor = TypeDescriptor.GetProperties(Model).Cast<PropertyDescriptor>().SingleOrDefault(p => propInfo.Name == p.Name);
        82:                         if (propDescriptor != null)
        83:                         {
        84:                             UIAttributes attrib = propDescriptor.Attributes.Cast<Attribute>().SingleOrDefault(p => p.GetType().Name == typeof(UIAttributes).Name) as UIAttributes;
        85:                             if (attrib != null)
        86:                             {
        87:                                 readonlyatt = attrib.IsReadOnly;
        88:                             }
        89:                         }
        90:                     }
        91:                     //);
        92:                     return readonlyatt;
        93:         }
        94:     }
        95: }
      and by using couple of ASP.NET MVC helpful classes like ViewContext, now I can detect which controller called this user control automatically and by adding new property to the the ViewData specifaying the Save Action method name in the controller; that gives more generic to the user control as it is not tight to specific controller or  methods.

    Still need more enhancements and features like validation and styles, but so far it is nice.

    It was very nice challenge to build such a control and I owe to references I mention below a lot.

    References:



  • Run time (Dynamic) attributes in C#

    In the current project I was concerned to build generic user control, accept generic collection and paint collection elements details in HTML table and allow editing functionality for specific columns. The challenge is to detect the editable properties in the collection elements in the run time without knowing the collection or columns types.

    My idea was to decorate desired properties with custom Attributes to specify the layout behavior; Attributes can be accessed and obtained using Reflection in the run time, the idea seems working fine.

    So, what are attributes in c#?

    "Attributes provide a powerful method of associating declarative information with C# code (types, methods, properties, and so forth). Once associated with a program entity, the attribute can be queried at run time and used in any number of way" - MSDN.

    Also attributes are classes that inherits System.Attribute as below

    [AttributeUsage(AttributeTargets.Property)]

    public class UIAttributes : Attribute

    {

    private bool isReadOnly = false;

    public bool IsReadOnly

    {

    get { return isReadOnly; }

    }

    public UIAttributes(bool isReadOnly)

    {

    this.IsReadOnly = isReadOnly;

    }

    }

    Then decorate class property with the custom attribute:

    public class ProductViewData

    {

    public string Name { get; set; }

    [UIAttributes(true)] //ProdcutID property is decorated to be read only.

    public int ProductID { get; set; }

    public double Price { get; set; }

    }

    Then by using reflection we can detect which property has the custom attribute and paint attribute according to that.

    public void DetectProperties(object o)

    {

    PropertyInfo[] propInfo = o.GetType().GetProperties();

    foreach(PropertyInfo prop in propInfo)

    {

    foreach(Attribute att in prop.GetCustomAttributes(true))

    {

    if((att is UIAttributes) && (((UIAttributes)att).IsReadOnly) )

    {

    //Do somthing.

    }

    }

    }

    }

    But let's say the same property should be editable in a view and non-editable in other view, then property behavior should be changed at run time to toggle between the editable/read-only mode, and custom attributes does now allow to be changed at runtime.

    So, after some readings there was no easy way to do that, but there was a great class in System.ComponentModel namespace called TypeDescriptor which allow dynamically assigning and querying metadata on instance on runtime, it seems like it is our solution.

    The only drawback is the long way to implement it, so first I built Interface representing any type need to follow my custom attribute:

    Interface:

    public interface IUIAttributes

    {

    bool IsReadOnly(string PName);

    bool IsReadOnly();

    }

    Custom Attribute:

    [AttributeUsage(AttributeTargets.Property)]

    public class UIAttributes : Attribute

    {

    private bool isReadOnly = false;

    public bool IsReadOnly

    {

    get { return isReadOnly; }

    set { isReadOnly = value; }

    }

    public UIAttributes()

    {

    }

    public UIAttributes(bool isReadOnly)

    {

    this.IsReadOnly = isReadOnly;

    }

    }

    Any Class implements the interface:

    public class ProductViewData : IUIAttributes

    {

    public string Name { get; set; }

    public int ProductID { get; set; }

    public double Price { get; set; }

    #region IUIAttributes Members

    bool IUIAttributes.IsReadOnly(string PName)

    {

    if (PName.Equals("ProductID"))

    return true;

    else

    return false;

    }

    bool IUIAttributes.IsReadOnly()

    {

    return false;

    }

    #endregion

    }

    Now I have to write a CustomTypeDescriptor and Provider, this descriptor will create instances of my custom attribute UIAttribute and associate them to types that implement the Interface:

    public sealed class CustomTypeDescriptionProvider : TypeDescriptionProvider where T : IUIAttributes

    {

    ///

    /// Constructor

    ///

    public CustomTypeDescriptionProvider(TypeDescriptionProvider parent)

    : base(parent)

    {

    }

    ///

    /// Create and return a custom type descriptor and chains it with the original

    /// custom type descriptor

    ///

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)

    {

    return new UIAttributeCustomTypeDescriptor(base.GetTypeDescriptor(objectType, instance));

    }

    }

    public sealed class UIAttributeCustomTypeDescriptor : CustomTypeDescriptor where T : IUIAttributes

    {

    ///

    /// Constructor

    ///

    public UIAttributeCustomTypeDescriptor(ICustomTypeDescriptor parent)

    : base(parent)

    {

    }

    public override AttributeCollection GetAttributes()

    {

    Type UIType = typeof(T).GetInterface(typeof(IUIAttributes).Name);

    if (UIType != null)

    {

    IUIAttributes UIInstance = GetPropertyOwner(base.GetProperties().Cast<PropertyDescriptor>().First()) as IUIAttributes;

    bool instanceLevelRoles = UIInstance.IsReadOnly();

    List<Attribute> attributes = new List<Attribute>(base.GetAttributes().Cast<Attribute>());

    UIAttributes UIAttrib = new UIAttributes(instanceLevelRoles);

    TypeDescriptor.AddAttributes(UIInstance, UIAttrib);

    attributes.Add(UIAttrib);

    return new AttributeCollection(attributes.ToArray());

    }

    return base.GetAttributes();

    }

    ///

    /// This method add a new property to the original collection

    ///

    public override PropertyDescriptorCollection GetProperties()

    {

    // Enumerate the original set of properties and create our new set with it

    PropertyDescriptorCollection originalProperties = base.GetProperties();

    List<PropertyDescriptor> newProperties = new List<PropertyDescriptor>();

    Type UIType = typeof(T).GetInterface("IUIAttributes");

    if (UIType != null)

    {

    foreach (PropertyDescriptor pd in originalProperties)

    {

    IUIAttributes UIInstance = GetPropertyOwner(pd) as IUIAttributes;

    bool propertyIsReadOnly = UIInstance.IsReadOnly(pd.Name);

    UIAttributes UIAttrib = new UIAttributes(propertyIsReadOnly);

    // Create a new property and add it to the collection

    PropertyDescriptor newProperty = TypeDescriptor.CreateProperty(typeof(T), pd.Name, pd.PropertyType, UIAttrib);

    newProperties.Add(newProperty);

    }

    // Finally return the list

    return new PropertyDescriptorCollection(newProperties.ToArray(), true);

    }

    return base.GetProperties();

    }

    }

    I have overridden GetAttributes in which I query the underlying instance to get read-only mode of the instance. Similarly, I have also overridden GetProperties which will in turn query the instance to get read-only mode of the specified property name.

    We are now ready to associate the UIAttribute with our class instances.

    ProductViewData pvd = new ProductViewData();

    pvd.ProductID = 1;

    pvd.Name = "Product1";

    pvd.Price = 99.99;

    TypeDescriptor.AddProvider(new CustomTypeDescriptionProvider<ProductViewData>(TypeDescriptor.GetProvider(typeof(ProductViewData))),pvd);

    And then by Reflection we can get properties attributes and check its layout behavior without knowing instance type, in next piece of code ‘Model’ is the instance name and ‘Name’ is the property name.

    public bool IsPropReadOnly(string Name)

    {

    bool readonlyatt = false;

    UIAttributes uiatt = TypeDescriptor.GetAttributes(Model).Cast<Attribute>().SingleOrDefault(a => a.GetType().Name == typeof(UIAttributes).Name) as UIAttributes;

    if ((uiatt != null) && uiatt.IsReadOnly)

    return uiatt.IsReadOnly;

    PropertyInfo propInfo = Model.GetType().GetProperty(Name);

    //(propInfo =>

    {

    PropertyDescriptor propDescriptor = TypeDescriptor.GetProperties(Model).Cast<PropertyDescriptor>().SingleOrDefault(p => propInfo.Name == p.Name);

    if (propDescriptor != null)

    {

    UIAttributes attrib = propDescriptor.Attributes.Cast<Attribute>().SingleOrDefault(p => p.GetType().Name == typeof(UIAttributes).Name) as UIAttributes;

    if (attrib != null)

    {

    readonlyatt = attrib.IsReadOnly;

    }

    }

    }

    //);

    return readonlyatt;

    }

    Thanks for this Post it helps a lot.

ASPXWizard.net some rights reserved 2005-2007
Powered by Community Server (Non-Commercial Edition), by Telligent Systems