The ASP.NET do and don’ts serie : episode 1 – the custom validator control

Posted August 19th, 2010 in ASP.NET, Do and don'ts by Sam Beauvois

This is the first post of a serie on the ASP.NET practices.

For this serie, I get inspiration from what I see in my daily work or on the web

Don’t use custom validator when you can use an other control !

Do:

in the aspx markup


<asp:TextBox runat="server" ID="FeedURLTbx"  />

<asp:RegularExpressionValidator runat="server"
     ID="URLFormatValidator"
     ControlToValidate="FeedURLTbx"
     ValidationExpression="(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?"
     Text="!"
     ErrorMessage="The URL format is not valid." />

Don’t:

in the aspx markup


<asp:TextBox runat="server" ID="FeedURLTbx"  />

 <asp:CustomValidator runat="server" ID="URLFormatValidator"
 ControlToValidate="FeedURLTbx"
 ErrorMessage="The URL format is not valid."
 OnServerValidate="ValidateURL" />

and in the aspx code behind

protected void ValidateURL(object source, ServerValidateEventArgs args)
{

System.Text.RegularExpressions.Regex myRegExp = new  System.Text.RegularExpressions.Regex(@"(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?");

args.IsValid = myRegExp.IsMatch(args.Value);
}

Why??

First because the RegularExpressionValidator is designed for string validations and it’s a non sense to do the same with another control !

Second because the CustomValidator used like here will cause a postback while the RegularExpressionValidator will generate the validation javascript and don’t cause postback.

With the RegularExpressionValidator :
good_start
good_end

With the CustomValidator :

bad_start
bad_end

I think the first reason is enough to not discuss the point !

Custom paging with Subsonic 3 and ObjectDataSource in ASP.NET

Posted April 22nd, 2010 in .NET, ASP.NET, SubSonic by Sam Beauvois

Here is how I created a paged items list in ASP.NET 3.5 using Subsonic for data access, and ASP.NET Controls for the display.

I use Subsonic 3.0.0.4 with ActiveRecord and ASP.NET 3.5 SP1 webforms.

For this example, we are creating a blog post list and that the DAL is created (the data class we use is “DAL.BlogPost”).

The DAL.BlogPost object has three properties we need : CreatedOn, Subject and Body.

First step : prepare your data manager class


namespace MyBlog.BLL
{
   public class PostManager
   {
     // ...
   }
}

(keep in mind the namespace and the class for the nexts steps)

In this class, two methods are required,

The first returns the total number of items


public int GetPostCount()
{
  return DAL.BlogPost.All().Count(x => x.IsDeleted != true);
}

The second one returns the items contained in a defined range (start index and then nomber of elements), so this method needs to know where to start and how many items to get.


public PagedList<DAL.BlogPost> GetLastestPosts(int startRowIndex, int maximumRows)
{
   if (startRowIndex != 0)
   {
     startRowIndex =(startRowIndex/maximumRows);
   }

    IOrderedQueryable<DAL.BlogPost> yourQuery = DAL.BlogPost.All().OrderByDescending(x => x.CreatedOn);
    return new PagedList<DAL.BlogPost>(yourQuery, startRowIndex, maximumRows);
}

One important part to remember when using Subsonic is


if (startRowIndex != 0)
{
   startRowIndex = startRowIndex / maximumRows;
}

This conversion is necessary because subsonic and the ObjectDataSource doesn’t share the same opinion about the paging method.
Subsonic says

“I want the third page containing five elements”

ObjectDataSource says

“I want the five elements following the third element”

So the conversion is mandatory if you want a correct paging.

Second step : create the aspx page.

Declare an ObjectDataSource:

<asp:ObjectDataSource runat="server" ID="postsDataSource"
 TypeName="Example.BLL.PostManager"
 SelectMethod="GetLastestPosts"
 EnablePaging="true"
 SelectCountMethod="GetPostCount" >
 </asp:ObjectDataSource>

Note the TypeName attribute : the value is the namespace + the class name of our datamanager class.
We set the EnablePaging attribute to true and we specify the SelectCountMethod and the SelectMethod.

We don’t need any parameters for the paging, by default the MaximumRowsParameterName and StartRowIndexParameterName are set to “maximumRows” and “startRowIndex”.
If we want other parameters names in our datamanager class, we can define them in the ObjectDataSource with these two parameters.

Add an asp ListView element

<asp:ListView runat="server" ID="postsListview"
     DataSourceID="postsDataSource">
 <LayoutTemplate>
    <asp:PlaceHolder ID="itemPlaceHolder"
         runat="server" />
 </LayoutTemplate>
 <ItemTemplate>
    <h2><%# Eval("CreatedOn","{0:d}")%> :
     <%# Eval("Subject") %></h2>
    <br />
    <p>
     <%# Eval("Body") %>
    </p>
    <hr />
 </ItemTemplate>
</asp:ListView>

Set the DataSourceID attribute to the ObjectDataSource ID parameter.

Define a LayoutTemplate and an ItemTemplate with the elements we want to display.

Then add an asp datapager control in the LayoutTemplate


<LayoutTemplate>

  <asp:DataPager ID="postsDataPager" runat="server"
       PageSize="3">
    <Fields>
      <asp:NextPreviousPagerField ButtonType="Link"
           FirstPageText="<<" PreviousPageText="<"
           ShowNextPageButton="false"
           ShowFirstPageButton="true" />
      <asp:NumericPagerField PreviousPageText="..."
           NextPageText="..." ButtonCount="10" />
      <asp:NextPreviousPagerField ButtonType="Link"
           LastPageText=">>" NextPageText=">"
           ShowPreviousPageButton="false"
           ShowLastPageButton="true" />
     </Fields>
   </asp:DataPager>

   <asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
 </LayoutTemplate>

Here is the result :
blogposts

Remark about the data access method

It’s possible to avoid the start index value conversion !

Check the PagedList first constructor code (code is available on the subsonic git repository)


public PagedList(IQueryable<T> source, int index, int pageSize)
{
   int total = source.Count();
   TotalCount = total;
   TotalPages = total / pageSize;

   if(total % pageSize > 0)
     TotalPages++;

   PageSize = pageSize;
   PageIndex = index;
   AddRange(source.Skip(index * pageSize).Take(pageSize).ToList());
}

As you can see, Subsonic use the Skip and the Take methods from Linq

So, you can modify the GetLastestPosts method to return a strongly typed List in place of the PagedList:


public List<DAL.BlogPost> GetLastestPosts(int startRowIndex, int maximumRows)
{
   return DAL.BlogPost.All().
          Skip(startRowIndex).
          Take(maximumRows).
          OrderByDescending(x => x.CreatedOn).
          ToList<DAL.BlogPost>();
}

With this method, no more conversion needed

Use ASP.NET Validators with the SharePoint:DateTimeControl

Posted March 15th, 2010 in ASP.NET, Development, Sharepoint by Sam Beauvois

If you want to use asp.net validation system with the SharePoint’s DateTimeControl, the first thing you need to know is that this control is “just” a wrapper around simple ASP.NET controls (textbox, dropdownlist).

If you want to set the ControlToValidate property of an ASP.NET validator to the DateTimeControl’s ID, it will throw an error!
As you can guess, you have to target the controls inside the DateTimeControl.

Since the DateTimeControl set the name of the controls programmatically, you can retrieve the id of the control:

  • Textbox date: DateTimeControl.ID +”Date”
  • Dropdownlist hours: DateTimeControl.ID +”DateHours”
  • Dropdownlist minutes: DateTimeControl.ID +”DateMinutes”

Example:

If you use the SharePoint:DateTimeControl with the TimeOnly property set to “true”


<SharePoint:DateTimeControl runat="server" ID="TheTime" TimeOnly="true"/>

You’ll have two dropdownlists, one for the hours and one for the minutes
The DateTimeControl set the name:

Dropdownlist hours: DateTimeControl.ID +”DateHours”

Html will be:

<select name="ctl00$PlaceHolderMain$ctl07$ctl02$TheTime$TheTimeDateHours" id="ctl00_PlaceHolderMain_ctl07_ctl02_TheTime_TheTimeDateHours" dir="ltr">
 <option value="00:">00:</option>
 <option value="01:">01:</option>
 <option value="02:">02:</option>
 <option value="03:">03:</option>
 <option value="04:">04:</option>
 <option value="05:">05:</option>
 <option value="06:">06:</option>
 <option value="07:">07:</option>
 <option value="08:">08:</option>
 <option value="09:">09:</option>
 <option value="10:">10:</option>
 <option value="11:">11:</option>
 <option value="12:">12:</option>
 <option value="13:">13:</option>
 <option value="14:">14:</option>
 <option value="15:">15:</option>
 <option value="16:">16:</option>
 <option value="17:">17:</option>
 <option value="18:">18:</option>
 <option value="19:">19:</option>
 <option value="20:">20:</option>
 <option value="21:">21:</option>
 <option value="22:">22:</option>
 <option value="23:">23:</option>
</select>

Dropdownlist minutes: DateTimeControl.ID +”DateMinutes”
Html will be:

<select name="ctl00$PlaceHolderMain$ctl07$ctl02$TheTime$TheTimeDateMinutes" id="ctl00_PlaceHolderMain_ctl07_ctl02_TheTime_TheTimeDateMinutes" dir="ltr">
 <option value="00">00</option>
 <option value="05">05</option>
 <option value="10">10</option>
 <option value="15">15</option>
 <option value="20">20</option>
 <option value="25">25</option>
 <option value="30">30</option>
 <option value="35">35</option>
 <option value="40">40</option>
 <option value="45">45</option>
 <option value="50">50</option>
 <option value="55">55</option>
</select>

So you can retrieve the ASP.NET controls by using :

  • For the date: TheTime$TheTimeDate
  • For the hours: TheTime$TheTimeDateHours
  • For the minutes: TheTime$TheTimeDateMinutes

Real case example:

I have two DateTimeControl, one named TheStartTime and one other named TheEndTime,

I want to validate that the end time is not before the start time.

So I’ll use the ASP.NET comparevalidator validator to compare the hours:

<asp:CompareValidator id="valDate" runat="server" ControlToValidate="TheEndTime$TheEndTimeDateHours" ControlToCompare="TheStartTime$TheStartTimeDateHours"
 Type="Integer"
 Operator="GreaterThanEqual"
 ErrorMessage="The end time must be greater than the start time">
 </asp:CompareValidator >

Now, if you want to validate both hours and minutes, one option is to add an “asp:CustomValidator” and check conditions server side :
Markup:

<asp:CustomValidator runat="server" ID="EndTimeMustBeLaterThanStartTimeCustomValidator"
EnableClientScript="false" ErrorMessage="Start time must be before end time"/>

Before the action, validate:

if (!IsEndTimeLaterThanStartTime())
{
 EndTimeMustBeLaterThanStartTimeCustomValidator.IsValid = false;
 return;
}

Validation method:


private bool IsEndTimeLaterThanStartTime()
{
 // retrieve the DropDownList
 DropDownList startHours = TheStartTime.FindControl("TheStartTimeDateHours") as DropDownList;
 DropDownList endHours = TheEndTime.FindControl("TheEndTimeDateHours") as DropDownList;

 // prevent from errors
 if (startHours == null ||
 endHours == null ||
 string.IsNullOrEmpty(startHours.SelectedValue) ||
 string.IsNullOrEmpty(endHours.SelectedValue))
 {
 return; // dont' do anything
 }

// the hour value is "00:"
 int startHour = Convert.ToInt32(startHours.SelectedValue.Replace(":",""));
 int endHour = Convert.ToInt32(endHours.SelectedValue.Replace(":", ""));
 if (endHour < startHour)
 {
 args.IsValid = false;
 }
 else
 {

 if (endHour == startHour)
 {
 // check the minutes
 DropDownList startMinutes = TheStartTime.FindControl("TheStartTimeDateMinutes") as DropDownList;
 DropDownList endMinutes = TheEndTime.FindControl("TheEndTimeDateMinutes") as DropDownList;
 // prevent from errors
 if (startMinutes == null ||
 endMinutes == null ||
 string.IsNullOrEmpty(startMinutes.SelectedValue) ||
 string.IsNullOrEmpty(endMinutes.SelectedValue))
 {
 return; // dont' do anything
 }

 int startMinute = Convert.ToInt32(startMinutes.SelectedValue);
 int endMinute = Convert.ToInt32(endMinutes.SelectedValue);

 if (endHour <= startMinute)
 args.IsValid = false;

 }
 }

 args.IsValid = true;

 }

You can also validate the Date himself if you choose to display it,
you can make a client side validation if you know javascript,
you can do everything like in “classic” ASP.NET.

CSS Mixer

Posted February 25th, 2010 in .NET, ASP.NET, CSS, MyProjects, Personal, Tools, Web development by admin

CSSmixer128 Css mixer is a tool I’ve developped in C# winforms, .NET framework 3.5

You can use it to improve you ASP.NET Themes or your CSS files for all your web projects.

If you have many css files linked to a page, you can group them in a single file with CSS Mixer.

You can also minify CSS files to save the bandwith.

 

To use it, open a folder containing CSS files,

cssmixer_screenshot

choose an action (simple combine, or combine and minify) then click Save as …

cssmixer_screenshot2

Download the last version of CSS Mixer on codeplex.

I hope you’ll find it usefull. please give me feedback on it

How to build a “visible on hover” action menu with jQuery ?

Posted February 5th, 2010 in ASP.NET, jQuery, Web development by Sam Beauvois

update 29/11/2010 : please notice that the same functionality could be achieved using css only

Here is a quick way to use jQuery library to create a “visible on hover” action menu.

I’ll explain step by step how to do that.

1.) Create an html page with actions in a container

<html>
 <head>
 <title>Sam Beauvois | jQuery usage demo 1</title>
 </head>
 <body>
 <h2>Visible on hover with jQuery demo.<h2>
 <h3>Please put you mouse hover the following texts</h3>
 <div>
 Demo 1
 <div>
 <a href="#">action 1</a> | <a href="#">action 2</a> | <a href="#">action 3</a>
 </div>
 </div>
 </body>
</html>

Result is

simple page

simple page

2.) Link the jquery library (here from google code, but you can download the library at jquery.com) and add a css class to the container div and one other to the action div


<head>
 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js" ></script>
</head>
<body>
<div>
 Demo 1
 <div>
 <a href="#">action 1</a> | <a href="#">action 2</a> | <a href="#">action 3</a>
 </div>
 </div>
</body>

3.) Add a function :

<script type="text/javascript">
 $(document).ready(function()
 {
    ShowActionOnOver();
    $(".hidden_action",this).hide(); // hide all
 });

 function ShowActionOnOver()
 {
   $(".hidden_action_container").hover(
       function()
       {
          $(".hidden_action",this).show();
       },
       function()
       {
           $(".hidden_action",this).hide();
        }
    );
 }

 </script>

Now it’s functionnal, hover the zone to check :

jquery.visible.on.hover.demo2

4.) Now, add some css to make it nicer :

.hidden_action_container
{
 padding : 15px 15px 15px 15px;
 background-color: #DDDDDD;
 border-style:solid;
 border-color:#EEEEEE;
 height:50px;
}

.hidden_action a
{
 color:#999999;
 font-size: 20px;
 font-weight:bolder;
}
<head>
 <link href="demo.css" type="text/css" rel="stylesheet" />
</head>

And the final result looks like

jquery.visible.on.hover.demo3

You can see the online demo here.

http://www.sambeauvois.be/blog/2010/02/how-to-build-a-visible-on-hover-action-menu-with-css/