Main Page | Türkçe Blog | Delete Language Cookie 
Daron Yöndem - December, 2006
a developers draft...
 Friday, December 22, 2006

How to capture screenshot of a web site as a Thumbnail with ASP.NET 2.0? Capturing screenshots with Windows application are very easy but how to capture a web sites screenshot? Imagine we load the given web site in our application and take a screenshot of it. To load the needed web site we can use WebBrowser component of .NET 2.0 Framework and use .DrawToBitmap method to get a bitmap picture of current web site. The scenario works well in Windows applications but how we do this in a web site?

Never forget that ASP.NET and Windows Forms applications are all about .NET Framework. So theoretically we can do all the things in an ASP.NET application like we do in Windows Forms. The idea behind our solution will be to initialize a hidden WebBrowser component and load our web site inside it to be able to get screenshot. Of course we will fight against many further problem.

Dim MyBrowser As New WebBrowser
MyBrowser.ScrollBarsEnabled = False
MyBrowser.Size = New Size(1027, 768)
MyBrowser.Navigate(”http://www.deveload.com”)
Dim myBitmap As New Bitmap(1024, 768)
Dim DrawRect As New Rectangle(0, 0, 1024, 768)
MyBrowser.DrawToBitmap(myBitmap, DrawRect)

After defining our WebBrowser variable we set some properties like ScrollBarsEnabled to false and the size of our browser. We call navigate method and open the web site “www.deveload.com”. On next we define our BitMap variable, drawing surface and use DrawToBitmap method to get our screenshot.

It seems all ok but if you run this application you will get many errors. First problem is that we didn’t wait for the web site to get loaded. We just took an instant screenshot after calling the web site. Normally we should wait for the web site to be loaded totally.

MyBrowser.Navigate(Me.URL)
While MyBrowser.ReadyState <> WebBrowserReadyState.Complete
     Application.DoEvents()
End While

After starting to load our web site we will just take a dummy loop to wait the web site to be loaded totally. Why we are using Application.DoEvents() is a trick I will explain later on. An other problem is that our WebBrowser control is actually a COM object. We need to be in a Single Threaded process to be able to use WebBrowser freely. All of our screen taking code should be handled in an independent thread.

Dim NewTh As New Threading.Thread(AddressOf DoIt)
NewTh.SetApartmentState(Threading.ApartmentState.STA)
NewTh.Start()

Let’s get back to our last code where we used a loop to wait our WebBrowser control. Think about it again. We have got a single thread and going in a loop to wait our web site to load. Normally our loop would lock our application and thread would wait for the loop to finish to be able to render our WebBrowser component further on. So why isn’t that happening like that? Application.DoEvents() is the answer. This method will let us to render our control while our loop is in process.

Finnaly we will use a classical approach to resize our screenshot to a thumbnail size.

Dim imgOutput As System.Drawing.Image = myBitmap
Dim oThumbNail As System.Drawing.Image = New Bitmap(twidth, theight, imgOutput.PixelFormat)
Dim g As Graphics = Graphics.FromImage(oThumbNail)
g.CompositingQuality = Drawing2D.CompositingQuality.HighSpeed
g.SmoothingMode = Drawing2D.SmoothingMode.HighSpeed
g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBilinear
Dim oRectangle As Rectangle = New Rectangle(0, 0, twidth, theight)
g.DrawImage(imgOutput, oRectangle)

In the code above we define imgOutput and assign our original image to it, which is named myBitmap. twidth, theight are our target size variables. We create a grapics object from a dummy image having our destination size. We set some properties of graphics object and make the render with DrawImage method.

Now let’s see the complete solution as an independent Class.

Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Windows.Forms
Imports System.Diagnostics
 
Namespace GetSiteThumbnail
 
    Public Class GetImage
        Private S_Height As Integer
        Private S_Width As Integer
        Private F_Height As Integer
        Private F_Width As Integer
        Private MyURL As String
 
        Property ScreenHeight() As Integer
            Get
                Return S_Height
            End Get
            Set(ByVal value As Integer)
                S_Height = value
            End Set
        End Property
 
        Property ScreenWidth() As Integer
            Get
                Return S_Width
            End Get
            Set(ByVal value As Integer)
                S_Width = value
            End Set
        End Property
 
        Property ImageHeight() As Integer
            Get
                Return F_Height
            End Get
            Set(ByVal value As Integer)
                F_Height = value
            End Set
        End Property
 
        Property ImageWidth() As Integer
            Get
                Return F_Width
            End Get
            Set(ByVal value As Integer)
                F_Width = value
            End Set
        End Property
 
        Property WebSite() As String
            Get
                Return MyURL
            End Get
            Set(ByVal value As String)
                MyURL = value
            End Set
        End Property
 
        Sub New(ByVal WebSite As String, ByVal ScreenWidth As Integer, ByVal ScreenHeight As Integer, ByVal ImageWidth As Integer, ByVal ImageHeight As Integer)
            Me.WebSite = WebSite
            Me.ScreenWidth = ScreenWidth
            Me.ScreenHeight = ScreenHeight
            Me.ImageHeight = ImageHeight
            Me.ImageWidth = ImageWidth
        End Sub
 
        Function GetBitmap() As Bitmap
            Dim Shot As New WebPageBitmap(Me.WebSite, Me.ScreenWidth, Me.ScreenHeight)
            Shot.GetIt()
            Dim Pic As Bitmap = Shot.DrawBitmap(Me.ImageHeight, Me.ImageWidth)
            Return Pic
        End Function
    End Class
 
    Class WebPageBitmap
        Dim MyBrowser As WebBrowser
        Dim URL As String
        Dim Height As Integer
        Dim Width As Integer
        Dim Ready As Boolean
 
        Sub New(ByVal url As String, ByVal width As Integer, ByVal height As Integer)
            Me.Height = height
            Me.Width = width
            Me.URL = url
            MyBrowser = New WebBrowser
            MyBrowser.ScrollBarsEnabled = False
            MyBrowser.Size = New Size(Me.Width, Me.Height)
        End Sub
 
        Sub GetIt()
            MyBrowser.Navigate(Me.URL)
            While MyBrowser.ReadyState <> WebBrowserReadyState.Complete
                Application.DoEvents()
            End While
        End Sub
 
        Function DrawBitmap(ByVal theight As Integer, ByVal twidth As Integer) As Bitmap
            Dim myBitmap As New Bitmap(Width, Height)
            Dim DrawRect As New Rectangle(0, 0, Width, Height)
            MyBrowser.DrawToBitmap(myBitmap, DrawRect)
            Dim imgOutput As System.Drawing.Image = myBitmap
            Dim oThumbNail As System.Drawing.Image = New Bitmap(twidth, theight, imgOutput.PixelFormat)
            Dim g As Graphics = Graphics.FromImage(oThumbNail)
            g.CompositingQuality = Drawing2D.CompositingQuality.HighSpeed
            g.SmoothingMode = Drawing2D.SmoothingMode.HighSpeed
            g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBilinear
            Dim oRectangle As Rectangle = New Rectangle(0, 0, twidth, theight)
            g.DrawImage(imgOutput, oRectangle)
            Try
                Return oThumbNail
            Catch ex As Exception
            Finally
                imgOutput.Dispose()
                imgOutput = Nothing
                MyBrowser.Dispose()
                MyBrowser = Nothing
            End Try
        End Function
    End Class
 
End Namespace

And the last part, how to use our class in our web application:

Partial Class _Default
    Inherits System.Web.UI.Page
 
    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim NewTh As New Threading.Thread(AddressOf DoIt)
        NewTh.SetApartmentState(Threading.ApartmentState.STA)
        NewTh.Start()
    End Sub
 
    Sub DoIT()
        Try
            Dim thumb As New GetSiteThumbnail.GetImage("http://www.deveload.com", 1024, 768, 320, 240)
            Dim x As System.Drawing.Bitmap = thumb.GetBitmap()
            x.Save("C:\Inetpub\wwwroot\screeny\deveload.jpg")
        Catch ex As Exception
            Dim y As System.IO.StreamWriter = System.IO.File.CreateText("C:\Inetpub\wwwroot\screeny\error.txt")
            y.WriteLine(ex.Message & vbCrLf & ex.Source)
            y.Flush()
            y.Close()
        End Try
    End Sub
End Class

The code above is creating an instance of our Class and trying to get a screenshot. If any error happens it will create a text file and write the error inside it. Do not forget that the class DoIT is working as a separated thread.

Happy X-Mas ;)

Friday, December 22, 2006 1:51:27 PM (GMT Standard Time, UTC+00:00)  #    Comments [3]   ASP.NET 2.0  | 
 Thursday, December 21, 2006

I know this is a really strange title up here but that was the first question coming into my mind when I heard Microsoft chooses to use JSON (JavaScript Object Notation) in ASP.NET AJAX Extension to move data between server and client. Actually this decision is of course a very clever decision, because JSON provides better bandwidth conservation. Microsoft has added JSON-based serialization classes to ASP.NET AJAX recently, but for our example we will use an online conversion tool at http://www.thomasfrank.se/xml_to_json.html

We will use the default example coming from our online convertor. The following is the xml data we will convert to JSON.

<animals>
     <dog>
          <name>Rufus</name>
          <breed>labrador</breed>
     </dog>
     <dog>
          <name>Marty</name>
          <breed>whippet</breed>
     </dog>
     <cat name="Matilda"/>
</animals>

And this is the converted JSON data.

{
    animals:{
        dog:[
            {
                name:'Rufus',
                breed:'labrador'
            },
            {
                name:'Marty',
                breed:'whippet'
            }
        ],
        cat:{
            name:'Matilda'
        }
    }
}

It seems like JSON data is longer but that's not. If we just save the XML data in a XML file and JSON in a JavaScript file; (do not forget to save data after eliminating all spaces in data which are only added to be more human readable) XML data is 162 byte and JSON is 133 byte. The difference between two formats data sizes will be higher on higher data amount.

An other positive side of JSON is that JSON is ready to be used in JavaScript. In JavaScript, XML must be parsed into DOM. Once we have constructed the DOM tree, we still have to loop through it to create corresponding JavaScript objects. In JSON we just get our data and use eval JavaScript function and we get all our data in JavaScript objects.

That’s why Microsoft chooses to use JSON in his AJAX (Asynchronous JavaScript and XML) Extension for ASP.NET. But where is the XML part of AJAX? :) That’s gone. We got a better solution that actually doesn’t have any brand-new name, better just carrying the well-known old name AJAX.

Thursday, December 21, 2006 9:37:08 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   AJAX  | 
 Sunday, December 17, 2006

Actually that's a very cool question. After having Microsoft SQL Express free edition, the default Membership Provider for .NET 2.0 has been changed to SQL2005. What if we need to use the old style access databases. There can be possible situations where you just need to handle 10 or maybe only 5 users in your web application and even that is free using an SQL database could be unneeded.

The providers contained in ASP.NET 2.0 Beta 1 have been imported to an independant project to serve the same functionality from the ASP.NET 2.0 Beta 1. You can just download the C# project from this web site or direct download from this url. The project is the source code imported from Beta 1, so you will need to compile the project to be able to use in different projects. The download packet includes needed Web.Config sample setting file as well.

To be able to use Access Membership Provider, first you need to compile the provider and add the generated DLL file as a reference to your project. Secondly add provided Access database to your project or just import all tables in given database to your own database. Finally add all the settings in given sample Web.Config file to your own Web.Config. Do not forget to edit your own Connection String for your access file. Now you can just start to use your access database for all Membership Provider needed functionalities like Role Manager, Profile and Web Part Personalization.

A little note; go to WebSite / ASP.NET Configuration menu in your Visual Studio 2005 to be able to manage users and roles.

Sunday, December 17, 2006 12:36:56 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]   ASP.NET 2.0  | 
Copyright © 2010 Daron Yöndem. Tüm hakları saklıdır.