Throughout our program, forms are opened like this:
FormName.SomeValue = 10
FormName.ShowDialog()
rather than the usual
Dim myForm As New FormName
myForm.SomeValue = 10
myForm.ShowDialog()
(There is nothing we could do about this - this was done automatically by the Visual Studio VB6 --> VB.Net converter)
The problem is that when forms are closed, they seem to not really be closed, only hidden - if I add some text to a textbox and close/reopen the form, the text is still there, rather than the textbox being cleared like normal. This is presumably because the form always uses the same instance.
Is there any easy way to fix this other than going through the entire program and creating a new form instance for every ShowDialog() call (there are hundreds)?
We considered resetting every control in every form's Load event, but that would still be a pain, so we figured we'd ask if there's a simpler way first.
public class MyForm: Form{
private static MyForm myForm = null;
public static DialogResult ShowDialog(bool newForm){
if(newForm)
{
if(myForm != null)
myForm.Dispose();
myForm= new MyForm();
}
return myForm.ShowDialog();
}
public static DialogResult ShowDialog(){
return ShowDialog(true);
}
}
What you are dealing with is called the form's "default instance" and is a carry over from the VB6 days. It is not recommended practice to use it. You may not want to hear it, but the best long-term strategy for your code base is to rewrite the form initializers the correct way than to do some hacky workaround in the form Load() events. You may hate it now, but you will appreciate it the next time you have to work on this code. You can probably even put together a snippet to do most of the typing for you.
Edit:
How to display the form using the Using statement
Using formName AS New FormName
formName.SomeValue = 10
formName.ShowDialog()
End Using
It appears from the code displayed here that there is now a static ShowDialog call that was added to your FormName class. You should be able to edit just this method to dispose of the old form and create and display the new one. This would help you avoid changing code all over the place, just in the one location.
You asked for easy way to fix this:
Change your ShowDialog() procedure/function calls in the following way:
AS PROCEDURE | AS FUNCTION
|
FormName.ShowDialog() | r = FormName.ShowDialog()
FormName.ShowDialog() | r = FormName.ShowDialog()
|
CHANGE TO | CHANGE TO
|
Call New FormName.ShowDialog() | r = New FormName.ShowDialog()
Call New FormName.ShowDialog() | r = New FormName.ShowDialog()
I know this is super late but-
Form1.Dispose()
works for me. It resets the textboxes.
If the question is about clearing text boxes then I would have Cleared all of them RECURSIVELY
For Each Control in Controls
If Control is type of TextBox
Control.Clear
Next
If you are binding controls by any DATASOURCE I would suggest to clear the Datasource and REBIND
Override the ShowDialog() Method.
Related
I have Two Forms
In The First Form I Have A Textbox Called TB1 An A String Called S1
In The Second Form I Have A Label Called L1
I Declared them publicly in a module
Module M1
Public L1 As New Label
Public TB1 As new Textbox
Public S1 As new String
End Module
/../
'in the First form
S1=TextBox1.text
'in the second form
L1.text=S1
But I Get this error "System.NullReferenceException" on the first Label L1
Any ideas why i'm getting this error
Try something like this:
On Form1 in the event calling Form2, Add:
Form2.L1.Text = TextBox1.text
Form2.Show()
You can't put them in a module like that; nothing will see them.
In addition, there's no way to see how this code is flowing.
To do a simple exercise:
Draw first form - form1 with Textbox1 on it.
Make the first form startup form (it will be by default in new winforms project)
Draw 2nd form - form2 with Label1 on it.
Put a button on the first form
Double click the button
In the handler put this code:
Sub Button1_Click (etc...
dim Form2Instance as new Form2
Form2Instance.Label1.Caption = Textbox1.text
Form2Instance.ShowDialog
End Sub
You can put a textbox on form2 and read the results in form1 the same way.
You can tell if the user OK'd (not canceled) form2 like this:
if Form2Instance.ShowDialog() = DialogResults.OK then
' only do this code if they HIT OK
I suggest maybe hitting youtube or pluralsite and watching a 'winforms for beginners' video, if such a thing exists. There are a lot of traps and pitfalls that you will run into hacking away on your own, that will be explained to before hand if you learn the basics. This is especially true if you've only done web programming or are just learning programming.
I have 20+ MDI forms with consistently named Public variables. When the child form closes a method on the MDI Parent is called passing Me as a generic form type. How can I access the public variables by name via the Form reference? I only need to read the variables. Of course the Variables() method does not exist...
Public Sub CleanupForm(ByVal frm As Form)
Dim sTable_Name As String = frm.Variables("TABLE_NAME") ' Public at form level
Dim cLock As clsRecLocks
cLock = frm.Variables("Rec_Lock")
cLock.DeleteThisLock()
'..
I've seen some posts on similar requests but most start out with "don't do it that way..." then go off in the weeds not answering the question. I concede it is poor design. I can't change all the calling forms in the short term so I need to use this approach.
VS2010, VB.Net, Win Forms, .Net 2.0
I was able to get to a simple variable using CallByName:
Try
Dim s As String = CallByName(frm, "TABLE_NAME", CallType.Get)
Stop
Catch ex As Exception
MsgBox(ex.Message)
End Try
On to the class object. Perhaps I can add a default Get for the class that returns the ID I need.
Default property won't work as the Locks object was not declared Public - at least for the CallByName() approach.
Can Reflection get to form level variables not declared Public? Seems like a security issue, but...
Can I get a "Parent" reference in the instantiated Locks class? i.e. A reference to the form that established the Locks object? I can change the clsRecLocks() class.
I found a property I could get to that told me the form was "read-only" and I can use that tidbit to delete the correct (or more correct - still not 100%) lock record. So the bug is 90% fixed. I think I need update all the forms with code that records the info I need to get to 100%.
Thanks to all!
Poor design but you can do this:
Public Sub CleanupForm(ByVal frm As Form)
Dim frmTest as object = frm
? = frmTest.TABLE_NAME
? = frmTest.Rec_Lock
End Sub
This will compile and if the variables exist, it will return them but if not, you get an error.
Converting to an interface after the fact is not that hard, you should do it now rather than later.
I have to run a thread create in the code.
In the form1 i have a button that run the new separate thread for elaborate some data, so i need it for not freeze the form.
I have inizialized thread:
dim th as thread = new thread (addressof elaborate)
And at the button.click event:
th.isbackground= true
th.start()
Now, at the form load i have iconized my program, but when i start new thread the tray icon is duplicated from it.
I want to resolve that when start new thread it's not show new notifyicon.
Any ideas?
(i don't have found anything online, only Multiple notification icons appear when using multithreading)
Create a class called Elab
inside that class, put a sub called work
Add a timer to your form that is disabled
with a tickcount of say 1000
Declare this in your form class:
Dim El as Elab
inside Form_Load() put:
El = New Elab()
Under your button, put this:
Dim gThread as new System.Threading.Thread(Address of El.Work)
Timer1.Enabled = True
Inside Elab declare a variable called Result:
Public Result as boolean
When elab has finished whatever it is doing, set result as true, and store the results in public variables you can access later.
Inside the timer:
If El.Result = True then
'Get results, deal with data
end if
This isn't written particularly well, and isn't a working example, but is to mearly point you in the right direction, by giving a thread an address of a sub inside a class, you can then access the same class from other threads, which means your form doesn't freeze, and you're not creating a new instance of your form, you are just accessing an existing classes sub routine; just make sure to give yourself a way to get the results (in this example i suggested a timer, but a "get result" button would do the same job) once the thread has completed.
Remeber:
If you need Elab to finish before a particular part of code can continue (for example, elab might add two numbers, and you need the result to continue) you can start the thread and do this:
Do until El.Result = True
Application.DoEvents()
System.Threading.Thread.Sleep(1)
Loop
gThread.Join()
Hope this helps a little.
So the answer to your question is; don't put your sub inside a form, instead put it in a class that you can create an instance of.
Hey guys I have got to a head scratcher well at least for me any way. I need to find a way of opening a form with a string. I have got this ...
Dim asm = System.Reflection.Assembly.GetExecutingAssembly
Dim myTypes As Type() = asm.GetTypes()
Dim frm As Form
For Each t As Type In myTypes
If t.IsSubclassOf(GetType(System.Windows.Forms.Form)) AndAlso Me.Label4.Text = t.Name Then
frm = CType(Activator.CreateInstance(t), Form)
frm.Close()
frm.Hide()
End If
Next
But it doesn't close the program or even hide it i have no clue?
Question: "I need to find a way of opening a form with a string"
Thanks in advance.
That looks to me like you created a new form instance of that type and tried to close/hide it, but I don't see it ever being shown.
If you are trying to close an existing form, then you don't want to create a new instance using Activator.CreateInstance. Rather you need to somehow locate the existing instance of the form that is already open, and close that specific instance.
The code you posted approximates code that would create a new instance of a form by type name, then close/hide the form.
(If you wanted to close an already-open form by name, I would do:
For Each f As Form In My.Application.OpenForms
If f Is My.Forms.NameOfFormThatIWantToClose Then f.Close()
Exit For
)
But I thought you wanted to open a new form by name. If so, you will need to use reflection. This page seems to do exactly what you want.
This is probably a simple one but I can't seem to figure it out.
I have a bunch of form items created by the form designer declared as (in frmAquRun.Designer.vb)
Public WithEvents btnAquRunEvent1 As VisibiltyButtonLib.VisibilityButton
Public WithEvents btnAquRunEvent2 As VisibiltyButtonLib.VisibilityButton
... etc
And I basically want to be able to supply a number to a function access each of these fields. So I wrote this function. (in frmAquRun.vb)
Const EVENT_BUTTON_PREFIX As String = "btnAquRunEvent"
Public Function getEventButton(ByVal id As Integer) As Windows.Forms.Button
Dim returnButton As Windows.Forms.Button = Nothing
Try
returnButton = DirectCast(Me.GetType().InvokeMember(eventButtonName, Reflection.BindingFlags.GetField Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.Instance, Nothing, Me, Nothing), Windows.Forms.Button)
Catch ex As Exception
End Try
Return returnButton
End Function
But it always seems to be generating field not found exceptions.
The message in the exception is "Field 'ATSIS_ControlProgram.frmAquRun.btnAquRunEvent1' not found.".
The namespace and form name in the message are correct. Any idea what i'm doing wrong?
The problem is that for WithEvents fields, VB actually creates a property that does the necessary event handler attaching and detaching. The generated property has the name of the field. The actual backing field gets renamed to _ + original name.1)
So in order for your code to work just prefix the button name by _ or use the BindingFlag that corresponds to the property getter (instead of GetField).
Alternatively, you can do this a lot easier by using the Controls collection of the form:
returnButton = DirectCast(Me.Controls(eventButtonName), Windows.Forms.Button)
But beware that this only works if the button is top-level, i.e. not nested within a container control on the form.
1) This is an implementation detail of the VB compiler but it’s portable (especially to Mono’s vbnc compiler) since the handling for WithEvents fields is described in great detail in the VB language specifications.
The problem is that the event handlers aren't really fields. As compiled they're really properties that implement add_btnAquRunEventX, remove_btnAquRunEventX and fire_btnAquRunEventX methods. There are ways of using reflection to get around this, but that's probably not the best way to approach the problem. Instead you can simply create a List<> and populate it with the event handlers, then index into that list.
I'm a little rusty in VB syntax but it should look something like this:
Dim events = New List<EventHandler>()
events.Add( btnAquRunEvent1 )
events.Add( btnAquRunEvent2 )
....
events( 0 )( null, EventArgs.Empty )
Take a step back though and evaluate why you're invoking by index. There may be a simpler way of abstracting the whole thing that doesn't involve all this indirection.