Mike Borozdin's Blog

A blog about programming, web and IT in general

Search

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© 2008 Mike Borozdin

Creating a Simple Ad Rotation User Control with LINQ to XML

Download files

Although there is a built-in control for advertisement rotation in ASP.NET, it is capable of showing image ads only. However, in the real life you often have to deal with the ads that require some JavaScript code, for instance, you want to put there AdSense code or want to use Flash banners instead of images. Thus, if you want to rotate complex ads, you have to develop your own control.

In this particular tutorial I'll show you how to create a very simple, but rather functional user control that will randomly show an ad from an XML file of the following format:

<Advertisements>
    <Ad>
        <Html>
            <![CDATA[
                Some HTML code
            ]]>
        </Html>
    </Ad>
    
    <Ad>
        <Html>
            <![CDATA[
                Some other HTML code
            ]]>
        </Html>
    </Ad>
</Advertisements>

 

Well, as you can see, it's very very simple, there is even no NavigateUrl element, but we don't even need, because we can always put a link into the HTML element, besides usually we use something similar to AdSense, so there is no need defining the URL.

The control will accept have only one attribute - AdvertisementFile - which points to an XML file with ads. So, to ad a control, you just have to place a similar code:

<uc1:HtmlAdRotator runat="server" AdvertisementFile="~/App_Data/ads.xml" />

 

Let's start creating it. We should add a new user control to our web site, then put a <div> that will hold the content of advertisements.

<div id="adContent" runat="server"></div>

 

In the code-behind we define the only attribute the user control has, please note that we use a neat feature of C# 3.0 that simplifies creation of properties. In the Page_Load() method we extract the data from the specified XML file and pick-up a random ad from there. We use LINQ to XML for XML file parsing.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Linq;

public partial class HtmlAdRotator : System.Web.UI.UserControl
{
    public string AdvertisementFile { get; set; }
    
    protected void Page_Load(object sender, EventArgs e)
    {
        XDocument xAds = XDocument.Load(Server.MapPath(AdvertisementFile));

        var ads = from a in xAds.Descendants("Ad")
                  select (string)a.Element("Html");

        Random rand = new Random();
        var ad = ads.ElementAt(rand.Next(ads.Count()));
        adContent.InnerHtml = ad;
    }
}

So, with ASP.NET you can easily build your own control suits your needs in a few minutes, while LINQ to XML helps you to extract the data in a very elegant way.

Download files


Posted by Mike Borozdin on Saturday, August 30, 2008 9:43 PM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (3) | Post RSSRSS comment feed

ASP.NET Myths Busted

busted Having read a great variety of forum and blog posts, I met a lot of misconceptions about ASP.NET that definitely made people to turn away from ASP.NET. I won't argue whether ASP.NET is better than PHP (or Ruby on Rails) or not, instead I'll try to bust those myths.

 

 

 

The common myths are:

  • ASP.NET is slow
  • ASP.NET produces non XHTML compliant code
  • ASP.NET validators work in IE only
  • ASP.NET produces dirty HTML code that is full of tables
  • ASP.NET is only about drag-n-drop controls
  • ASP.NET doesn't allow you to use any other databases except Microsoft SQL Server
  • ASP.NET doesn't allow you to use friendly URLs

ASP.NET is slow

That is certainly wrong. This misconception is caused by dynamic compilation, when an ASP.NET application is accessed for the first time it gets compiled unless it wasn't pre-compiled before. This leads to a delay in page loading. However, the next requests are served much faster.

ASP.NET produces non XHTML compliant code

Wrong. In fact, ASP.NET since its 2.0 version produces XHTML compliant code.

ASP.NET Validators work in IE only

First, all ASP.NET validators perform server-side validation that is surely cannot be browser dependent. Second, client-side validation works in any modern browser.

ASP.NET produces dirty HTML code that is full of tables

Well, some ASP.NET controls really output the code that is full of tables, however due to the extensible nature of ASP.NET you can write your own adapters that will extend the current controls and make them produce clean tableless code. Actually, you don't need to write adapters yourself, there is already a good set of ones available for free download.

ASP.NET is only about drag-n-drop controls

No. Although Visual Studio allows you drag controls from the Toolbox in the visual or in the source code, you still can write code yourself, IntelliSense helps a lot with that. Even if you use the drag-n-drop method and the visual tools, you still can see all the changes in the source view. Basically, those tools don't hide anything from you, they just generate the code that you can always view and modify.

ASP.NET doesn't allow you to use any other databases except Microsoft SQL Server

No. You can you any database that has a .NET provider. At the present time, you can find providers for MySQL, Oracle, DB2, PostgreSQL, Informix, Sybase, sqlite.

ASP.NET doesn't allow you to use friendly URLs

No. Just read some tutorials, for example, this one. There is also a module for IIS 7 that provides a mod_rewrite style of URL rewriting.

ASP.NET MVC Framework

If you want to have 100% control over HTML code, then you should definitely have a look at ASP.NET MVC Framework.

Conclusion

I think there are some more misconceptions. Anyway, if you already developer web sites with ASP.NET, then you are likely to know the truth, however if you develop web application with some other tools or want to start web programming, I recommend to take a closer look at ASP.NET. Sometimes, it is not a best option to read opinions on web forums that tend to be biased sometimes. You'd better visit some comprehensive resources, like www.asp.net and msdn.microsoft.com.


Tags: ,
Posted by Mike Borozdin on Wednesday, August 27, 2008 4:29 PM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (6) | Post RSSRSS comment feed

High Quality Image Resizing with .NET

I want to proceed with telling how to deal with uploaded images with ASP.NET. I have already showed how to resize images. However, that resizing method isn't perfect. It doesn't produce images of high quality. Even though it just shrinks images, the quality of resized images don't match the quality of original images, the produced quality usually appears to be worse. Sure, there is a neat solution. We just need to use the HighQualityBicubic Interpolation mode. Sounds difficult? In fact, it's not difficult to implement, just need to add a few lines of code,  but the result is great.

Here are two images, the left is made with a simply resizing method, while the right one is made with a resizing method. Note, that it doesn't have noise on the buildings and the right image has the better contrast.

low high

The code is:

private Bitmap ResizeImage(Stream streamImage, int maxWidth, int maxHeight)
{
    Bitmap originalImage = new Bitmap(streamImage);
    int newWidth = originalImage.Width;
    int newHeight = originalImage.Height;
    double aspectRatio = (double)originalImage.Width / (double)originalImage.Height;

    if (aspectRatio <= 1 && originalImage.Width > maxWidth)
    {
        newWidth = maxWidth;
        newHeight = (int)Math.Round(newWidth / aspectRatio);
    }
    else if (aspectRatio > 1 && originalImage.Height > maxHeight)
    {
        newHeight = maxHeight;
        newWidth = (int)Math.Round(newHeight * aspectRatio);
    }

    Bitmap newImage = new Bitmap(originalImage, newWidth, newHeight);
    
    Graphics g = Graphics.FromImage(newImage);
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
    g.DrawImage(originalImage, 0, 0, newImage.Width, newImage.Height);

    originalImage.Dispose();
    
    return newImage;     
}

You can read more on different interpolation mode here.

You can also download the project files here, they include uploading and high quality resizing.


Posted by Mike Borozdin on Monday, August 25, 2008 9:53 AM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (4) | Post RSSRSS comment feed

ASP.NET Image Uploading (part II)

In the previous tutorial I showed how to handle image uploads, how to validate some things and finally how to resize images retaining their proportions. This time I'll show you how to prevent uploading of files which size exceeds the defined limit. Well, this is not difficult to implement,  however there are some pitfalls I'll tell you about later.

Let's create a validator that will check if the size of an uploaded file is greater than the limit you set. We use a  CustomValidator for this.

<asp:CustomValidator ID="valFileSize" runat="server" 
    ErrorMessage="The image exceeds 10 MB" 
    onservervalidate="valFileSize_ServerValidate" Display="Dynamic" />

 

The method that actually performs the validation looks like this one:

 

protected void valFileSize_ServerValidate(object source, ServerValidateEventArgs args)
{
    if (IsValid)
    {
        int maxSize = 5 * 1024 * 1024;
        if (fileImage.PostedFile.ContentLength > maxSize)
        {
            args.IsValid = false;
        }
    }
}

Why do we check if the page is valid in the validation method? We check it, because the file size validation has sense only if there is an uploaded file and this file is an image. We should ensure that the validator that checks the file size is executed after all other validators, in order to make sure we should just put the corresponding CustomValidator after the other ones. On the other hand, we can have other fields on the form that we also want to validate and those fields are not connected to file uploading, in this case we can use a boolean field that will indicate whether file uploading is correct or not.

Well, if you run the web site and upload an image that is larger than 4 MB, you are likely to get a server error. This error is caused by uploading a file which size is larger than the maximum allowed size that is usually set to 4 MB. In order to extend that amount, you must correct Web.config, basically you need to set the maxRequestLength attribute of the httpRuntime element.

<httpRuntime maxRequestLength="20480"/>

 

In this example the maximum uploaded file size is set to 20 MB, because you set the value in KB, so 20480 = 20 * 1024.

You may also want to to set the value of the executionTimeout which will allow longer uploads.

However, you still cannot prevent a server from displaying an error if an uploaded file is larger than the value in Web.config, so it's recommended to set a deliberately large value and validate the file size programatically.


Posted by Mike Borozdin on Sunday, August 24, 2008 4:21 PM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (1) | Post RSSRSS comment feed

Correction to the Article on ASP.NET Image Uploading

I apologize, there were few mistakes in the article on ASP.NET Image Uploading. There was a logic error in the resizing method. Furthermore, I decided to use the <asp:FileUpload /> control instead of the HTML input, FileUpload automatically adds a proper encryption type to the form.

The article is still available by its previous URL, but now it's correct, as well as the project files.


Posted by Mike Borozdin on Saturday, August 23, 2008 3:47 PM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (0) | Post RSSRSS comment feed

PowerCommands Cause Visual Studio 2008 SP1 Crash

I was badly surprised today, when Visual Studio 2008 SP1 suddenly quit while loading the "Choose Toolbox Items" dialog. After some Googling I learnt that such things were caused by PowerCommands, thanks to the guys here. However, the workaround is pretty simply, you just have to re-install PowerCommands. After that procedure Visual Studio starts working properly again.


Posted by Mike Borozdin on Friday, August 22, 2008 5:07 PM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (1) | Post RSSRSS comment feed

ASP.NET Image Uploading (part I)

Download files

Introduction

Is image uploading different from simply file uploading? No, but when creating an image uploading functionality you usually have to perform more operation, than if you application were to receive text files only. Basically, when making an image uploading form, you have to implement the following things:

  • Check if a file is uploaded
  • Check if the uploaded file is an image
  • Check if the uploaded file doesn't exceed a certain size
  • Resize the uploaded images without changing the proportions of the picture
  • Save the image

In this particular tutorial, I'll tell you how to perform the 3 operations, while I'll explain how to check the file size in the next part.

Form

We need to create a form element that is used for choosing a file, we can do it by using the FileUpload control.

<asp:FileUpload ID="fileImage" runat="server" />

After that, we add two CustomValidators that will validate if file upload and the file type, we also add a submit button:

<asp:CustomValidator ID="valFile" runat="server" 
    ErrorMessage="No file uploaded" 
    onservervalidate="valFile_ServerValidate" Display="Dynamic" />
<asp:CustomValidator ID="valFileType" runat="server" 
    ErrorMessage="This isn't not an image" 
    onservervalidate="valFileType_ServerValidate" Display="Dynamic" /><br />
    
<asp:Button ID="btnSubmit" runat="server" Text="Upload" 
    onclick="btnSubmit_Click" />

Validation

Since, we cannot access the file input with JavaScript, we can perform only server-side validation. First, we should validate if a user has uploaded a file.

protected void valFile_ServerValidate(object source, ServerValidateEventArgs args)
{
    if (!fileImage.HasFile)
    {
        args.IsValid = false;
    }
}

There are two ways of validating the file type, the first one is to check the extension and the second one is to check the mime/type. The first one isn't very reliable, because a smart user can change the file extension, besides there are lot of extensions of JPEG images - .jpg, .jpeg, .jpe, while the mime/type is the only - "image/jpeg". So, we'll be using the second way, i.e. we will be checking the mime type. Here is the code we should write:

protected void valFileType_ServerValidate(object source, ServerValidateEventArgs args)
{
    if (fileImage.HasFile)
    {
        string[] acceptedTypes = new string[] 
    { 
        "image/bmp", 
        "image/jpeg", 
        "image/tiff", 
        "image/gif", 
        "image/png" 
    };

        if (!acceptedTypes.Contains(fileImage.PostedFile.ContentType))
        {
            args.IsValid = false;
        }
    }
}

There is an interesting thing in this code, we use the Contains() extension method, that checks if a string belongs to a collection.

Processing Image Upload

In order to process image uploading we should simply add some code to the submit button click event handler:

protected void btnSubmit_Click(object sender, EventArgs e)
{
    if (IsValid)
    {
        Bitmap image = ResizeImage(fileImage.PostedFile.InputStream, 200, 400);
        image.Save(Server.MapPath("~/Photos/") + Guid.NewGuid().ToString() + ".jpg", ImageFormat.Jpeg);
    }
}

Resizing

This is the most interesting part of the tutorial. We won't simply resize images, but we also retain their proportions. Here is the code: 

private Bitmap ResizeImage(Stream streamImage, int maxWidth, int maxHeight)
{
    Bitmap originalImage = new Bitmap(streamImage);
    int newWidth = originalImage.Width;
    int newHeight = originalImage.Height;
    double aspectRatio = (double)originalImage.Width / (double)originalImage.Height;

    if (aspectRatio <= 1 && originalImage.Width > maxWidth)
    {
        newWidth = maxWidth;
        newHeight = (int)Math.Round(newWidth / aspectRatio);
    }
    else if (aspectRatio > 1 && originalImage.Height > maxHeight)
    {
        newHeight = maxHeight;
        newWidth = (int)Math.Round(newHeight * aspectRatio);
    }
    
    return new Bitmap(originalImage, newWidth, newHeight);
}

Now, you can compile the project and test it by uploading some of new images and seeing the resized ones on the server.

Conclusion

Image uploading is very common problem. ASP.NET provides great tools for developers to accomplishing this task. In the next part, I'll show you how to validate the file size that can be very tricky sometimes.

Download files


Posted by Mike Borozdin on Thursday, August 21, 2008 12:42 PM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (2) | Post RSSRSS comment feed

Static Page Methods Instead of Web Services in ASP.NET AJAX Control Toolkit

As you may already know, it is required to create a web service to make use of some ASP.NET AJAX Control Toolkit controls. However, you can use static methods of your pages instead, you just have to mark them with the [System.Web.Services.WebMethod] attribute and not set the ServicePath parameter in the ACT controls.

In the example from my article on CascadingDropDown we used a web service for populating the drop-down lists, now we'll use static methods instead, we can just copy-paste the methods from the web service to the code-behind file of the page and remove the ServicePath parameters from the controls.

Extract from Default.aspx.cs

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    [System.Web.Services.WebMethod]
    public static CascadingDropDownNameValue[] GetContinents(string knownCategoryValues, string category)
    {
        CascadingDropDownNameValue[] continents = new CascadingDropDownNameValue[2];
        continents[0] = new CascadingDropDownNameValue("North America", "North America");
        continents[1] = new CascadingDropDownNameValue("Europe", "Europe");
        return continents;
    }
 
Extract from Default.aspx
<%--No ServicePath attribute here--%>
<cc1:CascadingDropDown ID="cddContinent" runat="server" TargetControlID="ddlContinent" 
    Category="continent" ServiceMethod="GetContinents" />

If you don't need to expose the methods consumed by the ACT controls, then you can use static methods instead of web services.


Posted by Mike Borozdin on Sunday, August 17, 2008 8:15 PM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (1) | Post RSSRSS comment feed

.aspx extension instead of .php? WHY???

This story is not a debate on "ASP.NET vs PHP", but this story is about the people who develop their web applications with PHP, but pretend that they are using ASP.NET. Yes, such people do really exist. I came across their posts in the forums, where they said that or asked how to change the extension of PHP scripts to .aspx.

Why on Earth are they doing that? Basically, they want to look more serious. For some reason, .aspx looks more sophisticated, serious and more enterpisy. The reason is actually well known, ASP.NET usually powers well known companies that everybody look up to.

However, often it's not the developers who want to "be more serious", but their clients who either demand the pages to have the ".aspx" extension or want to the web site to be written with ASP.NET. The latter is the worst case, I believe, because in such a situation, the developer lies to his client. He was hired to build a web site with ASP.NET, but in fact he does with PHP or with something else. I think it is really a lack of work ethic.

This post is partly inspired by the Rick Strahl's article, which is called "ASP.NET gets no Respect", where he puts his thoughts regarding that fact that people and especially the young developers, who are eager to launch their start-up project, simply disregard ASP.NET and prefers to use a more fashionable platform, like Ruby on Rails. While I agree with him on this point, there are people do an opposite thing - write their web sites with PHP and put the ".aspx" extension at the end of their files. <irony>Well, the world is really ridiculous...</irony>


Tags: , ,
Posted by Mike Borozdin on Friday, August 15, 2008 2:54 PM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (17) | Post RSSRSS comment feed

Creating Linked Drop-Down Lists with the CascadingDropDown Control

Download the project files

Creating dependant drop-down lists is a very common task, for example, when making a registration form, you usually have to add a country and a state fields, the contents of the state drop-down depends on the selected item in the country drop down field. Actually, it's not so difficult to implement that with the standard controls of ASP.NET. However if you don't want the page to be reloaded every time a user selects a value in the drop-down list, you should make use of AJAX. While you can use UpdatePanel for this task, it will be difficult if you have 3 or even linked drop-downs. Hopefully, you don't have to add a lot of UpdatePanels, instead you can pick up the CascadingDropDown Control from ASP.NET AJAX Control Toolkit.

You can see the demo here.

CascadingDropDown is an extender for the standard DropDown control that retrieves the data from a web service. In the example, we'll create 4 linked drop-downs - for the continent, for the country, for the territory and for the city.

Continent: <asp:DropDownList ID="ddlContinent" runat="server" />
<cc1:CascadingDropDown ID="cddContinent" runat="server" TargetControlID="ddlContinent" 
    Category="continent" ServicePath="Helper.asmx" ServiceMethod="GetContinents" />
<br />
Country: <asp:DropDownList ID="ddlCountry" runat="server" />
<cc1:CascadingDropDown ID="ccdCountry" runat="server" TargetControlID="ddlCountry" 
    ParentControlID="ddlContinent" Category="country" 
    ServicePath="Helper.asmx" ServiceMethod="GetCountries" />
<br />
Territory: <asp:DropDownList ID="ddlTerritory" runat="server" />
<cc1:CascadingDropDown ID="ccdTerritory" runat="server" TargetControlID="ddlTerritory" 
    ParentControlID="ddlCountry" Category="territory" 
    ServicePath="Helper.asmx" ServiceMethod="GetTerritories" />
<br />
City: <asp:DropDownList ID="ddlCity" runat="server" />
<cc1:CascadingDropDown ID="ccdCity" runat="server" TargetControlID="ddlCity" 
    ParentControlID="ddlTerritory" Category="city" 
    ServicePath="Helper.asmx" ServiceMethod="GetCities" />

 

I must explain some attributes we have here:

ParentControlID - the ID of the parent drop-down control

Category - the key of the collection we parse in a web service. I'll explain it below.

ServicePath - the path to a web service the control gets the data from

ServiceMethod - the name of the method the control invokes to retrieve the data

Let's proceed to the web service now. First, we should mark it with ScriptService attribute:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
[System.Web.Script.Services.ScriptService]
public class Helper : System.Web.Services.WebService

 

Then, the methods we are going to invoke must have the following signature:

 

[WebMethod]
public CascadingDropDownNameValue[] MethodName(string knownCategoryValues, string category)

 

The parameters names should match as well.

knownCategoryValues - contains the values of the all chosen cascading-drop downs. It can be transformed to a collection by CascadingDropDown.ParseKnownCategoryValuesString() method, which return the StringDictionary collection, the keys of the collection math the Category attributes of CascadingDropDowns.

category - contains the value of the Category atttribute of the CascadingDropDown that invokes the current method.

Some sample methods:

[WebMethod]
public CascadingDropDownNameValue[] GetContinents(string knownCategoryValues, string category)
    {
        CascadingDropDownNameValue[] continents = new CascadingDropDownNameValue[2];
        continents[0] = new CascadingDropDownNameValue("North America", "North America");
        continents[1] = new CascadingDropDownNameValue("Europe", "Europe");
        return continents;
    }

[WebMethod]
public CascadingDropDownNameValue[] GetCountries(string knownCategoryValues, string category)
{
    StringDictionary ddlData = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
    string continent = ddlData["continent"];

    List<CascadingDropDownNameValue> countries = new List<CascadingDropDownNameValue>();

    if (continent == "North America")
    {
        countries.Add(new CascadingDropDownNameValue("United States", "United States"));
        countries.Add(new CascadingDropDownNameValue("Canada", "Canada"));
    }
    else if (continent == "Europe")
    {
        countries.Add(new CascadingDropDownNameValue("United Kingdom", "United Kingdom"));
        countries.Add(new CascadingDropDownNameValue("France", "France"));
    }
    else
    {
        countries.Add(new CascadingDropDownNameValue("N/A", "N/A"));
    }

    return countries.ToArray();
}

 

You can download the entire project here.

Conclusion

CascadingDropDown is a very powerful AJAX enabled control that should be used when you have two or more linked drop-down lists. You can read more here and there.


Posted by Mike Borozdin on Wednesday, August 13, 2008 8:34 PM GMT
Bookmark on MSDN   Add to Technorati Favorites  Kick it!  
Permalink | Comments (1) | Post RSSRSS comment feed