I have a WinForm that I want to pass a variable to. This form may or may not already be open. The code I was using would check to see if the form was open (using an IsOpen function) and then do a .BringToFront if it was open, or it would do a .Show if it was not open.
Now with trying to use New() to pass the variable, things are not working as expected. Creating the form variable, using Dim AdBook As New frmAddressBook, means a new instance of the form is created. When I run the IsOpen function, it does see the form. However, since I created a "new" form, when I run the AdBook.FillPatientInfo function of that form, the other variables have not been set up because that "new" form has not actually been loaded.
The only solution I have found thus far is to close the form and then open it again. But that sounds like more overhead than it should be. And besides, I'm not sure how I would close a form that I can't get a hold of.
Form 1 Click Event:
Private Sub tsmiAddressBook_Click(sender As Object, e As EventArgs) Handles tsmiAddressBook.Click
If IsDBNull(dgvPhysician.CurrentRow.Cells(1).Value) Then Exit Sub
Dim AdBook As New frmAddressBook(CInt(dgvPhysician.CurrentRow.Cells(1).Value))
If IsOpen(AdBook, Reflection.MethodBase.GetCurrentMethod().Name) Then
AdBook.BringToFront()
AdBook.FillPatientInfo(CInt(dgvPhysician.CurrentRow.Cells(1).Value))
Else
AdBook.Show(Me)
End If
End Sub
Form 2 New() code:
Private intID As Integer = Nothing
Public Sub New(ByVal newID As Integer)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
intID = newID
End Sub
Code that checks to see if form IsOpen:
Public Function IsOpen(ByVal frm As Form, SubName As String) As Boolean
Try
Dim frmCol As New FormCollection()
frmCol = Application.OpenForms
Dim Cnt As Integer = 0
For Each f As Form In frmCol
If f.Name = frm.Name Then Cnt += 1
Next
Return CBool(IIf(Cnt > 0, True, False))
Catch ex As Exception
CustExErrorMsg("Functions", Reflection.MethodBase.GetCurrentMethod().Name, ex.Message)
Return False
End Try
End Function
Related
I have created 2 forms.
The first one is the button that you want to back up.
In the second there are paths that can be modified.
How to make a reference that after pressing the "backup" button will get a path of 2 forms.
The path is saved when I closed form2
I know how to do it in one form but unfortunately I can not refer to another form.
Source of Form 2:
Private Sub Browser_from1_Click(sender As Object, e As EventArgs) Handles Browser_from1.Click
Dim FolderBrowserDialog1 As New FolderBrowserDialog
FolderBrowserDialog1.ShowDialog()
TextBox1from.Text = FolderBrowserDialog1.SelectedPath
If Browser_from1.Text <> "" And TextBox1from.Text <> "" Then
Backup.StartCopy.Enabled = True
End If
End Sub
Private Sub Browser_to1_Click(sender As Object, e As EventArgs) Handles Browser_to1.Click
Dim FolderBrowserDialog1 As New FolderBrowserDialog
FolderBrowserDialog1.ShowDialog()
TextBox2to.Text = FolderBrowserDialog1.SelectedPath
If Browser_to1.Text <> "" And TextBox2to.Text <> "" Then
Backup.StartCopy.Enabled = True
End If
End Sub
Private Sub TextBox1from_TextChanged(sender As Object, e As EventArgs) Handles TextBox1from.TextChanged
End Sub
Private Sub save_settings_Click(sender As Object, e As EventArgs) Handles save_settings.Click
My.Settings.pathmem = TextBox2to.Text
My.Settings.pathmem1 = TextBox1from.Text
My.Settings.Save()
End Sub
Private Sub setting_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1from.Text = My.Settings.pathmem1
TextBox2to.Text = My.Settings.pathmem
End Sub
End Class
You dont want to create a reference to a form - that would (or could) create a whole new form. You want to hold onto the form reference.
This is done by passing a reference to the forms, but the talk of one form fiddling with the controls on another form is a bad idea because it breaks encapsulation. But forms are classes (it says so at the top of each one), so you can add Properties and Methods (Sub and/or Functions) to facilitate passing information back and forth.
Method One - Passing a Form Reference
The simplest way is to pass whatever the other form needs in the constructor:
' form 1 / "main" form / form to return to
Dim frm As New Form6(Me)
frm.Show()
Me.Hide()
In order for this to work, you need to modify the constructor (Sub New) on the destination form:
Private frmReturnTo As Form
Public Sub New(f As Form)
' This call is required by the designer.
InitializeComponent()
frmReturnTo = f
End Sub
It is best not to create your own constructor until you are familiar with them. Use the drop downs at the top of the code window: from the left pick the form name; from the right, select New. The designer adds required code to them which must not be changed.
Do not add any code before the InitializeComponent() call at least until you are familiar with the life cycle of a form. The form and its controls do not exist until that runs.
To return to the "main" form:
If frmReturnTo IsNot Nothing Then
frmReturnTo.Show()
End If
You may want to remove some of the title bar buttons or add code to the form Closing event to handle when the user closes via the system menu or buttons.
Using the constructor is ideal for cases where there is some bit of data which the form must have in order to do its job.
Method Two - Passing Data
Thats all well and good, but what about passing data to another form? You can use the constructor for that too. In order to pass say, a string, integer and a Point:
' destination / second form:
Public Sub New(a As String, b As Int32, c As Point)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Label1.Text = a
Label2.Text = b.ToString
Label3.Text = c.ToString
End Sub
Call it like this:
' method two: pass data you want to share in the ctor
Dim frm As New frmData("hello", 6, New Point(150, 550))
frm.Show()
Result:
Method Three: Properties
Thats fine, but if there is a lots of data that way can get cumbersome. Plus, you may want to update some of the data from the calling/main form. For this you can create Properties on the form to handle the data:
Public Property Label1Text As String
Get
Return Me.Label1.Text
End Get
Set(value As String)
Me.Label1.Text = value
End Set
End Property
Rather than a private variable to act as the backing field, one of the controls is used. The name leaves a bit to be desired as it exposes implementation details. So, use names which describe what the data represents rather than where it displays.
Public Property SpecialValue As Integer
Get
Return Integer.Parse(Me.Label2.Text)
End Get
Set(value As Integer)
Me.Label2.Text = value.ToString
End Set
End Property
Public Property SomePoint As Point
Get
Dim data = Me.Label3.Text.Split(","c)
Return New Point(Convert.ToInt32(data(0)),
Convert.ToInt32(data(1))
)
End Get
Set(value As Point)
Me.Label3.Text = value.X.ToString & "," & value.Y.ToString
End Set
End Property
A point was used just to show that other data types can be used. Setting those values from the calling/original/source form:
Using frm As New Form6
frm.Label1Text = "Ziggy"
frm.SpecialValue = 42
frm.SomePoint = New Point(111, 222)
frm.ShowDialog()
' do stuff here with any changes
Dim theint = frm.SpecialValue
End Using ' dispose of dialog
The destination controls would well have been TextBoxes for the user to edit. The Property "wrappers" allow you to fetch those values back, so in this case, a Dialog was used.
Method Four: Methods
You can also use methods as a way to pass data to the second/helper form. Here a List(of T) collection will be passed. In the child/display form a method is added to receive the data which it then displays. The task represented is proofing or viewing a filtered list:
Public Sub UpdateDisplay(lst As List(Of SimpleItem), filter As String)
DataGridView1.DataSource = lst
Label1.Text = String.Format("{0} Total {1} Items", lst.Count, filter)
End Sub
In the main/calling form:
' form level variable
Private frmDV As frmDataView
elsewhere...perhaps in a Click event:
' myList is a simple list of items
' Users pick which color to filter on via a combo box
Dim filter As String
If cboListFilter.SelectedItem IsNot Nothing Then
'Dim frmDV As New frmDataView
If frmDV Is Nothing OrElse frmDV.IsDisposed Then
frmDV = New frmDataView
End If
filter = cboListFilter.SelectedItem.ToString()
' apply the filter
Dim tmpList = myList.Where(Function(w) w.Color = filter).ToList()
frmDV.UpdateDisplay(tmpList, filter)
frmDV.Show()
Else
Return
End If
Result:
With DataBased apps a modified version of this can allow for the case where you display DataGridView data in detail form on another form. You need not have the second form rung SQL to add or update the record, and then the main form running another query to "refresh" the display. If the DataSource is a DataTable backed up by a fully configured DataAdapter, pass the DataTable and have the child form add, change or delete using that. The data will automagically be in the DataTable and DataGridView`.
There are other ways to do this, but they generally all boil down to passing something from A to B. Which way is "best" depends on what the app does, the use-case and the nature of the data. There is no one right way or best way.
For instance, Properties and in many cases Functions allow the B Form to close the feedback loop. With DB items, a DataChanged property might tell the calling form that data was added or changed so that form knows to use the DataAdapter to update the db.
'SECOND FORM
Public class secondForm (blah blah)
Public overloads property owner as myMainForm
'Must be only the form you prepared for that
Private sub secondForm_load(blah blah) handles blah blah
Texbox1.text=Owner.customcontrol.text
End sub
End class
'MAIN FORM
public class myMainForm(blah blah)
Private sub button1_click(blah blah) handles blah blah
Dim NewSecondForm as secondForm = New secondForm
NewSecondForm.owner(me)
NewSecondForm.show(me)
NewSecondForm.dispose()
' so you can have bidirectional communication between the two forms and access all the controls and properties from each other
End sub
End Class
I have to write a log of codes when open new Windows Form.
Can I make a function to run this command? by call it with "fDeliveryNotation" only
Case "Delivery Notation"
If Application.OpenForms().OfType(Of fDeliveryNotation).Any Then
For Each f As Form In Application.OpenForms
If TypeOf f Is fDeliveryNotation Then
f.Activate()
Exit For
End If
Next
Else
Dim NewMDIChild As New fDeliveryNotation
NewMDIChild.MdiParent = Me
NewMDIChild.Show()
NewMDIChild.WindowState = FormWindowState.Maximized
End If
Code for open new window when window not exist if exist activate it.
Generic function:
Public Sub OpenForms(Of T As {New, Form})()
Dim tForms = Application.OpenForms.OfType(Of T)()
If tForms.Any() Then
tForms.Select(Function(x) x).First().Activate()
Else
Dim NewMDIChild As New T
NewMDIChild.MdiParent = Me
NewMDIChild.Show()
NewMDIChild.WindowState = FormWindowState.Maximized
End If
End Sub
Usage:
Public Sub Foo()
OpenForms(Of fDeliveryNotation)()
''OpenForms(Of Application)() Compile Error since Application is not a Form
End Sub
EDIT:
Added constructor constraint for T and create new instance of T instead of hard coded Form
I have a custom control that has a panel where other controls display. Whenever a control is displayed, I add it to a List(Of Control) called UCList. Then, when they hit the Start Over button, I have a recursive function that clears each control, and any children, in the UCList.
Do lists deal with ref/val differently? When I call my ResetUC function, a breakpoint at the call shows that "oCon is oCashCash" however, as soon as I get into the Reset Function with variable passed Byref, the above is false. It still is the same type, has the same name, and children(with same values), etc. But it is a new entity. Simply changing it to accept a Byval instead of Byref, and everything is working ("oCon is oCashCash" even after passing into the reset function).
This behavior seems backwards to me. Is my understanding wrong, or do lists deal with reference differently for some reason? This is the first time I have been confused with how ref/val responds in quite a long time.
Edit:
control-level variable
Public UCList As List(Of Control)
Where control is added to list (most omitted)
ElseIf ControlName = "ConfirmCashCash" Then
If Not UCList.Contains(oCashCash) Then
UCList.Add(oCashCash)
End If
pnlMain.Controls.Add(oCashCash)
Startover sub click event (originally was foreach, but I was trying to figure it out). At this point, oCon is oCashCash
--Edited to show full button per request in comment--
Private Sub btnStartOver_Click(sender As Object, e As EventArgs) Handles btnStartOver.MouseUp
If pnlMain.Controls(0) Is oConfirmFinalize AndAlso oConfirmFinalize.btnFinalize.Text = "Finalize" Then
If MessageBox.Show("Deal Is Not Finalized. Are You Sure You Want To Leave This Page?", "Finalize Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) = Windows.Forms.DialogResult.No Then
Return
End If
End If
If Not btnBack.Enabled Then
If MessageBox.Show("Deal Is Editing And Not Finalized. Are You Sure You Want To Leave This Page?", "Loss of Work Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) = Windows.Forms.DialogResult.No Then
Return
End If
End If
ImpersonateUserID = String.Empty
'For Each con As Control In UCList
For i As Integer = 0 To UCList.Count - 1
Dim oCon As Control = UCList(i)
ResetUC(oCon)
Next
'ResetUC(New Control)
'Next
oConfirmFinalize.oLastCon = Nothing
oConfirmFinalize.oConfirm = New Confirm()
UCList.Clear()
'ResetUC(pnlMain.Controls(0))
ChangeUC("ConfirmMain", True, Nothing)
btnBack.Enabled = True
btnDelete.Visible = False
End Sub
Reset sub - byref it is NOT oCashCash; however, byval, it IS. (most omitted)
Private Sub ResetUC(ByVal oCon As Control)
For Each con As Control In oCon.Controls
If TypeOf con Is ConfirmIndexDetail Then
DirectCast(con, ConfirmIndexDetail).DateNavigator1.DateTime = DateTime.Now()
End If
If con.HasChildren Then
For Each child As Control In con.Controls
ResetUC(child)
Next
End If
'Code that clears fields.
Next
End Sub
Currently in VB.NET I have two public subs like so:
Public Sub addmember1()
'Stuff
End Sub
Public Sub furtherinfo1()
'suff
End Sub
I haven't included the code where "'stuff" is as it is very long and is exactly the same in each sub, however the underlying the principle remains the same.
A certain sub is ran depending on a boolean value. like so...
If add_member = True Then
addmember1()
ElseIf add_member = False Then
furtherinfo1()
End If
How would I use one function to carry out the same procedure as above? (my current solution works but involves repeating the same section of code twice )
I tried the following however was unsuccessful
Public Function forms(ByVal frm As Windows.Forms.Form)
'stuff
End Function
and then run the function like so... (addmember and furtherinfo are the two forms I am working with)
If add_member = True Then
forms(addmember)
ElseIf add_member = False Then
forms(furtherinfo)
End If
here is the paste bin of all the code for context it's in modual and I want to use it for writing information to a word document. Lines 20-71, 76-128, 160-164 is what I am on about.
http://pastebin.com/xWD0RBuh
You can pass the form object to a Sub() in a module as below
Module Printing
Dim StrToAdd As String
Sub MySub(ByVal frm As Form)
'The first line is your code
StrToAdd = "Firstname: " & addmember.txtName.Text
'Change it to as below using frm.Controls("controlname").Text
StrToAdd = "Firstname: " & frm.Controls("txtName").Text
End Sub
End Module
After looking at your code at http://pastebin.com/xWD0RBuh it seems you have a global module with multiple sub routines. Each sub routine has references to controls (such as text boxes) on a form instance. This means each global module sub routine needs to have access to this form instance.
You have only copied in part of the application - the global module, but you have not copied in the form definition. I presume you have a form called addmember, but I don't see it in the example - aside from references in the global module.
Not sure how you use the sub routines - probably a click of a button. If it were me, I would create a class object with properties that hold the data to pass around - one property for each control on the form you want to print. On the click of the button, I would create an instance of the class and copy the values from the form controls into the class properties. I would then pass the instance of the class to the sub routines, and I would alter the sub routines to refer to an instance of the class instead of an instance of a form. That would provide a level abstraction between the form (UI) and the behavior (the sub routines that print). I may even go "crazy" and use an interface.
Does your code compile as-is?
The Problem is that your two Forms are two different classes. Even though you named your controls
the same, you can't just access them simply by frm.txtUsername.
What you could do is iterate through all controls of each form und find them by name:
Public Sub DoStuff(frm As Form)
Dim txtUsername As TextBox = GetControlByName(frm, "txtUsername")
txtUsername.Text = "Hello World"
End Sub
Private Function GetControlByName(container As Control, name As String) As Control
Dim retVal As Control = Nothing
If Not TryGetControlByName(container, name, retVal) Then Throw New ApplicationException("control not found")
Return retVal
End Function
Private Function TryGetControlByName(container As Control, name As String, ByRef ctl As Control) As Boolean
For Each item As Control In container.Controls
If item.Name = name Then
ctl = item
Return True
End If
'If item is a Container (like GroupBox, Panel) check its children
If TryGetControlByName(item, name, ctl) Then Return True
Next
Return False
End Function
If you want to get really fancy you could define a Class with the common Controls and fill them via a little
bit of reflection magic. Though this might be overkill:
Public Sub DoStuff2(frm As Form)
Dim wrapper As New CommonForm(frm)
wrapper.txtUsername.Text = "Hello Wolrd"
End Sub
Public Class CommonForm
Public Property txtUsername As TextBox
Public Property txtFoo As TextBox
Public Property txtBar As TextBox
'Add more Controls here...
Public Sub New(frm As Form)
For Each item In Me.GetType().GetProperties()
Dim value = GetControlByName(frm, item.Name)
item.SetValue(Me, value, Nothing)
Next
End Sub
Private Function GetControlByName(container As Control, name As String) As Control
Dim retVal As Control = Nothing
If Not TryGetControlByName(container, name, retVal) Then Throw New ApplicationException("control not found")
Return retVal
End Function
Private Function TryGetControlByName(container As Control, name As String, ByRef ctl As Control) As Boolean
For Each item As Control In container.Controls
If item.Name = name Then
ctl = item
Return True
End If
'If item is a Container (like GroupBox, Panel) check its children
If TryGetControlByName(item, name, ctl) Then Return True
Next
Return False
End Function
End Class
My specific question is: How to ignore windows events when in a child form called from that event handler?
Some context: My application captures a fingerprint, and puts up the next form (secondForm) when a print is recognized against the database. I want to ignore any prints they put in while their secondForm is up. The trouble is that when people press the print multiple times, or while their secondForm is up, then the events are queued until after the secondForm closes, so I get multiple calls to the event handler for that person.. I've tried so many ways around this, including calling the routine that open secondForm as a delegate(is that even appropriate), putting a global boolean in the event handler, etc. If I disable fingerprint capture during the oncomplete eventhandler, my form never shows up.
Am I missing something obvious to you here? Much gratitude for any ideas...
Imports DPFP.Capture ' DigitalPersona fingerprint reader library
' Simple example to capture a fingerprint from DigitalPersona fingerprint reader.
Public Class SimpleFP
Implements DPFP.Capture.EventHandler
Private mIgnore As Boolean
Public eventHandlerComplete As DPFP.Capture.EventHandler
Public WithEvents mCapture As DPFP.Capture.Capture
Private Sub WaitForFPrint_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
' set up finger print capture
mCapture = New DPFP.Capture.Capture
mCapture.EventHandler = Me
mCapture.StartCapture()
Catch ex As Exception
MsgBox("Problem starting fingerprint reader: " & ex.Message)
End Try
End Sub
Public Sub FPOnComplete(ByVal Capture As Object, ByVal sernum As String, ByVal sample As DPFP.Sample) _
Implements DPFP.Capture.EventHandler.OnComplete
'mCapture.StopCapture()
'This is what I want to do, but when I leave this in, my secondForm won't stay up
Dim s As String = displaySecondScreen(sample)
showStatus(s)
'mCapture.StartCapture()
'This is what I want to do, but when I leave this in, my secondForm won't stay up
End Sub
' Put up second form. This is called from the fingerprint OnComplete event handler.
Private Function displaySecondScreen(ByVal sample As DPFP.Sample) As String
Dim status As String = "OK"
Try
Dim x As DPFP.FeatureSet
x = extractFeatures(sample)
' This is where I match the fp in my code, but I removed here to simplify
If True Then
Dim frm As New frmSecondForm
frm.ShowDialog(Me)
frm.Dispose()
Else
' No fingerprint match - normal case
status = "Fingerprint not recognized"
End If
Catch ex As Exception
status = "Exception during fingerprint verification: " & ex.Message
End Try
Return status
End Function
' Made rudimentary for this example, but I thought some person new to dpfp may be able to use this
Private Function extractFeatures(ByVal sample As DPFP.Sample) As DPFP.FeatureSet
Dim extractor As New DPFP.Processing.FeatureExtraction()
Dim feedback As New DPFP.Capture.CaptureFeedback()
Dim features As New DPFP.FeatureSet()
extractor.CreateFeatureSet(sample, DPFP.Processing.DataPurpose.Verification, feedback, features)
If feedback = DPFP.Capture.CaptureFeedback.Good Then
End If
Return features
End Function
Private Sub debugOut(ByVal s As String)
Console.WriteLine(s)
End Sub
' Display status on first form
Delegate Sub showStatusCallback(ByVal s As String)
Private Sub showStatus(ByVal s As String)
If lblStatus.InvokeRequired Then
Dim d As New showStatusCallback(AddressOf showStatus)
Me.Invoke(d, New Object() {s})
Else
lblStatus.Text = s
End If
End Sub
' ... followed by other implements dpfp eventhandlers not in use....
' ....
End Class