Jon's Blog

.NET Development & More

ASP.NET AJAX Control Toolkit: Bug with Modal Popup Extender

I found a bug in the September 30, 2009 release of the AJAX Control Toolkit.  When you press a button that is inside of a Modal Popup dialog box it will cause a full page postback instead of a partial postback.  When I reverted to the May 13, 2009 release this did not occur.

By the way, I also ran into another weird issue on the September 2009 release where random commas were being inserted into my text box on each postback.  If you are having this issue I also recommend reverting to the May 2009 release.

ASP.NET: Beware of Blank ImageUrl on ImageButton

Earlier today I was debugging a rather tricky issue.  It came down to the fact that an ImageButton control was not getting its ImageUrl property set in certain scenarios.  This would cause ASP.NET to output the following HTML for this control:

<input type="image" name="myName" id="myID" src="" />

 

This was causing the page to request Default.aspx since a src was not explicitly defined*.  So be sure to set the ImageButton control (or its container element) to Visible="false" if you are not setting its ImageURL property.  Otherwise you may have some code-behind logic on Default.aspx executed when you aren't expecting it!

* I assume this is because Default.aspx is set as the default document in IIS.

ASP.NET AJAX: Checking for Partial Postback - IsInAsyncPostBack

My initial thought is always to look for a property on the Page object, but this is actually a property on the ScriptManager.  So if you want to only execute code if it isn't a partial page post back you can do the following:

if (!scriptManagerInstance.IsInAsyncPostBack)
{
    // Do stuff here
}

LINQ Advanced Search

When creating an advanced search you often need to dynamically create the Where statments in your SQL.  Here is an easy way to do this in LINQ.  In the example below orderID, lastName, firstName, shipped are all values retrieved from controls on the page.

var query = from orders in dataContext.MyOrdersTable
select orders;

if (orderID.HasValue)
{
query = query.Where(order => order.OrderID == orderID);
}
if (!string.IsNullOrEmpty(lastName))
{
query = query.Where(order => order.LastName == lastName);
}
if (!string.IsNullOrEmpty(firstName))
{
query = query.Where(order => order.FirstName == firstName);
}
if (shipped.HasValue)
{
query = query.Where(order => order.Shipped == shipped);
}

myGridView.DataSouce = query;
myGridView.DataBind();

Using ASP.NET and GDI+ to Resize Uploaded Image

Below is some sample code I used to resize an uploaded image if the image does not meet the required width and height.  MAX_WIDTH and MAX_HEIGHT below are constants.  Change them as needed.  I am saving the resized image as a JPEG.  It is also a good idea to inform the user that their image was resized.  I removed that code to increase readability.

Note: In this sample the image is being saved to a database, but you could also save it to a file if you wanted to.

if (fileUpload.HasFile)
{
// Get uploaded image from upload control
System.Drawing.Image uploadedImage = System.Drawing.Image.FromStream(fileUpload.PostedFile.InputStream);

// Check width and height of image and resize if necessary
if (uploadedImage.Width > MAX_WIDTH || uploadedImage.Height > MAX_HEIGHT)
{
// Get the factor we must resize by
decimal heightFactor = Decimal.Divide(uploadedImage.Height, MAX_HEIGHT);
decimal widthFactor = Decimal.Divide(uploadedImage.Width, MAX_WIDTH);
decimal resizeFactor = Math.Max(heightFactor, widthFactor);

// Calculate the new height and width using the resize factor
int newHeight = Convert.ToInt32(uploadedImage.Height / resizeFactor);
int newWidth = Convert.ToInt32(uploadedImage.Width / resizeFactor);

Bitmap sourceBitmap = new Bitmap(uploadedImage);
Bitmap newBitmap = new Bitmap(newWidth, newHeight);
Graphics graphics = Graphics.FromImage(newBitmap);
MemoryStream memoryStream = new MemoryStream();

try
{
// Set quality settings and save to MemoryStream object as Jpeg
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, newWidth, newHeight));
newBitmap.Save(memoryStream, ImageFormat.Jpeg);

// Save to Business Object
bizObject.FileData = new Binary(memoryStream.ToArray());
bizObject.FileType = "image/jpeg";
bizObject.FileName = Path.GetFileNameWithoutExtension(fileUpload.FileName) + ".jpg";
}
catch (Exception)
{
throw;
}
finally
{
// Dispose of graphics objects
memoryStream.Dispose();
sourceBitmap.Dispose();
newBitmap.Dispose();
graphics.Dispose();
}
}
else
{
// Image size is correct, so save what was uploaded
bizObject.FileType = fileUpload.PostedFile.ContentType;
bizObject.FileName = fileUpload.FileName;
bizObject.FileData = new Binary(fileUpload.FileBytes);
}

// Other code removed for clarity
}

File Upload: File Extension Validation

This is an easy way to validate the file extension on a FileUpload control.  This way you won't have to postback to the server to validate.  Unless, of course, the user has JavaScript turned off.  The example below is for images (JPEG, GIF, or PNG).

<asp:RegularExpressionValidator ID="regxImage" runat="server" Text="*" 
ErrorMessage
="Image must be a JPEG, GIF, or PNG."
ValidationExpression="(.*?)\.(jpg|JPG|jpeg|JPEG|jpe|JPE|png|PNG|gif|GIF)$"
ControlToValidate="myFileUpload" />

IE8: Image max-width bug

I found a strange IE8 bug dealing with images and the CSS max-width property.  I had an image inside of a table cell.  The image had a max-width set using CSS.  In IE8, if the actual image width was larger than the max-width value then the image was resized properly, but the table cell was not.  It was strange because it worked fine in IE7 and Firefox.  The way I fixed this issue was to wrap the image inside of a div and then set the width of the div to the same value as the max-width of the image.  Oh well, easy enough.

HTML:

<table class="myTable">
    <tr>
        <td class="col1">
            <div class="imageWrapper">
                <img src="images/blah.jpg" class="myImage" />
            </div>
        </td>
        <td class="col2">
            <p>Content here</p>
        </td>
    </tr>
</table>

 

CSS:

table.myTable .col1 { vertical-align: top; width: 122px; }
table.myTable .col2 { vertical-align: top; width: 353px; padding-left: 5px; }

img.myImage { max-width: 122px; max-height: 172px; }
div.imageWrapper { width: 122px;  }

DropDownList: Customized DataTextField

If you try to bind a DropDownList to a DataSouce you will find that you can only use one column as the DataTextField.  The easiest way I have found to overcome this is to manually add the items to the DropDownList like this:

foreach (var item in items)
{
ListItem li = new ListItem(item.FirstName + " " + item.LastName, item.ID);
ddlMyDDL.Items.Add(li);
}

You could also change your query/stored procedure to pull back what you needed displayed in a single column.  But I prefer the above method.

SQL Server Trigger for Row Archive

This trigger will write to an archive table when a row is updated or deleted.  Very useful when you want to know who did what and when.

USE [DatabaseName]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE TRIGGER [dbo].[trigTableName_Archive] 
   ON  [dbo].[TableName]
   FOR UPDATE, DELETE
AS 
BEGIN
    SET NOCOUNT ON;
    
    INSERT INTO TableName_ARCHIVE
        (Column1,
        Column2,
        Column3)
    SELECT
        Column1,
        Column2,
        Column3
    FROM deleted

END

ASP.NET AJAX Web Service Call

So I was working on a project where I needed to have some checkboxes in a GridView.  When checked they needed to update the underlying business object.  My first idea was the just wrap the whole thing in an UpdatePanel, but that would of course require a partial postback with the full contents of the grid being posted back to the server.  The better solution is to call a web service method to do the update.

First, add a new Web Service class to your project.  You need to add the [ScriptService] attribute to this class.  This attribute is from the System.Web.Script.Services namespace.  Then create your WebMethod, including whatever parameters you will need to pass in.

[WebMethod]
public void UpdateFlag(int id, bool isChecked)
{
// Logic to update business object
}

Next you need to add a ServiceReference that points to the web service you just created.  You can add this to your ScriptManager instance.  In my scenario the ScriptManager was in the Master page and I just wanted to include the ServiceReference in this one ASPX page.  Here you can use the ScripManagerProxy object like so (assuming the WebService file is in the same directory as the ASPX file):

<asp:ScriptManagerProxy ID="scriptManProxy" runat="server">
    <Services>
        <asp:ServiceReference Path="MyWebService.asmx" />
    </Services>
</asp:ScriptManagerProxy>

 

Then add the following code to your GridView's RowDataBound event.  This will set the onclick events for the individual checkboxes you are binding to the GridView.

if (e.Row.RowType == DataControlRowType.DataRow)
{
    BusinessObject obj = (BusinessObject)e.Row.DataItem;
    CheckBox myCB = (CheckBox)e.Row.FindControl("myCB");

    string eventText = string.Format("myCB_Checked({0}, this);", obj.SomeID);
    myCB.Attributes["onclick"] = eventText;
}

 

Now you need to actually write the JavaScript method that will call your web service.  Here it is:

<script type="text/javascript">
    function myCB_Checked(id, ctrl) {
        var isChecked = ctrl.checked;
        Namespace.MyWebService.UpdateFlag(id, isChecked);
    }
</script>

 

Make sure that you use the fully qualified name of your class here.