Jim Rogers

Lives in Baton Rouge, LA, with two dogs, one cat, and one lovely wife. I'm a lead developer for GCR Incorporated.

Katrin and Jim

Month List

GridView DataKeys Persistence

by Jim Dec 03, 2007 8:39 AM

In the RowDeleting handler for my GridView, the GridViewDeleteEventArgs.Keys collection is empty. I'm setting the DataKeyNames property correctly - what gives?

I found a number of posts about this issue - the DataKeys are populated by the DataBind() but don't seem to survive postback. But they do survive the postback - they just aren't passed into the GridViewDeleteEventArgs. The keys are still in the GridView itself after postback.

So instead of this:

int ID = int.Parse(((DictionaryEntry)e.Keys[e.RowIndex]).Value.ToString());

I just do this:

int ID = (int)gvSessions.DataKeys[e.RowIndex].Value;

I saw a couple of suggestions to re-bind the data right before doing the delete. This of course ignores the possibility that the data has changed. The wrong record could wind up being deleted, if you're just going by index in the GridView.

Tags: ,

Code

WritableObject

by Jim Nov 13, 2007 4:24 PM

I'm reorganizing my website and getting rid of the page with miscellaneous code. But I would hate for that code to go to the great recycle bin in the sky. That's what blogs are for, right?

So here's the first bit of code, a base class called WritableObject, in VB.NET. It uses reflection to write its members out in ToString(). I've found this useful when debugging stuff and wanting to "watch the data go by," as it were.

Imports System.Text
Imports System.Reflection

 Public Class WritableObject

    'This is an object that is capable of listing the names 
    'and values of its fields and properties.  Derive from 
    'this class, but don't override the tostring() function;
    'the ToString() here will output all the public properties 
    'and fields of the derived object.

    Public Overrides Function toString() As String
        Dim mi() As MemberInfo
        Dim item As MemberInfo
        Dim length As Integer
        Dim obj As Object
        Dim b As StringBuilder = New StringBuilder
        b.Capacity = 5000

        Try
            'Write out the type of object I am
            b.Append(Me.GetType.ToString())
            b.Append(vbCrLf)

            'And write out the fields
            mi = Me.GetType.FindMembers( _
                MemberTypes.Field, _
                BindingFlags.Public Or BindingFlags.Instance, _
                Nothing, _
                Nothing)

            'Look for the longest name, so I can line up the items/properties
            length = 0
            For Each item In mi
                If item.Name.Length > length Then length = item.Name.Length
            Next

            For Each item In mi
                b.Append(item.Name)
                b.Append(New String(" ", length - item.Name.Length))
                b.Append(": ")
                If CType(item, FieldInfo).FieldType.Name = "ArrayList" Then
                    Dim list As ArrayList
                    list = Me.GetType.InvokeMember( _
                        item.Name, BindingFlags.GetField, Nothing, Me, Nothing)
                    If Not list Is Nothing Then
                        For Each o As Object In list
                            b.Append(vbCrLf)
                            b.Append(o.ToString())
                        Next
                    End If
                Else
                    'If item.FieldType.fullName Then
                    obj = Me.GetType.InvokeMember( _
                        item.Name, BindingFlags.GetField, Nothing, Me, Nothing)
                    If Not obj Is Nothing Then b.Append(obj.ToString())
                End If
                b.Append(vbCrLf)
            Next
        Catch ex As Exception
            b.Append("Error writing to string: " + ex.Message)
        End Try

        'Return the constructed string
        Return b.ToString()
    End Function
End Class

Tags:

Code

Null Coalescing Operator

by Jim Sep 23, 2007 7:57 AM

.NET 2.0 has some new language features, among them the null coalescing operator (jQuery15200027220021543247763_1327460644870), which I stumbled on just a few days ago. I need to keep up with my reading.

This is going to be particularly nice when storing properties in the ViewState in ASP.NET.

protected string WidgetName
{
    get { return ViewState["WidgetName"] != null ? (string)ViewState["WidgetName"] : ""; }
    set { ViewState["WidgetName"] = value; }
}

becomes:

protected string WidgetName
{
    get { return (string)ViewState["WidgetName"] ?? ""; }
    set { ViewState["WidgetName"] = value; }
}

For base types like int, things are a little tricky because the comparison must be done with nullable types. We can do this:

protected int WidgetId
{
    get { return (int)(ViewState["WidgetId"] ?? (object)0); }
    set { ViewState["WidgetId"] = value; }
}

Or we can cast our object to a nullable type; the syntax is a little nicer this way. int? is implicitly cast to the return type of int

protected int WidgetId
{
    get { return (int?)ViewState["WidgetId"] ?? 0; }
    set { ViewState["WidgetId"] = value; }
}

Tags:

Code

Truncated HttpWebResponse

by Jim Nov 08, 2006 11:07 PM

Pretend for a second that ReadToEnd() isn't a bad way to parse an HTML file from code. I'm doing a little screen-scraping here, and thinking I had a problem with my regular expression. It took me a while to realize that I didn't have the whole file.

The page is only 4K, and IE and Firefox download the page just fine, showing the full source. Recalling my experiences with downloading shortcut icons, it occured to me that the Content-Length header could be incorrectly reporting the length. Sure enough, that was it.

WebClient and HttpWebRequest (which probably use the same underlying code) both return a stream that is limited to the length in the Content-Length header, regardless of the actual length of the page. I don't think there's a way to get the whole page with these classes. If I want to screen-scrape this, I'll probably need to use lower-level code that can ignore the headers. Bummer.

// Get the html
try
{                        
    strm = Client.OpenRead(page);

    // won't read the whole file if length is short in the header
    Debug.WriteLine(string.Format("Content-Length: {0}",
        Client.ResponseHeaders["Content-Length"]));

    // Doesn't matter if we wrap the stream with reader or call ReadByte() 
    // on the raw stream.
    sr = new StreamReader(strm);
    html = sr.ReadToEnd();
    Debug.WriteLine(html);
    sr.Close();
    
    /*
    // This doesn't work either
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(page);
    HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
    
    strm = resp.GetResponseStream();
    sr = new StreamReader(strm);
    html = sr.ReadToEnd();
    Debug.WriteLine(html.Length);
    sr.Close();
    resp.Close();
    */                        
}

But IE and Firefox work fine! They are programmed to recover from just about any screw up that the server might make. While trying to download website icons, I found that they would recognize an icon if the headers reported an incorrect content type, among other things.

If you point IE at a URL, and a stream comes back, IE will figure out what's going on.

Tags:

Code

System Tray Location

by Jim Nov 04, 2006 11:17 PM

Getting the location of the system tray from vb.net or C# isn't terribly hard. But it isn't documented either, so figuring it out takes a little research. The trick here is knowing the names of the taskbar and system tray windows. This can be discovered using the Spy++ tool. For those of you who don't remember the days before .NET, Spy++ is in your Visual Studio tools folder in the start menu. It was once very handy, but I don't remember the last time I used it.

Private Declare Ansi Function FindWindow Lib "user32" Alias "FindWindowA" ( _
    ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr

Private Declare Ansi Function FindWindowEx Lib "user32" Alias "FindWindowExA" ( _
    ByVal hWnd1 As IntPtr, ByVal hWnd2 As IntPtr, _
    ByVal lpsz1 As String, ByVal lpsz2 As String) As IntPtr

Private Declare Function GetWindowRect Lib "user32.dll" ( _
    ByVal hWnd As IntPtr, ByRef lpRect As RECT) As Boolean

Private Structure RECT
    Public left As Integer
    Public top As Integer
    Public right As Integer
    Public bottom As Integer
End Structure

Private Function GetSystemTrayLocation() As Rectangle
    'Get the location of the system tray

    Dim rect As New RECT
    Dim retval As Rectangle = Nothing

    Dim hTaskbarHandle As IntPtr = FindWindow("Shell_TrayWnd", Nothing)
    If hTaskbarHandle <> 0 Then
        Dim hSystemTray As IntPtr = FindWindowEx(hTaskbarHandle, IntPtr.Zero, _
            "TrayNotifyWnd", Nothing)

        If hSystemTray <> 0 Then
            GetWindowRect(hSystemTray, rect)
            retval = Rectangle.FromLTRB( _
                rect.left, rect.top, rect.right, rect.bottom)
        End If
    End If

    Return retval
End Function

Tags:

Code