Link to home
Start Free TrialLog in
Avatar of stuengelman
stuengelmanFlag for United States of America

asked on

Need Help With Flickering Problem When I Dispose User Controls From a .NET DialogBox

Hello,

I am building a VB.NET PC application using VS 2008 and .NET Framework 3.5.

I am trying to dispose several hundred User Controls instantiated programmatically in a dialog popup by executing disposal code connected to a "Reset" button handler.  My code is shown in the code snippet.

The code in the FOR loop is the problem.  Instead of quickly disposing the User Controls, the system "flickers" for 30-60 seconds as it disposes each one individually (then the rest of the code runs quickly).

Any help would be most appreciated.

Thanks, Stu Engelman
Avatar of x77
x77
Flag of Spain image

Try using "SuspendLayout" an "ResumeLayout" to avoid rebuild Control list on each Disposed control.
Avatar of stuengelman

ASKER

Hi x77,

I use that technique in the form Load handler, and it works great on control instantiation.  But it does nothing to address the dispose operation flickering in the Reset button handler.  I.e., the dispose flickering remains even if I add the SuspendLayout code.

Stu
You can use   Hide and SuspendLayout before Loop
 and ResumeLayout and Show after loop.

I Test it an is very fast disposing controls.


Private b As Boolean = True
Private l As New List(Of Button)(100)

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
 If b Then
     Dim size = Button1.Size
     Dim xmax = Me.Width * 2, x = 0
     Dim ymax = Me.Height * 2, y = 0
     SuspendLayout()
     For x = 0 To xmax Step size.Width
        For y = 0 To ymax Step size.Height
           Dim btn As New Button With {.Location = New Point(x, y), .Size = size, .Text = "xx"}
           Controls.Add(btn)
           l.Add(btn)
        Next
     Next
     Me.ResumeLayout()
 Else
    Visible = False
    SuspendLayout()
    For Each btn In l
        btn.Dispose()
    Next
    ResumeLayout()
    l.Clear()
    Show()
 End If
 b = Not b
End Sub

Open in new window

The fastest method is owner Paint Controls and place one control each time at apropiate position to edit.
Same as DataGridView does.

But it is some complex.
Image1.png
Hi x77,

Two questions in regard to the SuspendLayout code snippet (taken from your post):

(1) Is it necessary to place the User Controls in a collection and execute the Clear command, or can I simply perform the individual Dispose operations?

(2) Should I add a "PerformLayout()" command after "ResumeLayout()"?

Thanks, Stu
Visible = False
SuspendLayout()
For Each btn In l
    btn.Dispose()
Next
ResumeLayout()
l.Clear()
Show()

Open in new window

SOLUTION
Avatar of x77
x77
Flag of Spain image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks x77,

Will try out your technique and report.

Stu
Hi x77,

Your method appears to help.  The Dispose time is speeded up (it now takes about about 7 seconds).  The flickering is still present.  I think I could live with the 7 second wait time for the Dispose operations, but would really like to eliminate the flickering if there was some way to do it.

Stu
I find on Web some proyects for WindowLess Controls (Vb6 labels, lightweight controls).

There are some people that try some solutions, but there are any significant.

In future, I whant enahce QueryCtl logic to build some Editor-Panel with binding support.

By now, the PropertyGrid has a good response.
I Use it some times, also over datatables to show full information list.

There are some good articles about PropertyGrid as Data Control.
Most them are so complex.
I take other test.

I create a Panel inside Form1.
Panel has autoscroll=false

Then I create controls on Panel.
At end I resize the panel to show all controls.

Disposing controls:

Hide Panel - don´t Flicker.
To Dispose controls: I copy  to an array, clear Panel.Controls and dispose all controls.

Controls  848 , Time 0.1808827 sec
Public Class Form1
Private b As Boolean = True
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
 If b Then
     Dim size = Button1.Size
     Dim xmax = Me.Width * 4, x = 0
     Dim ymax = Me.Height * 4, y = 0
     Panel1.SuspendLayout()
     For x = 0 To xmax - size.Width Step size.Width
        For y = 0 To ymax Step size.Height
           Dim btn As New Button With {.Location = New Point(x, y), .Size = size, .Text = "xx"}
           Panel1.Controls.Add(btn)
        Next
     Next
     Panel1.Size = New Size(xmax, ymax)
     Panel1.Visible = True
     Panel1.ResumeLayout()
 Else
    'Visible = False
    Dim t = Stopwatch.StartNew
    Panel1.Visible = False
    Dim arr(Panel1.Controls.Count - 1) As Control
    Panel1.Controls.CopyTo(arr, 0)
    Panel1.Controls.Clear()
    For Each c In arr : c.Dispose() : Next
    t.Stop()
    Debug.Print(arr.Length.ToString & " " & t.Elapsed.ToString)
    'Show()
 End If
 b = Not b
End Sub
End Class

Open in new window

x77,

I was wondering about the collection concept.  If I made the user controls members of a collection, and then issued the Clear command on the collection, would this actually prevent "collision" (runtime abend due to same named objects) when I recreate the controls, or just remove the controls from the collection?

Stu
Avatar of Howard Cantrell
I have used this on some apps.

 Public Declare Auto Function SendMessage Lib "User32" Alias "SendMessageA" (ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Boolean

         '// This code will kept the Maximized Child Forms from
        '//  flickering when opening
        '//  Sample:
        '//  Dim objForm As Form = New frmMyForm
        '//  With objForm
        '//       .Dock = DockStyle.Fill
        '//       SendMessage(Me.Handle, WM_SETREDRAW, 0, 0)
        '//       .Text = "MyForm Header"
        '//        .MdiParent = Me
        '//        .Show()
        '//  End With
        '//  SendMessage(Me.Handle, WM_SETREDRAW, 1, 0)
        '//  Me.Invalidate(True)   <----- the MDI Parent Form
        '//  Cursor = Cursors.Default
Hello, based in the x77 idea, what if you call Application.DoEvents() after set Visible = False, example:
Visible = False
Application.DoEvents()
SuspendLayout()
For Each btn In l
    btn.Dispose()
Next
ResumeLayout()
l.Clear()
Show()

Open in new window

Still flicking?
Hi Planocz and Yv989c,

Will try both of your methods and report back.

Thanks, Stu
yv989c,

Application.DoEvents() did not solve the problem.

Stu
planocz,

I'm not sure whether or not your code is applicable to my situation.  My issue is not flickering as the UserControls are instantiated and rendered.  The issue is flickering on their disposal.  I have a Reset button that disposes of the 200 or so UserControls (and two buttons), and then calls the dialog popup's Load handler to rebuild the popup from the database.  Please see the code snippet for the current state of the Reset button's handler logic.

The flickering is occuring in the FOR loop that disposes the UserControls.  Would you be able to revise the code in the code snippet to reflect what you think is the best way to handle the problem?

Thanks, Stu
Me.Visible = False
Application.DoEvents()
Me.SuspendLayout()
For i = 1 To ctlcounter
    Me.Controls("mxrow" & CStr(i)).Dispose()
Next
Me.Controls("btnMXReset").Dispose()
Me.Controls("btnMXClose").Dispose()
Me.ResumeLayout()
Me.PerformLayout()
Me.Show()
mdgExpListMgr_Load(Nothing, Nothing)
Me.ScrollControlIntoView(pbExpListBackground)

Open in new window

Hello, can you try this?
' Make a list of your controls...
Dim myControls As New List(Of Control)
For i As Integer = 1 To ctlcounter
    Dim myControl As Control = Me.Controls("mxrow" & i)
    myControls.Add(myControl)
Next
myControls.Add(Me.Controls("btnMXReset"))
myControls.Add(Me.Controls("btnMXClose"))

' Remove controls
Me.SuspendLayout()
For Each ctrl As Control In myControls
    Me.Controls.Remove(ctrl)
Next
Me.ResumeLayout(True)
Application.DoEvents()

' Dispose controls
For Each ctrl As Control In myControls
    ctrl.Dispose()
Next

mdgExpListMgr_Load(Nothing, Nothing)
Me.ScrollControlIntoView(pbExpListBackground)

Open in new window

yv989c,

You're code functions properly, but the flickering is still present.

Stu
Please try this, now I just want to know how much time requires your controls to dispose:
' Make a list of your controls...
Dim myControls As New List(Of Control)
For i As Integer = 1 To ctlcounter
    Dim myControl As Control = Me.Controls("mxrow" & i)
    myControls.Add(myControl)
Next
myControls.Add(Me.Controls("btnMXReset"))
myControls.Add(Me.Controls("btnMXClose"))

' Remove controls
Me.SuspendLayout()
For Each ctrl As Control In myControls
    Me.Controls.Remove(ctrl)
Next
Me.ResumeLayout(True)
Application.DoEvents()

Dim myStopwatch As New System.Diagnostics.Stopwatch
myStopwatch.Start()
' Dispose controls
For Each ctrl As Control In myControls
    ctrl.Dispose()
Next
myStopwatch.Stop()
MessageBox.Show(myStopwatch.Elapsed.ToString())

Open in new window


How much time show the message box?
Hi yv989c,

A great idea.  The answer came out 00:00:00:303.  That means the dispose is running really fast.  The flickering must be occuring on the reinstantiation of the controls when the Reset handler runs the command mdgExpListMgr_Load(Nothing, Nothing).  But here's what's confusing: I don't get this issue when the dialog popup is first popped and the Load handler runs at that time.  The Load handler uses SuspendLayout/.../ResumeLayout/DoEvents, which makes the initial load run nice and quickly.

Stu
How much time requires mdgExpListMgr_Load(Nothing, Nothing) to execute?

Try this to see that:
' Make a list of your controls...
Dim myControls As New List(Of Control)
For i As Integer = 1 To ctlcounter
    Dim myControl As Control = Me.Controls("mxrow" & i)
    myControls.Add(myControl)
Next
myControls.Add(Me.Controls("btnMXReset"))
myControls.Add(Me.Controls("btnMXClose"))

' Remove controls
Me.SuspendLayout()
For Each ctrl As Control In myControls
    Me.Controls.Remove(ctrl)
Next
Me.ResumeLayout(True)
Application.DoEvents()

' Dispose controls
For Each ctrl As Control In myControls
    ctrl.Dispose()
Next


Dim myStopwatch As New System.Diagnostics.Stopwatch
myStopwatch.Start()
mdgExpListMgr_Load(Nothing, Nothing)
myStopwatch.Stop()
MessageBox.Show(myStopwatch.Elapsed.ToString())


Me.ScrollControlIntoView(pbExpListBackground)

Open in new window

If you can post a code snippet of mdgExpListMgr_Load that will be useful
yv989c,

Took 7 seconds, compared with three for the initial popup.

The Load Handler is in the code snippet.

Thanks, Stu
Private Sub mdgExpListMgr_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Me.SuspendLayout()
        Me.BackgroundImage = Nothing
        pbExpListBackground.SendToBack()
        Dim CN As New SqlConnection
        Dim CMD As New SqlCommand
        Dim RDR As SqlDataReader
        CN.ConnectionString = glbLocalCnxnString
        CN.Open()
        CMD.Connection = CN
        Dim SQL As String
        SQL = "select * from ExpenseList order by Supercategory,Category,ExpenseName"
        CMD.CommandText = SQL
        RDR = CMD.ExecuteReader()
        Dim ii As Integer = 0
        Dim resettop As Integer = 0
        If RDR.HasRows Then
            Do While RDR.Read
                Dim dmxrow As New MXrow
                ii = ii + 1
                If ii = 1 Then
                    dmxrow.Name = "mxrow1"
                    Me.Controls.Add(dmxrow)
                    dmxrow.Left = 40
                    dmxrow.Top = Me.lblExpenseList.Top + 30
                Else
                    dmxrow.Name = "mxrow" & CStr(ii)
                    Me.Controls.Add(dmxrow)
                    dmxrow.HideHeaders()
                    dmxrow.Update()
                    dmxrow.SendToBack()
                    dmxrow.Left = 40
                    dmxrow.Top = Me.Controls("mxrow" & CStr(ii - 1)).Bottom - 30
                End If
                If RDR("SuperCategory") = "Fixed" Then
                    dmxrow.cboMXSuperCategory.SelectedIndex = 0
                Else
                    dmxrow.cboMXSuperCategory.SelectedIndex = 1
                End If
                dmxrow.txtMXCategory.Text = RDR("Category")
                dmxrow.txtMXExpenseName.Text = RDR("ExpenseName")
                dmxrow.cbMXStatus.Checked = RDR("Status")
                dmxrow.btnMXAdd.Tag = CStr(ii)
                dmxrow.btnMXAlter.Tag = CStr(RDR("ExpListID"))
                dmxrow.btnMXDelete.Tag = CStr(RDR("ExpListID"))
                resettop = dmxrow.Bottom
                ctlcounter = ii
                If CStr(ii) = glbMiscString Then
                    ii = ii + 1
                    dmxrow.Name = "mxrow" & CStr(ii)
                    Me.Controls.Add(dmxrow)
                    dmxrow.HideHeaders()
                    dmxrow.Update()
                    dmxrow.SendToBack()
                    dmxrow.Left = 40
                    dmxrow.Top = Me.Controls("mxrow" & CStr(ii - 1)).Bottom - 30
                    dmxrow.cboMXSuperCategory.SelectedIndex = -1
                    dmxrow.txtMXCategory.Text = ""
                    dmxrow.txtMXExpenseName.Text = ""
                    dmxrow.btnMXAdd.Tag = "0"
                    dmxrow.btnMXAdd.Text = "Enter"
                    dmxrow.btnMXAlter.Enabled = False
                    dmxrow.btnMXDelete.Enabled = False
                    dmxrow.cbMXStatus.Checked = False
                    resettop = dmxrow.Bottom
                    ctlcounter = ii
                    glbMiscString = ""
                End If
            Loop
        Else
            Dim dmxrow As New MXrow
            dmxrow.Name = "mxrow1"
            Me.Controls.Add(dmxrow)
            dmxrow.Left = 40
            dmxrow.Top = Me.lblExpenseList.Top + 30
            dmxrow.cboMXSuperCategory.SelectedIndex = -1
            dmxrow.txtMXCategory.Text = ""
            dmxrow.txtMXExpenseName.Text = ""
            dmxrow.btnMXAdd.Tag = "0"
            dmxrow.btnMXAdd.Text = "Enter"
            dmxrow.btnMXAlter.Enabled = False
            dmxrow.btnMXDelete.Enabled = False
            dmxrow.cbMXStatus.Checked = False
            resettop = dmxrow.Bottom
            ctlcounter = 1
        End If
        Dim btnMXReset As New Button
        btnMXReset.Name = "btnMXReset"
        Me.Controls.Add(btnMXReset)
        btnMXReset.Text = "Reset"
        btnMXReset.BackColor = Color.White
        btnMXReset.Left = (Me.Width / 2) - (btnMXReset.Width / 2)
        btnMXReset.Top = resettop + 15
        AddHandler btnMXReset.Click, AddressOf btnMXReset_Clicked
        resettop = btnMXReset.Bottom + 15
        Dim btnMXClose As New Button
        btnMXClose.Name = "btnMXClose"
        Me.Controls.Add(btnMXClose)
        btnMXClose.Text = "Close"
        btnMXClose.BackColor = Color.White
        btnMXClose.Left = (Me.Width / 2) - (btnMXClose.Width / 2)
        btnMXClose.Top = resettop
        AddHandler btnMXClose.Click, AddressOf btnMXClose_Clicked
        resettop = btnMXClose.Bottom + 15
        Dim lblspacer As New Label
        Me.Controls.Add(lblspacer)
        lblspacer.Text = ""
        lblspacer.Left = (Me.Width / 2) - (lblspacer.Width / 2)
        lblspacer.Top = resettop
        RDR.Close()
        RDR = Nothing
        CN.Close()
        CMD = Nothing
        CN = Nothing
        For i As Integer = 1 To ctlcounter
            Dim myControl As Control = Me.Controls("mxrow" & i)
            myControls.Add(myControl)
        Next
        myControls.Add(Me.Controls("btnMXReset"))
        myControls.Add(Me.Controls("btnMXClose"))
        Me.ResumeLayout(True)
        Application.DoEvents()
    End Sub

Open in new window

Hello, just a question, the MXrow class, when you create an instance of it, it does a DB query to fill a ComboBox/DropDownList?

Example, when I do this, it does a DB query to fill cboMXSuperCategory?
Dim dmxrow As New MXrow

Open in new window

Hi yv989c,

Correct.  A DB query is performed to populate the controls (one combobox, two textboxes, and one checkbox) in each MXrow instance.  I copy pasted the applicable logic contained in the previous code snippet to a new code snippet here.

Stu
If RDR("SuperCategory") = "Fixed" Then
    dmxrow.cboMXSuperCategory.SelectedIndex = 0
Else
    dmxrow.cboMXSuperCategory.SelectedIndex = 1
End If
dmxrow.txtMXCategory.Text = RDR("Category")
dmxrow.txtMXExpenseName.Text = RDR("ExpenseName")
dmxrow.cbMXStatus.Checked = RDR("Status")

Open in new window

Hello, well, that is not what I want to know, I saw that in your previous code... I think that I did not explain very well.. I'm going to make some changes to your method that I think can eliminate the flickering effect and reduce the load time.
Hi yv989c,

Thank you so much.  I really appreciate all your help.

Best regards, Stu
Two questions, how many rows contains the table [ExpenseList]? also, this table is updated very often?
Hello, try this new code:
Function GetExpenseListData() As DataTable
    Dim dtt As New DataTable

    Using cn As New SqlConnection(glbLocalCnxnString)
        Dim cmd As New SqlCommand("SELECT SuperCategory, Category, ExpenseName, Status, ExpListID FROM ExpenseList ORDER BY Supercategory, Category, ExpenseName", cn)
        cn.Open()
        Using dr As SqlDataReader = cmd.ExecuteReader
            dtt.Load(dr)
        End Using
    End Using

    Return dtt
End Function

Private Sub mdgExpListMgr_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Try
        Me.SuspendLayout()
        Me.BackgroundImage = Nothing
        pbExpListBackground.SendToBack()

        Dim myData As DataTable = GetExpenseListData()

        Dim ii As Integer = 0
        Dim resettop As Integer = 0
        If myData.Rows.Count > 0 Then
            Dim myMXrows As New List(Of MXrow)
            For Each row As DataRow In myData.Rows
                Dim dmxrow As New MXrow
                ii = ii + 1
                If ii = 1 Then
                    dmxrow.Name = "mxrow1"
                    myMXrows.Add(dmxrow)
                Else
                    dmxrow.Name = "mxrow" & CStr(ii)
                    myMXrows.Add(dmxrow)
                    dmxrow.HideHeaders()
                End If

                If row("SuperCategory") = "Fixed" Then
                    dmxrow.cboMXSuperCategory.SelectedIndex = 0
                Else
                    dmxrow.cboMXSuperCategory.SelectedIndex = 1
                End If

                dmxrow.txtMXCategory.Text = row("Category")
                dmxrow.txtMXExpenseName.Text = row("ExpenseName")
                dmxrow.cbMXStatus.Checked = row("Status")
                dmxrow.btnMXAdd.Tag = CStr(ii)
                dmxrow.btnMXAlter.Tag = CStr(row("ExpListID"))
                dmxrow.btnMXDelete.Tag = CStr(row("ExpListID"))

                resettop = dmxrow.Bottom
                ctlcounter = ii

                If CStr(ii) = glbMiscString Then
                    ii = ii + 1
                    dmxrow.Name = "mxrow" & CStr(ii)
                    myMXrows.Add(dmxrow)
                    dmxrow.HideHeaders()
                    dmxrow.cboMXSuperCategory.SelectedIndex = -1
                    dmxrow.txtMXCategory.Text = ""
                    dmxrow.txtMXExpenseName.Text = ""
                    dmxrow.btnMXAdd.Tag = "0"
                    dmxrow.btnMXAdd.Text = "Enter"
                    dmxrow.btnMXAlter.Enabled = False
                    dmxrow.btnMXDelete.Enabled = False
                    dmxrow.cbMXStatus.Checked = False
                    resettop = dmxrow.Bottom
                    ctlcounter = ii
                    glbMiscString = ""
                End If
            Next

            ii = 0
            For Each dmxrow As MXrow In myMXrows
                Me.Controls.Add(dmxrow)
                ii = ii + 1
                If ii = 1 Then
                    dmxrow.Left = 40
                    dmxrow.Top = Me.lblExpenseList.Top + 30
                Else
                    dmxrow.Update()
                    dmxrow.SendToBack()
                    dmxrow.Left = 40
                    dmxrow.Top = Me.Controls("mxrow" & CStr(ii - 1)).Bottom - 30
                End If

                resettop = dmxrow.Bottom
                ctlcounter = ii

                If CStr(ii) = glbMiscString Then
                    ii = ii + 1
                    Me.Controls.Add(dmxrow)
                    dmxrow.Update()
                    dmxrow.SendToBack()
                    dmxrow.Left = 40
                    dmxrow.Top = Me.Controls("mxrow" & CStr(ii - 1)).Bottom - 30
                    resettop = dmxrow.Bottom
                    ctlcounter = ii
                    glbMiscString = ""
                End If
            Next
        Else
            Dim dmxrow As New MXrow
            dmxrow.Name = "mxrow1"
            Me.Controls.Add(dmxrow)
            dmxrow.Left = 40
            dmxrow.Top = Me.lblExpenseList.Top + 30
            dmxrow.cboMXSuperCategory.SelectedIndex = -1
            dmxrow.txtMXCategory.Text = ""
            dmxrow.txtMXExpenseName.Text = ""
            dmxrow.btnMXAdd.Tag = "0"
            dmxrow.btnMXAdd.Text = "Enter"
            dmxrow.btnMXAlter.Enabled = False
            dmxrow.btnMXDelete.Enabled = False
            dmxrow.cbMXStatus.Checked = False
            resettop = dmxrow.Bottom
            ctlcounter = 1
        End If

        Dim btnMXReset As New Button
        btnMXReset.Name = "btnMXReset"
        Me.Controls.Add(btnMXReset)
        btnMXReset.Text = "Reset"
        btnMXReset.BackColor = Color.White
        btnMXReset.Left = (Me.Width / 2) - (btnMXReset.Width / 2)
        btnMXReset.Top = resettop + 15
        AddHandler btnMXReset.Click, AddressOf btnMXReset_Clicked
        resettop = btnMXReset.Bottom + 15

        Dim btnMXClose As New Button
        btnMXClose.Name = "btnMXClose"
        Me.Controls.Add(btnMXClose)
        btnMXClose.Text = "Close"
        btnMXClose.BackColor = Color.White
        btnMXClose.Left = (Me.Width / 2) - (btnMXClose.Width / 2)
        btnMXClose.Top = resettop
        AddHandler btnMXClose.Click, AddressOf btnMXClose_Clicked
        resettop = btnMXClose.Bottom + 15

        Dim lblspacer As New Label
        Me.Controls.Add(lblspacer)
        lblspacer.Text = ""
        lblspacer.Left = (Me.Width / 2) - (lblspacer.Width / 2)
        lblspacer.Top = resettop

        For i As Integer = 1 To ctlcounter
            Dim myControl As Control = Me.Controls("mxrow" & i)
            myControls.Add(myControl)
        Next

        myControls.Add(btnMXReset)
        myControls.Add(btnMXClose)
    Finally
        Me.ResumeLayout(True)
        Application.DoEvents()
    End Try
End Sub

Open in new window


There is a new function named GetExpenseListData and the updated sub mdgExpListMgr_Load.
Please test this and check if I have not broken anything ;)
Hi yv989c,

The ExpenseList table has about 200 rows.

I'll try out your code and report.

Thanks, Stu
Hi yv989c,

An incremental improvement.  I still have the three second refresh time, which is fine, as that's about as long as it takes the popup to initially load.  Also, the flickering is reduced to the two textboxes, whereas ealier most of the whole user control flickered (combobox, two textboxes, checkbox, and three buttons) as the refresh spun through the DB and rerendered the user controls.  What's so nice as mentioned earlier is that the popup simply "waits until done" on initial load before showing.

I'm wondering whether some kind of popup hide/show strategy would make sense for the refresh to get the final extra bit of performance I need?

My latest code is in the snippet.

Thanks, Stu
Imports System.Windows.Forms
Imports System.Data.SqlClient

Public Class mdgExpListMgr

    Private myUserControls As New List(Of MXrow)

    Private Sub mdgExpListMgr_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Try
            Me.SuspendLayout()
            Me.BackgroundImage = Nothing
            pbExpListBackground.SendToBack()
            Dim myData As DataTable = GetExpenseListData()
            Dim ii As Integer = 0
            If myData.Rows.Count > 0 Then
                For Each row As DataRow In myData.Rows
                    Dim dmxrow As New MXrow
                    ii = ii + 1
                    If ii = 1 Then
                        dmxrow.Name = "mxrow1"
                        myUserControls.Add(dmxrow)
                    Else
                        dmxrow.Name = "mxrow" & CStr(ii)
                        myUserControls.Add(dmxrow)
                        dmxrow.HideHeaders()
                    End If
                    If row("SuperCategory") = "Fixed" Then
                        dmxrow.cboMXSuperCategory.SelectedIndex = 0
                    Else
                        dmxrow.cboMXSuperCategory.SelectedIndex = 1
                    End If
                    dmxrow.txtMXCategory.Text = row("Category")
                    dmxrow.txtMXExpenseName.Text = row("ExpenseName")
                    dmxrow.cbMXStatus.Checked = row("Status")
                    dmxrow.btnMXAdd.Tag = CStr(ii)
                    dmxrow.btnMXAlter.Tag = CStr(row("ExpListID"))
                    dmxrow.btnMXDelete.Tag = CStr(row("ExpListID"))
                    If CStr(ii) = glbMiscString Then
                        Dim dnmxrow As New MXrow
                        ii = ii + 1
                        dnmxrow.Name = "mxrow" & CStr(ii)
                        myUserControls.Add(dnmxrow)
                        dnmxrow.HideHeaders()
                        dnmxrow.cboMXSuperCategory.SelectedIndex = -1
                        dnmxrow.txtMXCategory.Text = ""
                        dnmxrow.txtMXExpenseName.Text = ""
                        dnmxrow.btnMXAdd.Tag = "0"
                        dnmxrow.btnMXAdd.Text = "Enter"
                        dnmxrow.btnMXAlter.Enabled = False
                        dnmxrow.btnMXDelete.Enabled = False
                        dnmxrow.cbMXStatus.Checked = False
                        glbMiscString = ""
                    End If
                Next
                ii = 0
                For Each dmxrow As MXrow In myUserControls
                    Me.Controls.Add(dmxrow)
                    ii = ii + 1
                    If ii = 1 Then
                        dmxrow.Left = 40
                        dmxrow.Top = Me.lblExpenseList.Top + 30
                        dmxrow.Update()
                    Else
                        dmxrow.SendToBack()
                        dmxrow.Left = 40
                        dmxrow.Top = Me.Controls("mxrow" & CStr(ii - 1)).Bottom - 30
                        dmxrow.Update()
                    End If
                Next
            Else
                Dim dmxrow As New MXrow
                dmxrow.Name = "mxrow1"
                Me.Controls.Add(dmxrow)
                dmxrow.Left = 40
                dmxrow.Top = Me.lblExpenseList.Top + 30
                dmxrow.cboMXSuperCategory.SelectedIndex = -1
                dmxrow.txtMXCategory.Text = ""
                dmxrow.txtMXExpenseName.Text = ""
                dmxrow.btnMXAdd.Tag = "0"
                dmxrow.btnMXAdd.Text = "Enter"
                dmxrow.btnMXAlter.Enabled = False
                dmxrow.btnMXDelete.Enabled = False
                dmxrow.cbMXStatus.Checked = False
            End If
            Dim resettop = Me.Controls("mxrow" & CStr(ii)).Bottom
            Dim btnMXReset As New Button
            btnMXReset.Name = "btnMXReset"
            Me.Controls.Add(btnMXReset)
            btnMXReset.Text = "Reset"
            btnMXReset.BackColor = Color.White
            btnMXReset.Left = (Me.Width / 2) - (btnMXReset.Width / 2)
            btnMXReset.Top = resettop + 15
            AddHandler btnMXReset.Click, AddressOf btnMXReset_Clicked
            resettop = btnMXReset.Bottom + 15
            Dim btnMXClose As New Button
            btnMXClose.Name = "btnMXClose"
            Me.Controls.Add(btnMXClose)
            btnMXClose.Text = "Close"
            btnMXClose.BackColor = Color.White
            btnMXClose.Left = (Me.Width / 2) - (btnMXClose.Width / 2)
            btnMXClose.Top = resettop
            AddHandler btnMXClose.Click, AddressOf btnMXClose_Clicked
            resettop = btnMXClose.Bottom + 15
            Dim lblspacer As New Label
            Me.Controls.Add(lblspacer)
            lblspacer.Text = ""
            lblspacer.Left = (Me.Width / 2) - (lblspacer.Width / 2)
            lblspacer.Top = resettop
        Finally
            Me.ResumeLayout(True)
            Application.DoEvents()
        End Try
    End Sub

    Function GetExpenseListData() As DataTable
        Dim dtt As New DataTable
        Using cn As New SqlConnection(glbLocalCnxnString)
            Dim cmd As New SqlCommand("SELECT SuperCategory, Category, ExpenseName, Status, ExpListID FROM ExpenseList ORDER BY Supercategory, Category, ExpenseName", cn)
            cn.Open()
            Using dr As SqlDataReader = cmd.ExecuteReader
                dtt.Load(dr)
            End Using
        End Using
        Return dtt
    End Function

    Public Sub btnMXReset_Clicked()
        Me.SuspendLayout()
        For Each ctrl As Control In myUserControls
            Me.Controls.Remove(ctrl)
        Next
        Me.ResumeLayout(True)
        Application.DoEvents()
        For Each ctrl As Control In myUserControls
            ctrl.Dispose()
        Next
        myUserControls.Clear()
        If glbMiscString = "" Then
            Me.Controls("btnMXReset").Dispose()
            Me.Controls("btnMXClose").Dispose()
        End If
        mdgExpListMgr_Load(Nothing, Nothing)
        Me.ScrollControlIntoView(pbExpListBackground)
    End Sub

    Private Sub btnMXClose_Clicked()
        Me.Close()
    End Sub

End Class

Open in new window

I will try something and let you know...
OK, thanks yv989c, Stu
Hello Stu, based on your last code I did this, it will show a dialog (Please wait...) while your controls are created:

1. Add this sub to your form
Sub LoadMyFormControls(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim myForm As mdgExpListMgr = sender

    Dim myData As DataTable = GetExpenseListData()
    Dim ii As Integer = 0
    If myData.Rows.Count > 0 Then
        For Each row As DataRow In myData.Rows
            Dim dmxrow As New MXrow
            ii = ii + 1
            If ii = 1 Then
                dmxrow.Name = "mxrow1"
                myUserControls.Add(dmxrow)
            Else
                dmxrow.Name = "mxrow" & CStr(ii)
                myUserControls.Add(dmxrow)
                dmxrow.HideHeaders()
            End If
            If row("SuperCategory") = "Fixed" Then
                dmxrow.cboMXSuperCategory.SelectedIndex = 0
            Else
                dmxrow.cboMXSuperCategory.SelectedIndex = 1
            End If
            dmxrow.txtMXCategory.Text = row("Category")
            dmxrow.txtMXExpenseName.Text = row("ExpenseName")
            dmxrow.cbMXStatus.Checked = row("Status")
            dmxrow.btnMXAdd.Tag = CStr(ii)
            dmxrow.btnMXAlter.Tag = CStr(row("ExpListID"))
            dmxrow.btnMXDelete.Tag = CStr(row("ExpListID"))
            If CStr(ii) = glbMiscString Then
                Dim dnmxrow As New MXrow
                ii = ii + 1
                dnmxrow.Name = "mxrow" & CStr(ii)
                myUserControls.Add(dnmxrow)
                dnmxrow.HideHeaders()
                dnmxrow.cboMXSuperCategory.SelectedIndex = -1
                dnmxrow.txtMXCategory.Text = ""
                dnmxrow.txtMXExpenseName.Text = ""
                dnmxrow.btnMXAdd.Tag = "0"
                dnmxrow.btnMXAdd.Text = "Enter"
                dnmxrow.btnMXAlter.Enabled = False
                dnmxrow.btnMXDelete.Enabled = False
                dnmxrow.cbMXStatus.Checked = False
                glbMiscString = ""
            End If
        Next
        ii = 0
        For Each dmxrow As MXrow In myUserControls
            myForm.Controls.Add(dmxrow)
            ii = ii + 1
            If ii = 1 Then
                dmxrow.Left = 40
                dmxrow.Top = myForm.lblExpenseList.Top + 30
                dmxrow.Update()
            Else
                dmxrow.SendToBack()
                dmxrow.Left = 40
                dmxrow.Top = myForm.Controls("mxrow" & CStr(ii - 1)).Bottom - 30
                dmxrow.Update()
            End If
        Next
    Else
        Dim dmxrow As New MXrow
        dmxrow.Name = "mxrow1"
        myForm.Controls.Add(dmxrow)
        dmxrow.Left = 40
        dmxrow.Top = myForm.lblExpenseList.Top + 30
        dmxrow.cboMXSuperCategory.SelectedIndex = -1
        dmxrow.txtMXCategory.Text = ""
        dmxrow.txtMXExpenseName.Text = ""
        dmxrow.btnMXAdd.Tag = "0"
        dmxrow.btnMXAdd.Text = "Enter"
        dmxrow.btnMXAlter.Enabled = False
        dmxrow.btnMXDelete.Enabled = False
        dmxrow.cbMXStatus.Checked = False
    End If
    Dim resettop = myForm.Controls("mxrow" & CStr(ii)).Bottom
    Dim btnMXReset As New Button
    btnMXReset.Name = "btnMXReset"
    myForm.Controls.Add(btnMXReset)
    btnMXReset.Text = "Reset"
    btnMXReset.BackColor = Color.White
    btnMXReset.Left = (myForm.Width / 2) - (btnMXReset.Width / 2)
    btnMXReset.Top = resettop + 15
    AddHandler btnMXReset.Click, AddressOf btnMXReset_Clicked
    resettop = btnMXReset.Bottom + 15
    Dim btnMXClose As New Button
    btnMXClose.Name = "btnMXClose"
    myForm.Controls.Add(btnMXClose)
    btnMXClose.Text = "Close"
    btnMXClose.BackColor = Color.White
    btnMXClose.Left = (myForm.Width / 2) - (btnMXClose.Width / 2)
    btnMXClose.Top = resettop
    AddHandler btnMXClose.Click, AddressOf btnMXClose_Clicked
    resettop = btnMXClose.Bottom + 15
    Dim lblspacer As New Label
    myForm.Controls.Add(lblspacer)
    lblspacer.Text = ""
    lblspacer.Left = (myForm.Width / 2) - (lblspacer.Width / 2)
    lblspacer.Top = resettop
End Sub

Open in new window



2. Add this form (inside the zip file) to your project:
LoadingForm.zip
First extract the zip file to a folder and be sure to add both files (LoadingForm.vb and LoadingForm.Designer.vb) to your project.


3. Update your mdgExpListMgr_Load method with this:
Private Sub mdgExpListMgr_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Try
        Me.SuspendLayout()
        Me.BackgroundImage = Nothing
        pbExpListBackground.SendToBack()
        Me.Hide()
        Application.DoEvents()

        Using myLoadingForm As New LoadingForm(AddressOf LoadMyFormControls)
            myLoadingForm.ShowDialog(Me)
        End Using

        Me.Show()
    Finally
        Me.ResumeLayout(True)
        Application.DoEvents()
    End Try
End Sub

Open in new window


Try it, I hope it works :)
Hi yv989c,

It appears the class LoadingForm does not exist in VS 2008.

Stu
Hi Stu, LoadingForm class is the form in step 2, you must add these files to your project, do you need help with this step?
Oops, missed that.  Let me try again.

Stu
yv989c,

An excellent job, but one small problem.  Unless all other apps are minimized, the main app window of the system as well as the dialog popup disappear under the other apps.  Is there any way to bring the main app and dialogbox back to being the active windows (the dialogbox should sit on top of the main app window)?

Thanks, Stu
yv989c,

A clarification to my last post.  The app I'm working on does not lose focus when I press the button on the main app form to pop the dialog box.  The issue only arises when I press the Reset button on the dialog box itself; in this latter case, the VB app main screen and dialog box end up in back of all non-minimized windows from other open applications.

Stu
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks for all the help.  Awarded points to both x77 and yv989c as both made contributions to the final solution.  Congrats to yv989c for solving the final issue about getting the app form to reactivate after refresh.  Very complex problems were solved in this thread, and both of you did a great job.