Jan Schreuder on .Net

.Net code samples, experiences, observations

View my professional profile on LinkedIn

Recent Posts

Tags

News

  • Inappropriate comments will be deleted at my discretion.

    The information and code samples in this weblog is provided "AS IS" without warranty of any kind, either expressed or implied, including but not limited to the merchantability and/or fitness for a particular purpose.

Community

Email Notifications

Tool suppliers

Tools

General

Microsoft

Favorite blogs

Archives

March 2004 - Posts

Memory management and databindings

I was reading this article on MSDN Field Views on Windows Forms 1.0 and 1.1 Data Binding and bumped into a section about databinding and memory leaks. As you may know, I've struggled with this in my first .Net project. Steve White, the author of this article, explains what happens to datasource that are bound to controls and why they are not garbage collected. I have not really looked at how it works, and how to further implement the suggestions made here, but it gives you another example of how memory leaks in .Net can occur. Here is the text from that document:

Always Call DataBindings.Clear when Disposing

This is a habit that's worth getting into in order to avoid a problem with versions 1.0 and 1.1 of the .NET Framework. When disposing a data-bound Form, recurse into its Controls and clear their DataBindings collections. If you know which properties of which controls are bound, then there is a more selective method that I'll go into shortly.

The problem I mentioned has to do with bound objects not being garbage-collected. Technically speaking, this is not a memory leak because references on the objects are held by an internal .NET Framework class. However, given that the application code releases all of its references, for all intents and purposes, the objects are leaked for the lifetime of the application domain. Specifically, if an application constructs, shows and closes a Form on which a control is bound to an object, then the object will not be garbage collected unless bindings are cleared. If the object is a substantial one and has references to the form itself through events, then the Form will not be collected either. What happens is that, during binding, the bound object's property is examined through reflection and a reference to the object is placed in a ValueChanged event, which is indirectly cached in a static HashTable. The HashTable has to be explicitly cleared at some point, and a good time to do this is when the bound control's owning Form is disposed. Consider this binding statement:

C# code

   label1.DataBindings.Add("Text", myObj, "Property");

Visual Basic .NET code

   label1.DataBindings.Add("Text", myObj, "Property")

For single controls where the control's bound property is known, clearing the binding is as simple as using code similar to either:

C# code

   label1.DataBindings.Clear();

Visual Basic .NET code

   label1.DataBindings.Clear()

Or:

C# code

   TypeDescriptor.Refresh(label1.DataBindings["Text"].DataSource);

Visual Basic .NET code

   TypeDescriptor.Refresh(label1.DataBindings("Text").DataSource)

For a hierarchy of controls, use the same principle while recursing into the control tree. You should be aware of a further wrinkle when your navigation path refers to a property of a property. So, if you are binding like so:

C# code

   label1.DataBindings.Add("Text", myObj, "Nested.Property");

Visual Basic .NET code

   label1.DataBindings.Add("Text", myObj, "Nested.Property")

Then your disposing logic needs to be slightly more involved:

C# code

   object ds = label1.DataBindings["Text"].DataSource;
   label1.DataBindings.Clear();
   TypeDescriptor.Refresh(ds);

Visual Basic .NET code

   Dim ds As Object = label1.DataBindings("Text").DataSource
   label1.DataBindings.Clear()
   TypeDescriptor.Refresh(ds)

Again, recurse if you want to handle a control tree. To finish this section, I'll give the recommended method for performing a general clearing of bindings courtesy of Mike Taulty, a colleague in the DPE team whose customer first encountered this issue. This code covers all cases known, so although probably not the most performant solution, it is the safest and most general.

C# code

private void ClearBindings(Control c)
{
   Binding[] bindings = new Binding[c.DataBindings.Count];
   c.DataBindings.CopyTo(bindings, 0);
   c.DataBindings.Clear();
   foreach (Binding binding in bindings)
   {
      TypeDescriptor.Refresh(binding.DataSource);
   }
   foreach (Control cc in c.Controls)
   {
      ClearBindings(cc);
   }
}

Visual Basic .NET code

    Private Sub ClearBindings(ByVal c As Control)
        Dim bindings(c.DataBindings.Count) As Binding
        c.DataBindings.CopyTo(bindings, 0)
        c.DataBindings.Clear()
        For Each bind As Binding In bindings
            System.ComponentModel. _
                TypeDescriptor.Refresh(bind.DataSource)
        Next

        For Each cc As Control In c.Controls
            ClearBindings(cc)
        Next
    End Sub
IntelliSense(less)

Just ran into an annoying little habit in the Visual Studio Environment. I created a Windows form and handled the click events for the buttons. Due to a change in the requirements, I had to place the buttons on a panel. So I simply cut and paste the buttons onto the new panel.

I then started the application again and to my surprise, the click events were no longer handled. When I double clicked one of the buttons in design mode, I found that an empty procedure was created. Visual Studio had removed all the Handles ButtonX.Click from the existing procedures.

So instead of this:

Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click

I had this in the code.

Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Thank you Mr. Intellisense(less)

Posted: Mar 24 2004, 04:42 PM by Jan Schreuder | with 3 comment(s)
Filed under:
Masked edit in .Net

Ah, the bad old days. When you had to use the Masked Edit control that came standard with Visual Basic. Today I found, downloaded, and used a Masked Edit control developed in VB.Net. It inherits from the standard text box and comes as source code!

I made one change and it ran perfectly.

You can download it here, at The Code Project.

Posted: Mar 18 2004, 07:26 PM by Jan Schreuder | with 6 comment(s)
Filed under:
Simplying nested if statements in VB.Net

I read this in Automatiserings Gids last weekend, and thought it was pretty neat. So for those that missed it, check this out.

We've all seen constructs such as this one:

.... Some code

If
 ConditionA
Then
    If ConditionB
Then
        If ConditionC
Then
            If ConditionD
Then
                If ConditionE
Then
                    ' Do something
                End
If
            End
If
        End
If
    End
If
End If

.... More code

With only a few conditions, this might not even be too bad. But you can replace it with this, which I think makes it easier to read. It's also improves your performance!

.... Some Code

Do
    If Not ConditionA
Then
        Exit
Do
    End
If
    If Not ConditionB
Then
        Exit
Do
    End
If
    If Not ConditionC
Then
        Exit
Do
    End
If
    If Not ConditionD
Then
        Exit
Do
    End
If
    If Not ConditionE
Then
        Exit
Do
    End
If
    ' Do something
Loop Until True

.... More code

The loop will only run once. The Exit Do will prevent the execution of all other code, so there is your performance gain. And since the branching is brought down a few levels, the code is also a lot easier to read.

Help, please!

Ladies and Gentlemen,

Is there really no one out there that can help me with this TrueDBGrid problem? I've been looking in all sorts of places for someone that might be able to help me, but I still nothing.

I would be most grateful if you can give me a solution.

Jan

Posted: Mar 16 2004, 12:31 PM by Jan Schreuder | with no comments
Filed under:
Component One newsgroups

I've been searching of course to find a solution to my TrueDBGrid problem. In my search I've come across this newsgroup. I've posted my problem there too, hopefully I will get an answer soon. 

Some components built by ComponentOne are also shipped with the ASP.Net Resource Kit, so this newsgroup may come in handy.

Posted: Mar 11 2004, 08:09 AM by Jan Schreuder | with 1 comment(s)
Filed under:
ComponentOne True DBGrid for .NET

In the project I now work on, I'm building an application that uses True DBGrid from ComponentOne. It's a really easy to use data-bound grid, but I stumble on a small problem.

Based on values in the grid I need to set the backcolor for an entire row. This works by setting the FetchRowStyle property to True and adding code to the FetchRowStyle event. In the event you get a row property which allows you to query column values for that row. You can then set the backcolor property. As long as you don't use grouping in the grid (DataView property set to Normal), then it works just as you expect:

Private Sub dbgImport_FetchRowStyle(ByVal sender As Object, ByVal e As C1.Win.C1TrueDBGrid.FetchRowStyleEventArgs) Handles dbgImport.FetchRowStyle

Dim dblValue As Double = CDbl
(dbgImport.Columns(DataOrders.COL_VALUE).CellValue(e.Row))

Select Case
True
   
Case
dblValue >= 25000
       
e.CellStyle.BackColor = System.Drawing.Color.Red
    Case dblValue >= 12500 And
dblValue < 25000
        e.CellStyle.BackColor = System.Drawing.Color.Orange
    Case
dblValue < 12500
        e.CellStyle.BackColor = System.Drawing.Color.White
    End
Select
End Sub

When you allow grouping, the e.row value is giving me incorrect values. For example, when I expand the first group, the row.value I get equals 1. But the values show are those of row 0. When I open the fifth group, the row value I get equals 5. But in my application, the values show are in fact those of row 17. Because of this, the backcoloring of the grid is completely incorrect when you allow grouping.

Does anyone have any idea how I can work around this problem?

Posted: Mar 10 2004, 04:35 PM by Jan Schreuder | with 5 comment(s)
Filed under:
Finally an application where I could use this...

I finally was able to use a single procedure as an event handler for multiple controls. I knew it was possible, but never had a proper use for it. But I do now.

In a VB.Net application I'm building for the client I now work for, I have a number of CheckBox Controls which enable/disable other forms on the screen. In the oldern days(= VB6), you either had to write more than one event handler or create an array of controls. In .Net you can reduce it to this:

Private Sub chkStatus_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkStatus.CheckedChanged, chkReleaseStatus.CheckedChanged, chkImportUser.CheckedChanged, chkAuthoriseUser.CheckedChanged

   ' Write some code to enable/disable various controls on the form

End Sub

I know it's not really spectacular, but I at least had a proper use for it.

Posted: Mar 10 2004, 04:16 PM by Jan Schreuder | with no comments
Filed under: