OK, I am relatively new to C#, so in order to give myself a 'learning' project, I thought I'd update an application I wrote a couple of years ago.
The application uses a ListView control in "Details" mode to display MP3 tag data stored in an Access MDB database file.
The problem I have is that in VB6 the list (containing some 3000+ entries) is populated quite rapidly with only a minimal impact on form drawing that is barely noticeable.
However, in C#, there is a very noticable delay and interpution to the drawing of the form.

Here is the original VB6 code...

// Blank down any items we were displaying and set the Path as the sorted field.
    Call frmMain.lstCatView.ListItems.Clear
    lstCatView.SortKey = 6
    
    // Check if there are any records in the Database.
    If DBHasData > 0 Then
    
        // If we have something to browse, then get a list of every record.
        Set Rec = dbCat.OpenRecordset("SELECT * FROM Tracks WHERE 1")

        // Step through each record and display it in the ListView control, sorted by Path.
        For tmpCntFiles = 0 To DBHasData - 1
            tmpDBRows = Rec.GetRows
            If tmpDBRows(5, 0) < 1 Or tmpDBRows(5, 0) > 255 Then
                frmMain.lstCatView.ListItems.Add (tmpCntFiles + 1), , "??"
            Else
                frmMain.lstCatView.ListItems.Add (tmpCntFiles + 1), , tmpDBRows(5, 0)
            End If
            If tmpDBRows(1, 0) = "" Then
               frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(1) = "<not specified>"
            Else
               frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(1) = tmpDBRows(1, 0)
            End If
            If tmpDBRows(2, 0) = "" Then
                frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(2) = "<not specified>"
            Else
                frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(2) = tmpDBRows(2, 0)
            End If
            If tmpDBRows(3, 0) = "" Then
                frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(3) = "<not specified>"
            Else
                frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(3) = tmpDBRows(3, 0)
            End If
            If tmpDBRows(4, 0) < 1900 Or tmpDBRows(5, 0) > Year(Now) Then
                frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(4) = "????"
            Else
                frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(4) = tmpDBRows(4, 0)
            End If
            If tmpDBRows(6, 0) > 147 Then tmpDBRows(6, 0) = 148
            frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(5) = arrGenreName(tmpDBRows(6, 0))
            frmMain.lstCatView.ListItems(tmpCntFiles + 1).SubItems(6) = tmpDBRows(7, 0)
        Next tmpCntFiles
    End If

    // Destroy the Record object now that we have finished with it and, lastly, turn on sorting again.
    Set Rec = Nothing
    lstCatView.Sorted = True

... and here is the C# version (with less field validation being done)...

lstCatView.Items.Clear(); 
            string sql = "SELECT * FROM Tracks";
            OleDbConnection conn = new OleDbConnection(connectionString);
            OleDbCommand cmd = new OleDbCommand(sql, conn);
            cmd.CommandType = CommandType.Text;
            conn.Open();
            OleDbDataReader reader = cmd.ExecuteReader();
            
            try
            {
                while (reader.Read())
                {
                    bool EventsCall = false;
                    object[] DBRecord = new Object[8];
                    int i = reader.GetValues(DBRecord);

                    if (EventsCall)
                    {
                        // Allow system events to propagate on
                        // every other iteration.
                        Application.DoEvents();
                        EventsCall = !EventsCall;
                    }

                    string tmpStrTrk = DBRecord[5].ToString();

                    int tmpIntTrk = Int32.Parse(tmpStrTrk.Trim());
                    ListViewItem LVI = new ListViewItem(((tmpIntTrk > 0 && tmpIntTrk < 255) ? tmpIntTrk.ToString() : " - "));
                    LVI.SubItems.Add(DBRecord[1].ToString());
                    LVI.SubItems.Add(DBRecord[2].ToString());
                    LVI.SubItems.Add(DBRecord[3].ToString());
                    LVI.SubItems.Add(DBRecord[4].ToString());
                    LVI.SubItems.Add(DBRecord[6].ToString());
                    LVI.SubItems.Add(DBRecord[7].ToString());
                    lstCatView.Items.Add(LVI);

                }
            }
            catch(Exception eX)
            {
                MessageBox.Show("ERROR : " + eX.Message);
            }
            finally
            {
                conn.Close();
                reader.Close();
                cmd.Dispose();
                reader.Dispose();
                conn.Dispose();
            }

... so, why does the C# version appear slower? Is there something I am doing wrong?

Recommended Answers

All 7 Replies

You're not doing anything wrong. Windows Forms is notorious for having a noticeably slow redraw out of the box. There are a number of ways to improve matters, but the list is kind of long, so I'll just suggest searching google.

One thing to keep in mind is that C# has to go through the .NET framework while VB6 would hit the Win32 API directly. There's a whole extra layer or two difference, though that's not an excuse for the unresponsiveness of WinForms. ;)

A lot of this has to do with the technique of filling the controls at the same time you're reading from the database.

The controls are not exactly the same and neither is the driver getting to the database.

I *personally* would read the content into a collection then put the data into the controls. The collection I would create would have nothing to do with any type of display, so when I change my project type (Console, WinForm, ASP.NET) I would not have to change the database access code.

Another test you might want to run would be to do this in VB.NET to see if you still have the same issue.
...and as deceptikon mentioned: one is managed and the other is native.

Thanks for the reply - I was thinking of 'faking' it by hiding my ListView, showing an identical ListView that doesn't do anything and swapping the visibility of the two once the 'real' list is populated. What do you think?
BTW, is there a way to tell if a form has finished painting?

That's a neat technique and it should work without a problem.

You are reading the database in two different ways. In the VB code, you fetch the entire table in a single read, then loop through the records. In the C# code, you are fetching single rows at a time, so you are making multiple calls to the DB.

commented: Good Catch +12

Ahh! I'll have to sort that - thanks for pointing it out, I missed that!

Another thing is your EventCall section. It forces the listbox to redraw each time you run through it (since it's added new elements). One thing you can try is creating a list of the information, then use databinding to the list, effectively adding them all at one time.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.