December 2005 - Posts

Recently, friend Paul Sheriff sent this email:

Remember in VB6 we had the Me.Print and it would print the bitmap of the form? Is there anyway to do that in VB.NET?

I sat on it for a while, knowing that it would be fun to work out, once I got to it. Turns out, in VS 2005, it's really easy. The Control class adds a DrawToBitmap method, so you can create a method like this to do the work:

Public Function GetFormImage(ByVal frm As Form) As Bitmap
  Dim bmp As Bitmap
  Using g As Graphics = Me.CreateGraphics()
    Dim sz As Size = frm.Size
    bmp = New Bitmap(sz.Width, sz.Height, g)
    frm.DrawToBitmap(bmp, New Rectangle(0, 0, sz.Width, sz.Height))
  End Using
  Return bmp
End Function

Pass in a form reference, and you've got yourself a bitmap of the form's contents. You could do this in 2003, but it required an ugly call to the BitBlt API method (I borrowed and modified code from MSDN for this portion):

' One of the many constants that modify
' the behavior of the BitBlt API:
Private Const SRCCOPY = &HCC0020 ' (DWORD) dest = source

Private Declare Function BitBlt _
 Lib "gdi32" Alias "BitBlt" _
 (ByVal hDestDC As IntPtr, _
 ByVal x As Integer, ByVal y As Integer, _
 ByVal nWidth As Integer, ByVal nHeight As Integer, _
 ByVal hSrcDC As IntPtr, _
 ByVal xSrc As Integer, ByVal ySrc As Integer, _
 ByVal dwRop As Integer) As Integer

Public Function GetFormImage(ByVal frm As Form) As Bitmap
  Dim bmp As Bitmap
  Dim formGraphics As Graphics
  Dim memoryGraphics As Graphics
  Dim dc1 As IntPtr
  Dim dc2 As IntPtr

  Try
    formGraphics = frm.CreateGraphics()

    ' Create a bitmap big enough to contain
    ' the entire form, and retrieve a Graphics
    ' object that can work with the bitmap:
    Dim sz As Size = frm.Size
    bmp = New Bitmap(sz.Width, sz.Height, formGraphics)
    memoryGraphics = Graphics.FromImage(bmp)

    ' Retrieve the two drawing context handles (HDCs)
    ' dc1 represents the form
    ' dc2 represents the new bitmap
    dc1 = formGraphics.GetHdc()
    dc2 = memoryGraphics.GetHdc()

    ' Copy the image to dc2,
    ' using rectange 0, 0, sz.Width, sz.Height
    ' from dc1
    ' copy from the upper-left corner
    BitBlt(dc2, _
       0, 0, _
       sz.Width, sz.Height, _
       dc1, _
       -1 * SystemInformation.FrameBorderSize.Width, _
       -1 * (SystemInformation.FrameBorderSize.Height + _
       SystemInformation.CaptionHeight), SRCCOPY)

  Catch ex As Exception
    MessageBox.Show(ex.Message)

  Finally
    If Not formGraphics Is Nothing Then
      formGraphics.ReleaseHdc(dc1)
      formGraphics.Dispose()
    End If
    If Not memoryGraphics Is Nothing Then
      memoryGraphics.ReleaseHdc(dc2)
      memoryGraphics.Dispose()
    End If
  End Try

  Return bmp
End Function

It's ugly. It's complex. But it works! If you need to create a screen dump of a form, either in 2003 or 2005, it's possible. Just takes a little digging...

Posted by KenG | 1 comment(s)

Yes, I know. I am just about the worst blogger on earth. Would you believe that I've been on the road pretty much for three months? I know, you're feeling terribly sorry for little ol' me.

Anyway, over the past two weeks I've handled a problem for myself (while recording training videos) and then for two friends, and I figured if we're all having the same trouble, others must be, as well.

Here's the scenario (it's an ASP.NET 2.0 thing):

You create a SqlDataSource retrieving data. You create a GridView control, and bind the GridView control to the SqlDataSource control. All is good--you see all the rows. You can enable editing, make changes, and you look like a winner.

Then, you decide that the SqlDataSource shouldn't be retrieving all the columns from the data source, so you modify the data source to retrieve fewer columns.

Go back to your page, run it, and suddenly, the GridView control isn't showing any data! WTF?

Apparently, late in the beta, Microsoft changed this behavior. Now, if you modify the data source, you'll need to re-add the fields you want displayed within the GridView control yourself. You could have the control auto-populate its fields, but then you'll have to do extra work if you want to convert any of the columns into template fields.

The fun isn't over, however. Now, you can view the data, and you can even edit data. But your changes won't save! (And here is where I spent around 4 hours one night, apparently because I'm stupider than most.)

The solution is (after much trial and error) that when you modified the data source, and stripped the GridView control of all its fields, you also reset the GridView control's DataKeyNames property. In order for the automatic edit/save mechanism to work, the GridView control needs to know what its data's primary key field(s) is/are, and without a value in the DataKeyNames property, it hasn't a clue.

Therefore, the solution is simple (once you know it). You must also reset the DataKeyNames property of the GridView control.

The moral of this story, kids? Just 'cause something works late in the beta doesn't mean it works when the product ships. Oops, did I say that?

Posted by KenG | with no comments
Filed under: