Thursday, November 19, 2009

Draw rotated text in VB/ASP.NET using the Matrix class

While working on 2D drawings in VB.NET, you might find yourself in a situation where you may want to rotate a text or any other object on the drawing canvas. I discovered this technique when I was trying to draw my text vertically from bottom to top and the System.Drawing.StringFormat class’s built in vertical text format would only support top to bottom layout. Then I discovered the System.Drawing.Drawing2D.Matrix class which is used for geometric transformation. Using the Matrix class you can rotate the drawing canvas to any desired angle, draw the text/object and then rotate it back to the original position.

Example of rotated text:
Rotated text in VB .NET

I have implemented the drawing by using ASP.NET web application where the drawing is generated by an aspx page. To view the image in web browser, you just need to create an HTML image tag on a new page and set the image source(src) to the .aspx file. The .aspx file code to generate a sample PNG file is given below. The main functionality for rotating the text is implemented in the RotateString subroutine which is provided with the text, rotation angle, and the x and y coordinates.

Imports System.Drawing
Imports System.IO
Imports System.Drawing.Drawing2D

Partial Public Class TextImageDraw
    Inherits System.Web.UI.Page

    'Dimensions of the bitmap
    Const DRAWING_HEIGHT As Integer = 300
    Const DRAWING_WIDTH As Integer = 300

    Dim bitmap As New Bitmap(DRAWING_WIDTH, DRAWING_HEIGHT)
    Dim myDrawing As Graphics = Graphics.FromImage(bitmap)

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim myDrawingBackgroundColor As New SolidBrush(Color.AliceBlue)
        'Fill Drawing Background
        myDrawing.FillRectangle(myDrawingBackgroundColor, 0, 0, DRAWING_WIDTH, DRAWING_HEIGHT)

        myDrawing.TextRenderingHint = Text.TextRenderingHint.AntiAlias

        Dim myText As String = "   web3o.blogspot.com"

        'Text Rotation Cordinates
        Dim xCordinate As Integer = DRAWING_WIDTH / 2
        Dim yCordinate As Integer = DRAWING_HEIGHT / 2

        'Draw Rotation Center
        myDrawing.DrawEllipse(Pens.Black, New Rectangle(xCordinate - 1, yCordinate - 1, 3, 3))

        'Draw Header Text
        Dim headingText As String = "Rotating Text"
        Dim headingFont As Font = New Font("arial", FontSize.XLarge, FontStyle.Regular)
        Dim headingTextWidth As Double = myDrawing.MeasureString(headingText, headingFont).Width
        Dim headingTextHeight As Double = myDrawing.MeasureString(headingText, headingFont).Height
        myDrawing.DrawString(headingText, headingFont, Brushes.Red, _
                             (DRAWING_WIDTH - headingTextWidth) / 2, 2)

        'Draw Text in 360 degrees with 30 degree interval
        For angle = 0 To 330 Step 30
            RotateString(myText, angle, xCordinate, yCordinate)
        Next

        'Draw Footer Text
        myDrawing.DrawString(headingText, headingFont, Brushes.Red, _
                            (DRAWING_WIDTH - headingTextWidth) / 2, _
                            DRAWING_HEIGHT - headingTextHeight - 2)

        'Set smoothing mode for the drawing to Anti-alias
        myDrawing.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias

        'Write the image to browser...
        Response.ClearContent()
        Response.ContentType = "image/png"
        Dim mem As New MemoryStream()
        bitmap.Save(mem, System.Drawing.Imaging.ImageFormat.Png)
        mem.WriteTo(Response.OutputStream)
        myDrawing.Dispose()
        bitmap.Dispose()
    End Sub

    Private Sub RotateString(ByVal Text As String, ByVal angle As Integer, _
                             ByVal x As Integer, ByVal y As Integer)
        Dim myFont As Font = New Font("arial", FontSize.XLarge, FontStyle.Regular)
        Dim myColor As SolidBrush = New SolidBrush(Color.Black)
        ' Make a rotation matrix
        Dim myMatrix As New Matrix
        myMatrix.RotateAt(angle * -1, New Point(x, y)) 'Rotate drawing
        myDrawing.Transform = myMatrix
        myDrawing.DrawString(Text, myFont, myColor, x, y) 'Draw the text string
        myMatrix.RotateAt(angle, New Point(x, y)) 'Rotate back
        myDrawing.Transform = myMatrix
    End Sub

End Class

You can just copy the RotateString subroutine to your code and pass the Graphics class object along with other parameters to draw the rotated string on it.

Multiple File Uploads in ASP.NET with Add/Remove and Up/Down functionalities

HTML or ASP.NET does not allows you to select more then one file in a single file upload input field. To work around this shortcoming you will either have to add dynamic file upload fields by using JavaScript or use a single file upload field to upload the files one by one.

I have devised a nifty technique by using the ASP.NET ListBox control to keep track of the added files and manage them. You can add/remove or change the upload order of the files before uploading them all to your server/database. Sometimes the order of your file upload is also very important and it becomes more easier with this technique where you can arrange them by using the Up/Down controls.

Multiple File Uploads in ASP.NET

The code for the .aspx file is pretty straight forward with a FileUpload control, a ListBox control and buttons to handle all the functionalities. You can even implement this code as an ASP.NET User Control for your project.

Here is the File Upload code for .aspx file:
<table>
<tr>
    <td colspan="2">
    <font color="red"><asp:Label ID="Message" runat="server" Text=""></asp:Label></font>
    </td>
</tr>
<tr>
    <td colspan="2">
    <asp:FileUpload runat="server" id="FileUploader" Width="300"></asp:FileUpload>
    </td>
</tr>
<tr>
    <td>
    <asp:ListBox ID="FileListBox" runat="server" Width="300" Rows="5" SelectionMode="Single" BackColor="AliceBlue">
    </asp:ListBox>
    </td>
    <td>
    <asp:Button ID="Up" runat="server" Text="&uarr;" Font-Bold="true" Font-Size="Large" /><br />
    <asp:Button ID="Down" runat="server" Text="&darr;" Font-Bold="true" Font-Size="Large" /><br />
    </td>
</tr>
<tr>
    <td colspan="2">
    <asp:Button ID="Add" runat="server" Text="Add" />
    <asp:Button ID="Remove" runat="server" Text="Remove" />
    <asp:Button ID="Upload" runat="server" Text="Upload" />
    </td>
</tr>
</table>
<br /><br />
<asp:Literal ID="FileList" runat="server"></asp:Literal>

The backend code in VB is also pretty easy to understand with subroutines to handle the button click events. To keep track of all the files added to the list we use a Listbox control and to store the HttpInputFile objects we use a global ArrayList named Files. For saving the files I have used an example of storing the files in a Database using stored procedure, but you can always save the file to your server itself by using the HIF.PostedFile.SaveAs method. Detailed description for the functionality of each button click event subroutine is given below:

  • Add_Click: Fires when the user browses a file and click on the Add button. Error checking is done to ensure if the FileUpload control has input file, if the file size is not zero and does the file already exists in the list or not. After error checking is passed, the file name is added to to List Box and also to the global ArrayList which holds the object to the HttpInputFile class.
  • Remove_Click: This subroutine simply removes the file name at selected index from the List Box and from the Files Array.
  • Up_Click: The position of the selected item is switched with the item on top of it in the List Box and the Array List.
  • Down_Click: The position of the selected item is switched with the item below it in the List Box and the Array List.
  • Upload_Click: All the files in the Files Array are uploaded to the database using stored procedure.

Here is the VB file code for the File Uploader:

Imports System.Data.SqlClient

Partial Public Class _Default
    Inherits System.Web.UI.Page

    Public Shared Files As ArrayList = New ArrayList()
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    End Sub

    Protected Sub Add_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Add.Click
        Try
            Message.Text = ""
            If FileUploader.HasFile Then 'Check if file exists...
                If FileUploader.PostedFile.ContentLength > 0 Then 'Check file size
                    If FileListBox.Items.Contains(New ListItem(System.IO.Path.GetFileName(FileUploader.PostedFile.FileName))) Then
                        Message.Text = "File already in the list!"
                    Else
                        Files.Add(FileUploader)
                        FileListBox.Items.Add(System.IO.Path.GetFileName(FileUploader.PostedFile.FileName))
                        Message.Text = "Add another file or click Upload to save them all..."
                    End If
                Else
                    Message.Text = "File size cannot be 0!"
                End If
            Else
                Message.Text = "Please select a file to add!"
            End If
        Catch ex As Exception
            ' Handle Error
        End Try
    End Sub

    Protected Sub Remove_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Remove.Click
        If FileListBox.Items.Count > 0 Then
            If FileListBox.SelectedIndex < 0 Then
                Message.Text = "Please select a file to remove!"
            Else
                Files.RemoveAt(FileListBox.SelectedIndex)
                FileListBox.Items.Remove(FileListBox.SelectedItem.Text)
                Message.Text = "File removed..."
            End If
        End If
    End Sub

    Protected Sub Up_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Up.Click
        If FileListBox.Items.Count > 1 AndAlso FileListBox.SelectedIndex > 0 Then
            Dim index As Integer = FileListBox.SelectedIndex
            Dim toMove As String = FileListBox.SelectedValue
            'Move up filename in ListBox
            FileListBox.Items.Insert(index - 1, toMove)
            FileListBox.Items.RemoveAt(index + 1)
            FileListBox.Items.Item(index - 1).Selected = True
            'Move up file in Files ArrayList
            Dim TempFileHandle As FileUpload = Files(index - 1)
            Files(index - 1) = Files(index)
            Files(index) = TempFileHandle
        End If
    End Sub

    Protected Sub Down_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Down.Click
        If FileListBox.Items.Count > 1 AndAlso FileListBox.SelectedIndex < FileListBox.Items.Count - 1 Then
            Dim index As Integer = FileListBox.SelectedIndex + 1
            Dim toMove As String = FileListBox.Items.Item(index).Text
            'Move down filename in ListBox
            FileListBox.Items.Insert(index - 1, toMove)
            FileListBox.Items.RemoveAt(index + 1)
            FileListBox.Items.Item(index).Selected = True
            'Move down file in Files ArrayList
            Dim TempFileHandle As FileUpload = Files(index - 1)
            Files(index - 1) = Files(index)
            Files(index) = TempFileHandle
        End If
    End Sub

    Protected Sub Upload_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Upload.Click
        Dim conn As SqlConnection = New SqlConnection("")
        conn.Open()
        For Each HIF As FileUpload In Files
            FileList.Text += "Uploading file: " & HIF.PostedFile.FileName + "<br />" + vbCrLf
            Dim fileBytes(HIF.PostedFile.InputStream.Length) As Byte
            HIF.PostedFile.InputStream.Read(fileBytes, 0, fileBytes.Length)
            Dim Command As New SqlCommand
            With Command
                .Connection = conn
                .CommandType = CommandType.StoredProcedure
                .CommandText = "upload_files"
                .Parameters.AddWithValue("@id", System.Guid.NewGuid.ToString())
                .Parameters.AddWithValue("@name", System.IO.Path.GetFileName(HIF.PostedFile.FileName))
                .Parameters.AddWithValue("@size", HIF.PostedFile.ContentLength)
                .Parameters.AddWithValue("@type", HIF.PostedFile.ContentType)
                .Parameters.AddWithValue("@file", fileBytes)
                .Parameters.AddWithValue("@dateadded", DateTime.Now())
            End With
            Command.ExecuteNonQuery()
        Next
        conn.Close()
        Message.Text = ""
        'Clear Files ArrayList
        Files.Clear()
        'Clear File List Box
        FileListBox.Items.Clear()
    End Sub
End Class

This implementation contains very minimal amount of code to attain the desired functionalities, you can always add more error checking or styles to suit your website design.

Friday, November 13, 2009

Making Checkboxes behave like Radio Buttons using jQuery

Sometimes you might need Checkboxes to behave like Radio Buttons on your webpage because you may only want zero or one selection from a category of choices. You cannot select 0 or 1 choice from a Radio Button Group because Radio Button groups are meant to select only one choice from a group.

I have written an easy to use and understand code in jQuery for achieving this task. In this example, all the checkboxes with name Colors are assigned a click function (Checkbox_to_RadioButton()) when the page loads for the first time. The Checkbox_to_RadioButton() is a generic function which will automatically uncheck all other other checkboxes with the same name/group.

Too make this work on your page, you just need to paste this JavaScript code in the head section of your webpage and set the Checkbox group name in the page ready function.

JavaScript Code:
<script type="text/javascript" src="http://jqueryui.com/latest/jquery-1.3.2.js"></script>
<script type="text/javascript">
$(document).ready(function(){
      $('input:checkbox[name=Colors]').click(function(){
            Checkbox_to_RadioButton(this);
      });
});
function Checkbox_to_RadioButton(box){
      $('input:checkbox[name=' + box.name + ']').each(function(){
            if (this != box) $(this).attr('checked', false);
      });
}
</script>

HTML Code:
<input type="checkbox" name="Colors" value="Red" />Red<br />
<input type="checkbox" name="Colors" value="Blue" />Blue<br />
<input type="checkbox" name="Colors" value="Green" />Green<br />
<input type="checkbox" name="Colors" value="Yellow" />Yellow<br />