Looping between OpenForms excluding the ActiveMdiChild - vb.net

I'm trying to make a loop which runs for every form excluding the activeform... I found a solution which I could use ActiveForm.ActiveMdiChild.Name = FormRefresh.Name but the problem is if I've 2 forms of same name, It'll not loop in the not active form...
Public Shared Sub VerificaAlterações()
For Each FormRefresh As Form In Application.OpenForms()
If ActiveForm.ActiveMdiChild = FormRefresh Then Continue For ' Error here
' Do some work...
Next
End Sub
Edit: The problem was the Shared in Sub... I removed it and it worked with the answer below.

There's no need to compare the names of the forms when you can compare the forms themselves:
If FormRefresh Is ActiveMdiChild Then Continue For

Related

Dynamically Removing controls from a form

I use a form that has controls that are dynamically added according to the class of object for which it is being used.
In order to load the next object into the form it is necessary to delete and rebuild the dynamic part.
I am experiencing random results when removing the objects using the code below, can you tell me where my error may be?
Sub CntrlKill(KillName As String)
For Each c In Me.Controls
If Strings.InStr(c.name, KillName) > 0 Then
Me.Controls.Remove(c)
End If
Next
End Sub
Translating the second link provided by GSerg to vb.net.
Create a list to hold the controls you want to delete. You can loop through the all the controls and add certain ones to a list. This does not effect the Controls collection. I haven't seen InStr used in a very long time. The .net .Contains will probably do what you need.
The second loop loops through the lstToDelete. This list is not effected by removing controls from the Controls collection.
The rule for For Each loops is don't effect the Collection you are looping through. You can change properties of the items in the collections. Just don't remove any items.
Sub CntrlKill(KillName As String)
Dim lstToDelete As New List(Of Control)
For Each c As Control In Controls
If c.Name.Contains(KillName) Then
lstToDelete.Add(c)
End If
Next
For Each c In lstToDelete
c.Dispose()
Next
End Sub
Or the backwards loop way.
Private Sub NukeControls(KillString As String)
For i = Controls.Count - 1 To 0 Step -1
If Controls(i).Name.Contains(KillString) Then
Controls(i).Dispose()
End If
Next
End Sub

Split Form creates a separate collections for each of its parts

I have a split Form in MS Access where users can select records in the datasheet and add it to a listbox in the Form thereby creating custom selection of records.
The procedure is based on a collection. A selected record gets transformed into a custom Object which gets added to the collection which in turn is used to populate the listbox. I have buttons to Add a Record, Remove a Record or Clear All which work fine.
However I thought, that pressing all these buttons is a bit tedious if you create a Selection of more then a dozen records, so i reckoned it should be simple to bind the actions of the Add and Remove buttons to the doubleclick event of a datasheet field and the listbox respectively.
Unfortunately i was mistaken as this broke the form. While a doubleclick on a field in the datasheet part of the form added the record to the listbox it was now unable to remove an item from the underlying collection giving me Run-time error 5: "Invalid procedure call or argument". Furthermore the clear all Button doesn't properly reset the collection anymore. When trying to Clear and adding a record of the previous selection the code returns my custom error that the record is already part of the selection and the listbox gets populated with the whole previous selection, which should be deleted. This leaves me to believe, that for some Reason the collection gets duplicated or something along these lines. Any hints into the underlying problem is apprecciated. Code as Follows:
Option Compare Database
Dim PersColl As New Collection
Private Sub AddPerson_Click()
AddPersToColl Me!ID
FillListbox
End Sub
Private Sub btnClear_Click()
Set PersColl = Nothing
lBoxSelection.RowSource = vbaNullString
End Sub
Private Sub btnRemovePers_Click()
PersColl.Remove CStr(lBoxSelection.Value)
FillListbox
End Sub
Private Sub FillListbox()
Dim Pers As Person
lBoxSelection.RowSource = vbaNullString
For Each Pers In PersColl
lBoxSelection.AddItem Pers.ID & ";" & Pers.FullName
Next Pers
lBoxSelection.Requery
End Sub
Private Function HasKey(coll As Collection, strKey As String) As Boolean
Dim var As Variant
On Error Resume Next
var = IsObject(coll(strKey))
HasKey = Not IsEmpty(var)
Err.Clear
End Function
Private Sub AddPersToColl(PersonId As Long)
Dim Pers As Person
Set Pers = New Person
Pers.ID = PersonId
If HasKey(PersColl, CStr(PersonId)) = False Then
PersColl.Add Item:=Pers, Key:=CStr(PersonId)
Else: MsgBox "Person Bereits ausgewählt"
End If
End Sub
This works alone, but Simply Adding this breaks it as described above.
Private Sub Nachname_DblClick(Cancel As Integer)
AddPersToColl Me!ID
FillListbox
End Sub
Further testing showed that its not working if i simply remove the Private Sub AddPerson_Click()
Edit1:
Clarification: I suspected that having 2 different events calling the same subs would somehow duplicate the collection in memory, therefore removing one event should work. This is however not the case. Having the subs called by a button_Click event works fine but having the same subs called by a double_click event prompts the behaviour described above. The issue seems therefore not in having the subs bound to more than one event, but rather by having them bound to the Double_Click event.
Edit2: I located the issue but I Haven't found a solution yet. Looks like Split-Forms are not really connected when it comes to the underlying vba code. DoubleClicking on the record in the datasheet view creates a Collection while using the buttons on the form part creates another one. When trying to remove a collection item by clicking a button on the form, it prompts an error because this collection is empty. However clicking the clear all button on the form part doesn't clear the collection associated with the datasheet part.
Putting the collection outside into a separate module might be a workaround but i would appreciate any suggestions which would let me keep the Code in the form module.
The behavior is caused by the split form which creates two separate collections for each one of its parts. Depending from where the event which manipulates the collection gets fired one or the other is affected. I suspect that the split form is in essence not a single form, but rather 2 instances of the same form-class.
A Solution is to Declare a collection in a separate module "Coll":
Option Compare Database
Dim mColl as new Collection
Public Function GetColl() as Collection
Set GetColl= mColl
End Function
And then remove the Declaration of the Collection in the SplitFormclass and Declare the Collection in every Function or Sub by referencing the collection in the separate Module like in the following example:
Option Compare Database
Private Sub AddPersToColl(PersonId As Long)
Dim Pers As Person
Dim PersColl as Collection
Set PersColl = Coll.GetColl
Set Pers = New Person
Pers.ID = PersonId
If HasKey(PersColl, CStr(PersonId)) = False Then
PersColl.Add Item:=Pers, Key:=CStr(PersonId)
Else: MsgBox "Person Bereits ausgewählt"
End If
End Sub
This forces the form to use the same Collection regardless if the event is fired from the form or datasheet part of the split-form. Any Further information is appreciated but the matter is solved for now.
Thanks everyone for their time

Show progress on UserForm without percentage update

I want to show progress while my program is doing everything it does, to know that it is working and not failed. Problem is, I don't use any index, For...Next, While, or anything I can use to call my progress-bar update.
The program:
opens 2 books
copies a bunch of rows from one to another and
refreshes all the data/graphics in many other sheets.
As I don't have a loop through all the rows (which was my main reference point for progress-bar), I'm not sure how I can show the progress.
Sorry if I can't be more specific but I'm not sure if I can share any of the code. If you need more information I could try to post some in here.
My progress-bar code is:
Public Sub UpdateProgressBar(PctDone As Single)
With ProgressForm
' Update the Caption property of the Frame control.
.FrameProgress.Caption = Format(PctDone, "0%")
' Widen the Label control.
.LabelProgress.Width = PctDone * (.FrameProgress.Width - 10)
End With
' The DoEvents allows the UserForm to update.
DoEvents
End Sub
Public Sub UpdateProgressText(txt)
ProgressText.Caption = txt
End Sub

Declaring WebBrowser in VB.NET Other Form

I am working on a project that has WebBrowsers in Other Forms;
I wrote the code below to control these WebBrowsers; but I need the code to recognize (Declare) the WebBrowsers of these forms.
Dim openForm As Form = Nothing
For Index As Integer = My.Application.OpenForms.Count - 1 To 0 Step -1
openForm = My.Application.OpenForms.Item(Index)
If openForm IsNot Me Then
MyWebBrowser.navigate("http://www.google.com/") ' PROBLEM IN THIS LINE
End If
Next
My Module created them as below:
Module MMMBrowser
Dim frmNew As New Form
Dim MekdamBrowser As New WebBrowser
Other info gleaned from comments:
there is form factory of some sort which creates new frmNew
there are many of these open at a time, which is the reason for the backwards loop thru OpenForms to find the last one.
The MekdamBrowser reference is an attempt to refer to the browser on the form.
The easy things is to provide a way for outsiders to tell the form to navigate somewhere using a new Sub, and let the form drive the browser control. This probably eliminates the need for a global MekdamBrowser reference. In the browser form add something like this:
Public Sub GotoNewURL(url As String)
myWebBrowserName.navigate(url)
End Sub
This procedure only exists for Form1 not the generic Form type, so we need to change how you find the form to use. Your existing loop is wonky. It will only ever find the last instance of a form which is not the current form. If you add a third form type, it wont work well:
Dim lastBrowserFrm As Form1 ' use the class name!
' this will try to get the last Instance of Form1
lastBrowserFrm = Application.OpenForms.OfType(Of Form1)().LastOrDefault
' LastOrDefaultcan return nothing if there are none,
' so test
If lastBrowserFrm IsNot Nothing Then
lastBrowserFrm .GotoNewUrl("www.stackoverflow.com")
Else
' create a new one, I guess
End If
Your loop was not considering that there could be other form types in the collection which are not Form1 or even if a new browser form was the last one created! This is more important now because GotoNewURL is only available on Form1 instances.
I changed the name to lastBrowserFrm to reflect what is really going one - it will just find the last one created. If you are trying to work with a specific instance, you need to provide a way to track the ones you create such as with a List(of Form1) or use the Name property so you can tell one from the other. As is, you do not a way to get back a specific form instance.

VBA Listbox becomes unresponsive after first use

I have a VBA (Excel 2010) system which involves selecting an item from a listbox and then displaying it in another form. Here is a very simplified version of what happens.
' Part of frmForm1 code module
sub lstListbox_Click
dim MyEvent as string
dim i as integer
i=me.lstListbox.listindex
MyEvent=me.lstlistbox.list(i)
' Now show the item in the second form
Load frmForm2
me.hide
ThisWorkbook.LoadDataIntoForm2 (frmForm2, MyEvent)
frmForm2.show
unload frmForm2
me.show
end sub
The listbox accepts the click, and first the event (the event handler is giver above). Key parts of the event handler are:
Load the second form (to display the detail data)
Pass the second form as a UserForm parameter to a procedure (LoadDataIntoForm2)
Hide the host form (frmForm1) and show the second form (frmForm2)
When the second form processes an Exit click, the code looks like this:
' Part of frmForm2 code module
sub cmdExit_Click
me.hide
end sub
The first time round it works fine - but when I return to frmForm1 (in the tail end of the lstListBox_Click procedure), even though the rest of the form is operative, the listbox remains stubbornly unresponsive.
I've managed to abstract this down to a little demo system if that would help - the same behavior is seen there. (It's regular .xls file, but that seems not to be easily acceptable as an upload)
Has anyone seen this before? And does anyone have any ideas how I might get this to work the way I want it to?
Thanks,
Tony
The default for the .Show method is to make the form modal. Explicitly set it to modeless:
Sub lstListbox_Click
...
Me.Show vbModeless
End Sub