Steve Moseley

"To err is human. To really screw up takes a computer." - Dilbert

Building an List of Links Asynchronously Using jQuery, AJAX, PURE, and Spark View Engine for MVC

clock December 27, 2009 13:05 by author steve

Yes, I know the title is a mouthful, but its all the cool stuff I used to make this trick work.  I have mentioned before that I have been using the Spark View Engine with ASP.Net MVC just because I think it is much cleaner markup when it comes to intermingling HTML with C#.  I also mentioned that I have been working on my blog app and like my own home blog page, I want to have a blog roll.  So what I did was create a table with two records containing information about my two favorite blogs.

blog_table

Now what I want to do is on the Master Page that I am using is have to these two links display on the page asynchronously after the base HTML loads.

The Spark Master Page and Partial Rendering

There is pretty good documentation for Spark for getting started, so I won’t go into detail about it here; but there are a few things I wanted to mention.  The default way to use a Master Page in the Spark View Engine is to create a folder named “Layouts” inside the “View” folder and then to add a file named “Application.spark” in that folder.  This is the convention that will allow all other views to access that Master Page unless otherwise specified.

   1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
   2: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
   3:   <head>
   4:     <title>${H(Title)}</title>
   5:     <link rel="stylesheet" href="~/Content/Site.css" type="text/css" />
   6:     <use content="head"/>
   7:      <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min-vsdoc.js" type="text/javascript"></script>
   8:     <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js" type="text/javascript"></script>
   9:     <script src="/Scripts/pure.js" type="text/javascript"></script>
  10:   </head>
  11:   <body>
  12:     <div id="wrapper">
  13:         <div id="header">
  14:             <div id="logo">
  15:                 <h1>Website Logo Goes Here</h1>
  16:             </div>
  17:             <div id="search">
  18:                 Search goes here
  19:             </div>
  20:         </div>
  21:         <div id="header_menu">
  22:             <ul id="menu">
  23:                 <li>
  24:                     <a title="" accesskey="1" href="#">Home</a>
  25:                 </li>
  26:                 <li>
  27:                     <span>|</span>
  28:                 </li>
  29:                 <li>
  30:                     <a title="" accesskey="2" href="#">Blog</a>
  31:                 </li>
  32:                 <li>
  33:                     <span>|</span>
  34:                 </li>
  35:                 <li>
  36:                     <a title="" accesskey="3" href="#">About Me</a>
  37:                 </li>
  38:                 <li>
  39:                     <span>|</span>
  40:                 </li>
  41:                 <li>
  42:                     <a title="" accesskey="4" href="#">Contact</a>
  43:                 </li>
  44:             </ul>
  45:             <div id="menu_spacer">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  46:             </div>
  47:         </div>
  48:         <div id="content">
  49:             <div id="colOne">
  50:                 <div class="box">
  51:                      
  52:                     <myLinks />
  53:                 </div>
  54:             </div>
  55:             <div id="colTwo">
  56:                 <use content="view"/>
  57:             </div>
  58:         </div>
  59:         
  60:     
  61:   </body>
  62: </html>

Some things to notice on the master page here, is that I am accessing jQuery, and PURE.  Of course, jQuery is the JavaScript framework that makes almost everything JavaScript related really easy.  The other script tag for PURE is also a JavaScript framework but it is mainly to make the mapping of markup to your JSON response much easier.  We use it a lot where I work, so I have become a big fan of it.  I will demonstrate that later.

Also another thing to notice in this code is the tag <myLinks />.  This is how Spark does partial HTML rendering.  The convention for partial rendering in Spark is to create a file in the “Views/Shared” folder that is prefixed with “_” and has the extension “spark” like all the other views.  So for my example above, I have a file located at “<project_location>/View/Shared/_myLinks.spark”.

 

The Controller Class

Nothing special here. I have a controller class that returns a JSON response.  The only thing I should mention though is in order for PURE to work with a list of records you will need to rap your List object in another object.  Also, as mentioned in a previous post, I need to return a JsonResult to get the properly formatted response.  Since these links won’t change much (I am not that fickle) I am caching this action using the OutputCache attribute.

   1: using System.Collections.Generic;
   2: using System.Web.Mvc;
   3: using Aviblog.Core.Dto;
   4: using Aviblog.Core.Services;
   5:  
   6: namespace Aviblog.Web.Controllers
   7: {
   8:     public class LinksController : Controller
   9:     {
  10:         private readonly ILinksService _linksService;
  11:  
  12:         public LinksController(ILinksService linksService)
  13:         {
  14:             _linksService = linksService;
  15:         }
  16:  
  17:         [AcceptVerbs(HttpVerbs.Get)]
  18:         [OutputCache(Duration = 60, VaryByParam = "None")]
  19:         public JsonResult All()
  20:         {
  21:             Links links;
  22:             IList<LinkDto> result = _linksService.GetActiveLinks();
  23:             links = result != null ? new Links() {LinkList = result} : null;
  24:             return Json(links);
  25:         }
  26:     }
  27: }

My Links class looks like this:

   1: using System.Collections.Generic;
   2:  
   3: namespace Aviblog.Core.Dto
   4: {
   5:     public class Links
   6:     {
   7:         public IList<LinkDto> LinkList { get; set; }
   8:     }
   9: }

This will generate a JSON response as follows:

{"LinkList":
[{"LinkId":1,"Title":"Scott Gunthie","Description":"Scott Gunthrie\u0027s Blog","BlogUri":http://weblogs.asp.net/scuttgu/,
"FeedUri":null,"IsActive":true},{"LinkId":2,"Title":"Scott Hanselman",
"Description":"Scott Hanselman\u0027s Blog","BlogUri":"http://www.hansleman.com/","FeedUri":null,"IsActive":true}]}

Notice, that I now have my wrapper class called LinkList that I can use to tell PURE that this is my collection.

 

The Partial HTML that Loads the Links

So in my _myLinks.spark file I have the JavaScript that is going to make an AJAX call to get the links, and the PURE code that will map the response back to the unordered list.

   1: <script type="text/javascript">
   1:  
   2:     $(document).ready(function() {
   3:         $.ajax({
   4:             type: "GET",
   5:             url: "/Links/All",
   6:             dataType: "json",
   7:             success: function(res) {
   8:                 var $blogRollList = $("ul#blogRollList");
   9:                 
  10:                 var directive = {
  11:                     'li':{
  12:                         'link<-LinkList':{
  13:                             'a':'link.Title',
  14:                             'a@href':'link.BlogUri'
  15:                         }
  16:                     }
  17:                 };
  18:                 
  19:                  $blogRollList.render(res, directive);
  20:  
  21:             }
  22:         });
  23:     });
</script>
   2:  
   3:         
   4: <h3>Bloggroll</h3>
   5: <div id="blogRoll">
   6:     <ul id="blogRollList">
   7:         <li><a></a></li>
   8:     </ul>
   9: </div>

I am using the $.Ajax function to make a call out to my controller class “LinksController” and the action “All”.  If the code returns a successful response, the PURE code maps the response to the list.

The $blogRollList variable is the ul tag that I want my response to be loaded into.

The directive variable is how I tell PURE to map my response.  It is saying for each item in the response, create a list tag.  The “link<-LinkList” is saying for the collection LinkList there will be items named “link”.  Inside that declaration I am then using each “link” item that was defined and them mapping it to an anchor tag.

The Result

So now when the page loads I get the result:

blogroll



Creating a Custom Trace Listener for WCF

clock June 15, 2009 11:00 by author Steve

Introduction

We have been using WCF to consume a lot of 3rd party web services, and one of things we depend on when testing is being able to see the actual request and response that is being sent to and from the host.  This is expecially inportant in 3rd party services, because the service is essentially a black box in which there is no way to know what is going on except that you send it a request and you get a response back.  I posted how, out of the box, you can configure WCF to write the message to a trace xml file, but one of things about writing to a file is that if you or your testers do not have access to the web server where the file is being written then this is not going to work.

Solution

The way around this problem is to create a custom trace listener that catches the messages and does what ever you want.  Here is a simple sample.

Create a class that inherits from the TraceListiner class.

    1 using System.Diagnostics;

    2 

    3 namespace WcfTrace.Trace

    4 {

    5     public class WebTraceListener : TraceListener

    6     {

    7         public override void Write(string message)

    8         {

    9             //write you custom code here

   10             Debug.WriteLine(message);

   11         }

   12 

   13 

   14         public override void WriteLine(string message)

   15         {

   16             //write your custom code here

   17             Debug.WriteLine(message);

   18         }

   19     }

   20 }

The next thing to do is to wire up the custom class in the web.config (or app.config) so that when messages are created ,they caught by the custom trace listener class.  The config below is telling source messages to use the sharedListener named xml which the custom trace listener listed above.

  108   <system.diagnostics>

  109     <sources>

  110       <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">

  111         <listeners>

  112           <add name="xml" />

  113         </listeners>

  114       </source>

  115       <source name="System.ServiceModel.MessageLogging">

  116         <listeners>

  117           <add name="xml" />

  118         </listeners>

  119       </source>

  120     </sources>

  121     <sharedListeners>

  122       <add name="xml" type="WcfTrace.Trace.WebTraceListener,WcfTrace.Trace" />

  123     </sharedListeners>

  124   </system.diagnostics>

  125 

  126   <system.serviceModel>

  127     <diagnostics>

  128       <messageLogging

  129           logEntireMessage="true"

  130           logMalformedMessages="false"

  131           logMessagesAtServiceLevel="true"

  132           logMessagesAtTransportLevel="false"

  133           maxMessagesToLog="300000"

  134           maxSizeOfMessageToLog="200000"/>

  135     </diagnostics>

That's it.  Now when ever a web service call is made, the custom trace listener class is executed, passing it the trace messages.



Creating a Web Application Using MVC, Unity and NHibernate – Part 1

clock January 13, 2009 11:29 by author Steve

 

Introduction

 

I thought it would be useful to document what it would take to incorporate building a web application that is designed with the Microsoft MVC framework, that also incorporated using NHibernate as a O/R Mapper and uses Microsoft Unity as the Dependency Injection, IoC framework.  I must confess, I am not an expert of any of these tools, so I welcome feedback from the community.  The reason I am doing this to begin with, is there is not a whole a lot of documentation on of this “stuff” by themselves let alone all together, so I am hoping fill a little bit of that void.  If you think there are better approaches then what I am doing, feel free to provide feedback.   I am basically doing this for my own enrichment and if it is also helpful to the community, then—well—even better.

Since this going to have to be a series of posts, I will probably not cover every aspect of this application all at once, so if what you are looking for is not in this post, then be patient and maybe I will get to it in a later one.  As a matter of fact, since I am going to be adhering to a test first approach (red, green, refactor), I will probably not get to the MVC framework until several posts from now.  The first few posts will only be covering tests.

 

Setting up NHibernate

 

I have mentioned this before, but if you are new to NHibernate and don’t know how to get started, I highly recommend the Summer of NHibernate videos by Stephen Bohlen.  The download of NHibernate is located here.  Once downloaded, the first thing I need to do is put the NHibernate schema files in the Visual Studio schema folder (my folder is located here C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas) so I can get intellisense on the configuration and mapping files. 

In general, for all the external libraries, it is useful to place them all in a folder location relative to, or just inside your solution so, for example, if you are using source control the other developer machines will pick up the references without any problems.  Thus, I am doing the same with this application, and will then be referencing the NHibernate.dll in all my relevant projects.  In my case, I am only going to have a Web, Core, and Test project so I will reference it for now in the Core and Test project.  I will probably need to reference in the web project once I set up Unity, but I will leave it out for now.

The next thing I will need to do is to create a NHibernate configuration file.  One of the cool concepts NHibernate follows is the Convention over Configuration paradigm.  That is, as long as I follow a certain convention, I will not need to configure certain aspects of NHibernate when setting it up.  So if I create a configuration file and name it hibernate.cfg.xml, then set is build action to copy to output folder, I will not need to tell NHibernate where the configuration file is.  Note:  I tried this in a Microsoft Team Test project and for some reason the test project would not copy that file to the output folder so I ended up having to configure the path anyway.

Here is the configuration file that is in the root directory of my test project.

    1 <?xml version="1.0" encoding="utf-8" ?>

    2 <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">

    3   <session-factory name="NHibernate.Test">

    4     <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>

    5     <property name="connection.connection_string">

    6       Data Source=(local);Initial Catalog=News;Persist Security Info=True;User ID=my_dev;Password=my_dev

    7     </property>

    8     <property name="adonet.batch_size">10</property>

    9     <property name="show_sql">true</property>

   10     <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>

   11     <property name="use_outer_join">true</property>

   12     <property name="command_timeout">444</property>

   13     <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>

   14     <mapping assembly="News.Core"/>

   15   </session-factory>

   16 </hibernate-configuration>

 

Notice that in the root tag that since I place the NHibernate schema files in the Visual Studio schema folder I now have access to intellisense.  You can see the property setting definitions here, but here are the important nodes

·          The show_sql property is going to be useful when debugging so I can see the SQL statements.  I will turn this off in production because it is expensive.

·          The dialect is going to tell NHibernate to what specific database language and version it is going to translate the SQL to.

·          The mapping tells NHibernate where the mapping files and classes are located.

Once the configuration file is created, I then can call it from my code and create the ISessionFactory.  I will talk about my approach later in another post, but essentially because ISessionFactory is expensive to create and there also some threading concerns with creating it; I am going to create it differently in the test project than in the web project.  In both cases, I am going to implement an interface I created call ISessionFactoryManager.  This interface, as of now, will have one method called GetSessionFactory.  The code in my test project looks like this.

    1 using News.Core.Data;

    2 using NHibernate;

    3 using NHibernate.Cfg;

    4 

    5 namespace News.Web.Tests

    6 {

    7     internal class TestSessionFactoryManager : ISessionFactoryManager

    8     {

    9         public ISessionFactory GetSessionFactory()

   10         {

   11             string path = @"C:\Projects\News\Src\News.Web.Tests\hibernate.cfg.xml";

   12             var cfg = new Configuration();

   13             cfg.Configure(path);

   14             return cfg.BuildSessionFactory();

   15 

   16         }

   17     }

   18 }

 

 Note:  If I want, I can also add properties at run time.  For example, say my connection string is stored in a super secret place; I could get it and set the ISessionFactory with the following code.  It just has to be done before the BuildSessionFactory is called, because once called, it cannot be changed.

  15             cfg.Properties.Add("connection.connection_string",connectionString);

 

Once the BuildSessionFactory method is called, Nhibernate goes and retrieves the configuration file, and also the Mapping files (I will discuss later) and returns the session factory.  I now can open sessions to the database and do whatever database CRUD I need to do.

Mapping Tables to Classes

 For my example, I am going to have two tables with the following schema:


For this post, I am only going to worry about the News table.  Again, using convention over configuration, I have mapping file named NewsItemDto.hbm.xml so I do not have to tell NHibernate where it is.  I need to make sure this file is an embedded resource, so it can be referenced.  The content of the file looks like this:

    1 <?xml version="1.0" encoding="utf-8" ?>

    2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="News.Core" namespace="News.Core.Dto">

    3   <class name="News.Core.Dto.NewsItemDto, News.Core" table="News">

    4     <id column="NewsId" name="NewsId" type="long" unsaved-value="0">

    5       <generator class="native"></generator>

    6     </id>

    7     <property column="AuthorId" name="AuthorId" type="long" not-null="true"/>

    8     <property column="DateAdded" name="DateAdded" type="DateTime" not-null="true"/>

    9     <property column="DateUpdated" name="DateUpdated" type="DateTime" not-null="true"/>

   10     <property column="DatePublished" name ="DatePublished" type="DateTime" not-null="true" />

   11     <property column="Title" name="Title" type="string" not-null="true"/>

   12     <property column="ShortDescription" name="ShortDescription" type="string" not-null="false"/>

   13     <property column="Body" name="Body" type="string" not-null="true"/>

   14     <property column="IsFrontPage" name="IsFrontPage"/>

   15     <property column="IsPublished" name="IsPublished"/>

   16   </class>

   17 </hibernate-mapping>

 

Again, the root references the NHibernate mapping schema which will give me intellisense.  You can see all the property settings here.  In the class, I tell it the namespace and assembly name where the class is located.  The ID identifies the primary key of the table.  By setting the generator attribute to native, I am telling NHibernate that the field is an identity field and the value is created by the database.

Here is the code for the class:

    5     public class NewsItemDto

    6     {

    7         public virtual long NewsId { get; set; }

    8 

    9         public virtual long AuthorId { get; set; }

   10 

   11         public virtual DateTime DateAdded { get; set; }

   12 

   13         public virtual DateTime DatePublished { get; set; }

   14 

   15         public virtual DateTime DateUpdated { get; set; }

   16 

   17         public virtual string Title { get; set; }

   18 

   19         public virtual string ShortDescription { get; set; }

   20 

   21         public virtual string Body { get; set; }

   22 

   23         public virtual bool IsFrontPage { get; set; }

   24 

   25         public virtual bool IsPublished { get; set; }

   26     }

 

The properties are all virtual so NHibernate can utilize the proxy pattern for performance reasons.  This will come more into play when use the Authors table in following posts.

 

Creating the test

 

As a part of the buildup and teardown of my NHibernate tests, I will have each of my test create a session object that connects to the data base before the test starts and then close the session object once the test finishes.

    1 using News.Core.Data;

    2 using Microsoft.VisualStudio.TestTools.UnitTesting;

    3 using NHibernate;

    4 

    5 namespace News.Web.Tests

    6 {

    7     public class DatabaseBaseTest

    8     {

    9         private ISessionFactoryManager sessionFactoryManager = new TestSessionFactoryManager();

   10         protected ISession session;

   11 

   12 

   13 

   14         [TestInitialize]

   15         public void SetUp()

   16         {

   17             

   19             session = sessionFactoryManager.GetSessionFactory().OpenSession();

   20         }

   21 

   22         [TestCleanup]

   23         public void CleanUp()

   24         {

   25             session.Close();

   26             session = null;

   27         }

   28     }

   29 }

 

Now that I have Session I can create my first test.  This test will simply return 1 record from the NewsItem table by passing the NewsItemId parameter.

   78         [TestMethod()]

   79         public void GetNewsItemByItemIdShouldReturnMatchingTitle()

   80         {

   81             var target = new NHibernateDataProvider(session);

   82             const string expected = "test";

   83             const int itemId = 2;

   84             Assert.AreEqual(expected, target.GetNewsItemByItemId(itemId).Title);

   85         }

 

My database has a record which contains the Title value “test”.

 

The Repository

 

The data access code that gets the records looks like this:

   17         public NewsItemDto GetNewsItemByItemId(long newsItemId)

   18         {

   19             return _session.Get<NewsItemDto>(newsItemId);

   20 

   21         }

 

 

In the next post I will add the Authors table to the mix and some functionality retrieving those joined records.



Extension Methods

clock October 9, 2008 13:33 by author Steve

Extension Methods are new to Visual Studio 2008 and they basically let you bolt on additional functionality to the side of an existing type or function.  You can see the offical definition here.  This functionality is especially useful with LINQ which I will show in a sample of shortly, but first let's look at a simple sample to see how you can use it.

Basic Sample

I am going to create a simple console application that takes a number and doubles it, tripples it, splits it, and divides it into thirds.  With one line of code I am going to call all of these functions.  Below is the class that contains the Extension Methods.

    6 namespace ExtensionMethodSample

    7 {

    8     public static class Extension

    9     {

   10 

   11         public static decimal Double(this decimal amount)

   12         {

   13             return amount*2m;

   14         }

   15 

   16         public static decimal Tripple(this decimal amount)

   17         {

   18             return amount*3m;

   19         }

   20 

   21         public static decimal Split(this decimal amount)

   22         {

   23             return amount/2;

   24         }

   25 

   26         public static decimal Thirds(this decimal amount)

   27         {

   28             return amount/3;

   29         }

   30     }

   31 }

Notice that for the method to be an extension, you have to define it as a static method, and you have to define the first parameter as "this" which will be the type value or method return value you are bolting this extension method on to.  You can have more parameters following but the first parameter is always the calling instance.

I can then call the methods above like this:

    8     class Program

    9     {

   10         static void Main(string[] args)

   11         {

   12             var amount = 5m;

   13 

   14             var result = amount.Tripple().Split().Double().Thirds();

   15 

   16             Console.Write(result);

   17             Console.Read();

   18 

   19         }

   20     }

 The crazy calculation above actually return the original value.

 




Linq Sample

You are most likely going to be calling pre-defined Extensions rather than writing you own especially when using LINQ.  For example I can abreviate the followng LINQ statement: 

   10             var db = new NewsContextDataContext();

   11             return from n in db.NewsPosts

   12                    where n.IsPublished

   13                    select n;

 I can use extension methods and abbreviate it like this (thanks Resharper!):

   10             var db = new NewsContextDataContext();

   11             return db.NewsPosts.Where(n => n.IsPublished);

 The MVC Storfront Sample

On the ASP.net web site there is a bunch a screencasts and code samples mainly dedicated to implimenting a real world MVC web site, but one of the interesting approaches they have taken is that when they use LINQ to talk to the database, they actually have a methods that returns a basic query of data and then they have a set of Extension Methods that take the initial data set and filters it further depending on what is needed.  So for example if you look at my code in the sample of above, I can take that data set and return it as the type IQueryable<NewsPost>.  I can then take that type and if I want to Query it further, I can use an extension method to return a smaller data set.  Also, notice here I have more than one argument I am passing in.

   12         public static IQueryable<NewsPost> WithTopFrontPage(this IQueryable<NewsPost> posts, int take)

   13         {

   14             return (posts.Where(p => p.IsFrontPage)).Take(take);

   15         }

The above extension method takes the original data set and gets only the records that have the FrontPage indicator set to true, and then takes the top 5 records.  I then have a test that checks to see if I have indeed returned 5 records.

   70             var actual = respository.GetAllActive().WithTopFrontPage(5).ToList();

   71             var expected = 5;

   72             Assert.AreEqual(actual.Count, expected, "Expected result does not equal 5");

Yey it passed!  This is a pretty neat approach that should make retrieving data simpler.

Downside:

Now that I got us all excited about using this feature just a word of warning.  It seems to me that in using Extesnsion Methods we are violating the philosophy of "not talking to strangers."  In other words we are pretty tightly coupled here and if you do not have control over the object that you are bolting to your extension to, there could be problems down the road.  Actually even if you do have control, you still could be entering the "zone of pain."  So with that said, happy coding and use carefully.



Creating a Category Menu Dynamically Using the HoverMenuExtender

clock September 20, 2008 12:24 by author Steve

Introduction

When building an eCommerce website, there are a multitude of ways to display categories an subcategories that are dynamically retrieved from a database.  In this sample, I will show how to display list of categories that when you hover over the category with your mouse, a popup menu will appear with the subcategories.  To do this I am going use the AjaxControlToolKit HoverMenuExtender.

 

The Master Page

I am going to place this menu on my master page so it can be used by all pages related to searching for a product.  The first thing you will always need when using any of the AJAX Controls is the ScriptManager.

   16     <cc1:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">

   17     </cc1:ToolkitScriptManager>

 The next thing I need to add is the markup for the categories.

   20             <asp:Repeater ID="RepeaterCategories" runat="server" OnItemDataBound="RepeaterCategories_ItemDataBound"

   21                 DataMember="CategoryId">

   22                 <ItemTemplate>

   23                     <asp:Panel class="categoryItem" runat="server" ID="categoryPanel">

   24                         <asp:LinkButton runat="server" ID="linkCategory" Text='<%#Eval("CategoryName")%>'></asp:LinkButton>

   25                         <asp:HiddenField ID="hiddenCategory" runat="server" Value='<%#Eval("CategoryId") %>' />

   26                     </asp:Panel>

   27                     <asp:Panel class="subCategoryList" runat="server" ID="subCategoryPanel">

   28                         <asp:Repeater ID="RepeaterSubCategories" runat="server">

   29                             <ItemTemplate>

   30                                 <div class="categoryItem">

   31                                     <a href="#">

   32                                         <%#Eval("CategoryName")%>

   33                                     </a>

   34                                 </div>

   35                             </ItemTemplate>

   36                         </asp:Repeater>

   37                     </asp:Panel>

   38                     <cc1:HoverMenuExtender ID="HoverMenuExtender1" runat="server" PopupPosition="Right"

   39                         PopupControlID="subCategoryPanel" TargetControlID="linkCategory" HoverCssClass="popupHover">

   40                     </cc1:HoverMenuExtender>

   41                 </ItemTemplate>

   42             </asp:Repeater>

Here I have a repeater control which will repeat for each main category.  Inside the ItemTemplate for that control, I have two panels, one for the main category links and the other which will be the container for the subcategories.  Make sure your style for subcategory panel is defaulted to display equals none and visibility equals hidden.  This will prevent the panel from flashing when the page first loads.  Inside the panel for the subcategories I have a nested repeater which will retrieve the subcategories for each category.  To get the subcategories I will need to fire off the OnItemDataBound event on the main category repeater to make a call to the database and get the subcategories.  The subcategory link would most likely have a link to a product page.

The OnItemDataBound Event

On the OnItemDataBound event,  I need retrieve the current item being bound to the control and if the item is of the appropriate type, then pass the necessary Category Id to my middle tier to retrieve the corresponding subcategories.

   46         protected void RepeaterCategories_ItemDataBound(object sender, RepeaterItemEventArgs e)

   47         {

   48             RepeaterItem item = e.Item;

   49 

   50             if (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem)

   51             {

   52                 HiddenField category = item.FindControl("hiddenCategory") as HiddenField;

   53                 int catId = Convert.ToInt32( category.Value );

   54                 Repeater subcategoryControl = (Repeater)e.Item.FindControl("RepeaterSubCategories");

   55                 subcategoryControl.DataSource = presenter.OnCategoryItemBound(catId);

   56                 subcategoryControl.DataBind();

   57 

   58             }

   59         }

 In this case I am retrieving the Category ID from the hidden tag placed in the same row as the category name.

 The Result

Okay, so this is not the prettiest sample in the world, but you get the idea.  Perhaps you can jaz this up a bit with a shadow back drop and a rounded corners but the intent was just show how to make it work, and all though I did not show it, I used LINQ  behind the scenes so all in all, there was very little code to do this.

 

 



Using ASP.Net AJAX To Auto Update From Date and To Date textboxes

clock August 16, 2008 09:15 by author Steve

Introduction

If you have ever had to make a hotel reservation on line you may have seen this functionality. Basically there is a field for a check-in date and there is also a field for a check-out date. Typically the websites have cool popup windows with a calendar in it that when selected, the text field is automatically updated with the chosen date. It then gets a little bit more complicated because usually when you select the check in date, the check out-date is automatically updated also.

 Let's take a look at how to a this with ASP.AJAX and the Ajax Control Toolkit CanderExtender.

 The Mark Up

   32 <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">

   33         <ContentTemplate>

   34             <div class="DatePanel">

   35                 <table>

   36                     <tr>

   37                         <td>

   38                             from date:

   39                             <asp:TextBox runat="server" ID="txtFromDate" onFocus="javascript:this.blur();" Width="80"

   40                                 autocomplete="off" AutoPostBack="true" OnTextChanged="txtFromDate_TextChanged" />

   41                             <asp:ImageButton runat="Server" ID="Image1" ImageUrl="~/images/Calendar_scheduleHS.png"

   42                                 AlternateText="Click to show calendar" /><br />

   43                             <cc1:CalendarExtender ID="calendarButtonExtender" runat="server" TargetControlID="txtFromDate"

   44                                 PopupButtonID="Image1" />

   45                             <asp:RangeValidator ID="RangeValidatorFromDate" runat="server" ErrorMessage="Please enter a date a greater than or equal today's date"

   46                                 ControlToValidate="txtFromDate" Type="Date"></asp:RangeValidator>

   47                         </td>

   48                         <td>

   49                             &nbsp;&nbsp;&nbsp; to date:

   50                             <asp:TextBox runat="server" ID="txtToDate" onFocus="javascript:this.blur();" Width="80"

   51                                 autocomplete="off" OnTextChanged="txtToDate_TextChanged" AutoPostBack="true"/>

   52                             <asp:ImageButton runat="Server" ID="Image2" ImageUrl="~/images/Calendar_scheduleHS.png"

   53                                 AlternateText="Click to show calendar" /><br />

   54                             <cc1:CalendarExtender ID="CalendarExtender1" runat="server" TargetControlID="txtToDate"

   55                                 PopupButtonID="Image2" />

   56                             <asp:RangeValidator ID="RangeValidatorToDate" runat="server" ErrorMessage="Please enter a date a greater than or equal today's date."

   57                                 ControlToValidate="txtToDate" Type="Date"></asp:RangeValidator>

   58                         </td>

   59                     </tr>

   60                 </table>

   61                 <br />

   62                 <br />

   63             </div>

   64         </ContentTemplate>

   65         <Triggers>

   66             <asp:AsyncPostBackTrigger ControlID="txtFromDate" EventName="TextChanged" />

   67             <asp:AsyncPostBackTrigger ControlID="txtToDate" EventName="TextChanged" />

   68         </Triggers>

   69     </asp:UpdatePanel>

The textbox could be wired many different ways, but here is what I did.

  • First off, I set the OnFocus property to blur. This blocks the user's from entering a date in the texbox directly.  I did not want the user to have the ability to enter an incorrectly formated date, so I am only allowing them to enter the date only by using the calendar control.
  • Because I want to update the "to date" when the text changes in the "from date", I have set the OnTextChange event to fire which will map to our function that will do the update.  I have also set the AutoPostback property to true, so it knows to make a postback.  Because we also have this event mapped to UpdatePanel trigger, it will do an asynchronous call to the function causing only this section of the page to update.
  • The ImageButton will fire the popup calendar.
  • The CalendarExtender control them maps the Textbox and the ImageButton so they work in tandom.  Because I am using an ImageButton, the calendar will automatically disappear when the date is selected.
  • I have a RangeValidator that basically checks to make sure the date is not before today's date.
  • The same thing is then done for the "to date" controls.
  • All of this is then wrapped by an UpdatePanel (note: make sure you set the ToolScriptManager to enable partial rendering and you also set the UpdatePanel's UdateMode to "Conditional" for better performance).
  • The triggers are then set to asynchronously post back when text changes.

The Page_Load Event

I would definatelly refactor this code later but for simplicity sake I am just puttong the code right on the page load event.

   19 protected void Page_Load(object sender, EventArgs e)

   20         {

   21             if (!Page.IsPostBack)

   22             {

   23                 RangeValidatorFromDate.MinimumValue = DateTime.Today.ToShortDateString();

   24                 RangeValidatorFromDate.MaximumValue = DateTime.MaxValue.ToShortDateString();

   25                 RangeValidatorToDate.MinimumValue = DateTime.Today.ToShortDateString();

   26                 RangeValidatorToDate.MaximumValue = DateTime.MaxValue.ToShortDateString();

   27                 txtFromDate.Text = DateTime.Today.ToShortDateString();

   28                 txtToDate.Text = DateTime.Today.AddDays(30).ToShortDateString();

   29 

   30             }

   31 

   32 

   33         }

Here all I am doing is setting the RangeValidator exception rules, and I also setting the default date to be today and 30 days from now.

The Text Change Events

   35         protected void txtFromDate_TextChanged(object sender, EventArgs e)

   36         {

   37             DateTime fromDate = Convert.ToDateTime(txtFromDate.Text);

   38 

   39             if (fromDate >= DateTime.Today)

   40                 txtToDate.Text = fromDate.AddDays(30).ToShortDateString();

   41             else

   42             {

   43                 txtFromDate.Text = DateTime.Today.ToShortDateString();

   44                 RangeValidatorFromDate.IsValid = false;

   45 

   46             }

   47         }

   48 

   49         protected void txtToDate_TextChanged(object sender, EventArgs e)

   50         {

   51             DateTime toDate = Convert.ToDateTime(txtToDate.Text);

   52             DateTime fromDate = Convert.ToDateTime(txtFromDate.Text);

   53 

   54             if (toDate < fromDate && toDate >= DateTime.Today)

   55                 txtFromDate.Text = toDate.ToShortDateString();

   56             else

   57             {

   58                 if (toDate < DateTime.Today)

   59                 {

   60                     txtFromDate.Text = DateTime.Today.ToShortDateString();

   61                     txtToDate.Text = DateTime.Today.AddDays(30).ToShortDateString();

   62                     RangeValidatorToDate.IsValid = false;

   63                 }

   64             }

   65 

   66         }

  • When the "from date" is selected, I want to change the "to date" to be 30 days from now.  If the date is less than today, I want to set the "to date" back to today.
  • When the "to date" is selected, if it is less than the "from date", then I want to set the "from date" to be the same date.  If the "to date" is less than today's date, then I want warn the user and set the dates back to the default dates.
 Not too difficult.

 

 

 



Where did CopyAsHTML go?

clock August 10, 2008 12:03 by author Steve

I was asked how I was copying my code to this blog, and I was going to reply that you can get this great free tool at the address below but if you click on it (at the time of this writing anyway), the link is dead.

www.jtleigh.com/people/colin/blog/archives/2006/06/copysourceashtm_9.html 

I heard about CopyAsHtml tool from MSDN Magazine a couple years ago in its aricle titled, "Visual Studio Add-Ins Every Developer Should Download Now."  Even though it was written in 2005, much of the tools on this page are still pretty useful.  If you find this tool some where you can still get it to work for Visual Studio 2008.  Go to your VS 2005 addins folder (C:\Users\<user>\Documents\Visual Studio 2005\Addins) and copy the CopyAsHtml files to your Visual Studio 2008 folder.  Then Open the CopySourceAsHtml.AddIn file and where ever you see 8.0, change it to 9.0.

Now as far as settings, Scott Hanselman has a nice post on the different themes out there that you can use if you do not want to go through all the toils of setting all the fonts yourself.  You can now Import themes which is quite cool.  I picked Oren Ellenbogen's theme, because I really like the soft dark colors.  I only changed a couple of colors to make my User Object and key words stand out a bit more.  I started my IT life in front of a green screen, and even when I was developing in Visual Basic 6.0 and Visual Interdev, I changed my background settings to black.

 If you know where we can get CopyAsHtml let us know.  It's a great tool and it would be a shame if it disappeared.



Sorting a List Object

clock August 9, 2008 08:28 by author Steve

Introduction

When Generics came out in Visual Studio 2005, the new List object was introduced.  This feature was a new collection object that also gave you the ability to sort records based on whatever criteria you wanted.  There was also a new coding feature that came out called “anonymous functions” which basically allowed you to plop a blob of code in a parameter of another function.   The Sort function was a big step in sorting collections, much simpler than in Visual Studio 2003 but it still a bit jankie.

Sorting in Visual Studio 2005

So let's take a look at how to do this old way and then look at some new ways we can do the same thing in Visual Studio 2008.

   30         public enum SortDirection

   31         {

   32             Ascending = 1,

   33             Descending = -1

   34         }

   35 

   36 

   37         public List<GuestDto> SortLastNameUsingAnonymousFunction()

   38         {

   39             if (guests == null)

   40                 return null;

   41 

   42             guests.Sort(new Comparison<GuestDto>(delegate(GuestDto guest1, GuestDto guest2)

   43                 {

   44                     return Convert.ToInt32(SortDirection.Ascending) * guest1.LastName.CompareTo(guest2.LastName);

   45                 }));

   46 

   47             return guests;

   48         }

  Even after having done this many times, I still need to refer to an example before doing it because it is just not that intuitive.

Using Lambda Expressions in the Same Example

Now let's look at a cleaner way to do this that even I can remember.

   50         public List<GuestDto> SortLastNameUsingLambdaExpressions()

   51         {

   52             if (guests == null)

   53                 return null;

   54 

   55             guests.Sort((guest1, guest2) =>

   56                 Convert.ToInt32(SortDirection.Ascending) * guest1.LastName.CompareTo(guest2.LastName));

   57 

   58             return guests;

   59         }

Essentially the way it works is the parameters are placed on the left side of the => statement and the body of the code you want to execute is on the right.   All the types (including guest1, guest2, the new Comparison object , and the delegate and return statements) are automatically determined by the compiler so you do not need to specify it.  So the Lambda Expression is a nice way to abbreviate anonymous methods and make the code more readable.  

 Adding Linq to the Equasion

When I mentioned to my team the neat way sorting which is easier to code, a co-worker and friend Mike Bosch (great blog here) showed me an even better approach by just referencing the Linq namespace.

 

   62         public List<GuestDto> SortLastNameUsingLinqLambdaExpressions()

   63         {

   64             if (guests == null)

   65                 return null;

   66 

   67             List<GuestDto> sortedGuests = guests.OrderBy(g => g.LastName).ToList();

   68 

   69             return sortedGuests;

   70 

   71         }

To which in the immortal words of Bill and Ted's Excellent Adventure, I say "PARTY ON DUDES!"

 

 



Calendar

<<  July 2010  >>
MoTuWeThFrSaSu
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar

Sign in