I am experiencing a strange behaviour when using RightToLayout layout:
My form automatically closes.
I have created a simple project to reproduce the problem:
Create a new VB.NET project in VS2012 and add forms "Form1" and "Form2" to it.
Set "Form1" to be the start form.
Then add the following code to "Form1":
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.Hide()
Dim f As New Form2
f.ShowDialog()
MessageBox.Show("After dialog closed.")
modControls.setFormRTL(Me) 'This line causes the Form2 to automatically close. Why??? This line should only be processed AFTER the dialog has been shown and closed
End Sub
End Class
Add the following code to "Form2":
Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles Me.Load
modControls.setFormRTL(Me)
End Sub
End Class
Add a module with the name "modControls" to the project.
Add the following code to it:
Module modControls
Public Sub setFormRTL(ByVal uForm As Form)
uForm.RightToLeft = RightToLeft.Yes
End Sub
End Module
Without the setFormRTL, my project works perfectly fine, but with it, "Form2" automatically closes down. You can see this because the messagebox is shown.
When I remove the line
modControls.setFormRTL(Me)
from Form1 load, it works fine again.
Yes, I really mean from "Form1", not from "Form2"!!!
Now this is really strange because it should not matter at all because this line is not processed before the dialog is closed.
I hope somebody understands what I mean.
Can anybody shed some light on what might be happening here?
Yes, you'll get unexpected behavior if you set the RightToLeft property anywhere other than a control's constructor (in VB.NET parlance, that's the New method).
In fact, the constructor is where you should set all of the properties of a form or control object. If you come from VB 6, it might seem logical to do it in the Load event handler, but that's not idiomatic .NET and, as you've discovered, can cause problems when initializing certain properties.
The technical reason for this is that certain properties (like RightToLeft) can actually only be set on the native window (which is how the Form objects used in the .NET world are implemented behind the scenes) at the time that it is created. When you attempt to change the property, the framework code actually has to destroy and then re-create the native window with the new property values.
Change the code to look like this instead:
Public Class Form1
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
modControls.SetFormRtl(Me)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.Hide()
Dim f As New Form2
f.ShowDialog()
MessageBox.Show("After dialog closed.")
End Sub
End Class
Public Class Form2
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
modControls.SetFormRtl(Me)
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles Me.Load
End Sub
End Class
Speaking of non-idiomatic .NET code:
Methods should all be Pascal cased by convention. That means your setFormRTL method should be named SetFormRtl.
A helper function that sets the properties of an object just seems wrong to me. If anything, that's just bad OO design. If you want this method to be available for all of your Form objects, derive a custom form class and add this method (or even do the desired initialization in the constructor). Either way, all forms that you derive from this custom form object will inherit the functionality. Example:
Public Class MyCustomForm : Inherits System.Windows.Forms.Form
Public Sub New()
MyBase.New()
Me.SetRtl()
End Sub
Public Sub SetRtl()
Me.RightToLeft = RightToLeft.Yes
End Sub
End Class
Public Class Form1 : Inherits MyCustomForm
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.Hide()
Dim f As New Form2
f.ShowDialog()
MessageBox.Show("After dialog closed.")
End Sub
End Class
Public Class Form2 : Inherits MyCustomForm
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles Me.Load
End Sub
End Class
can you try this? all i am doing here is setting the RTL before the form is displayed.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Me.Hide()
Dim f As New Form2
modControls.setFormRTL(f)
f.ShowDialog()
MessageBox.Show("After dialog closed.")
modControls.setFormRTL(Me)
End Sub
End Class
and remove code form load event of form 2
Related
I have a usercontrol form named "ucSETTINGS", where there is a textbox and once the button was clicked, the text inside the textbox will be added to the combobox from another usercontrol form name "ucITEMS"
I tried this code but it's not working
(cboCategory is the name of the combobox from ucITEMS, txtNAME is the textbox from ucSETTINGS)
Private Sub btnSAVE_Click(sender As Object, e As EventArgs) Handles btnSAVE.Click
Dim category As New ucITEMS()
category.cboCATEGORY.Items.Add(txtNAME.Text)
End Sub
Can someone help me?
In this sort of situation, the user controls don't know about each other by default and it should stay that way. The source UC just exposes an interface and lets whomever is watching use that as it sees fit. That means raising an event when something happens and exposing required data via properties, e.g.
Public Class SourceControl
Public ReadOnly Property TextBox1Text As String
Get
Return TextBox1.Text
End Get
End Property
Public Event Button1Click As EventHandler
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
OnButton1Click(EventArgs.Empty)
End Sub
Protected Overridable Sub OnButton1Click(e As EventArgs)
RaiseEvent Button1Click(Me, e)
End Sub
End Class
The Text of the TextBox is exposed via a property and, when the user clicks the Button, the UC raises an event.
The destination UC provides an interface for new items to be provided but it adds them to its own ComboBox, e.g.
Public Class DestinationControl
Public Sub AddItemToComboBox1(item As Object)
ComboBox1.Items.Add(item)
End Sub
End Class
The form then plays go-between, handling the event, getting the property and calling the method:
Private Sub SourceControl1_Button1Click(sender As Object, e As EventArgs) Handles SourceControl1.Button1Click
DestinationControl1.AddItemToComboBox1(SourceControl1.TextBox1Text)
End Sub
Obviously you would use something more specific and appropriate than my generic naming.
I have a vb.net application which contains two forms. One is called "MapComponentForm" and another called "ComponentPropertiesForm". In the MapComponentForm I have defined an event which I want fired when the OK button is clicked. If the ComponentPropertiesForm is open, I want it to "hear" this event and act accordingly. Stepping through the code the event seems to fire as expected but the ComponentPropertiesForm seems to be oblivious to this. I've attached the code from the two forms after stripping out the non relevant code in hopes that someone can tell me why my event goes unheeded. I used the information here:
https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/events/walkthrough-handling-events
in constructing my own code.
Thanks for any suggestions.
Public Class MapComponentForm
Public Event PartMapped(ByRef status As Boolean)
Public Sub New(shape As Visio.Shape)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_shape = shape
End Sub
Private Sub OkButton_Click(sender As Object, e As EventArgs) Handles OkButton.Click
'If the component properties window is open, refresh it
If Application.OpenForms().OfType(Of ComponentPropertiesForm).Any Then
RaiseEvent PartMapped(True)
End If
end sub
end class
Public Class ComponentPropertiesForm
Private WithEvents mPartMap As MapComponentForm
Private Sub ComponentPropertiesForm_Load(sender As Object, e As EventArgs) Handles Me.Load
mPartMap = New MapComponentForm(Nothing)
End Sub
Private Sub mPartMap_PartMapped(ByRef status As Boolean) Handles mPartMap.PartMapped
If status = True Then
MsgBox("something got mapped")
End If
End Sub
end class
I'm trying to pass a value from one form, let's call it Form1 to another form, Form2. When i double click a row in a listview that is in Form2, the value should be passed to a combobox in Form1.
I could get one of my other forms to do this using an event called PropertyChanged, but I can't seem to get it to work on other forms. I don't know if it is the fact that you can only have 1 event in the entire project and not have another with the same name. I'm missing something, but i just don't know where.
This is the code i used in Form2:
Public Event PropertyChanged As Action(Of Object)
Private Sub ListView2_DoubleClick(sender As Object, e as EventArgs) Handles ListView2.DoubleClick
RaiseEvent PropertyChanged(ListView1.SelectedItems(0).SubItems(0).Text)
End Sub
And this is the code I used in Form1:
Dim WithEvents f2 As Form2
Private Sub PropertyChanged(obj As Object) Handles f2.PropertyChanged
cmb_form1.Text = obj
End Sub
There are several ways to implement this. This one is quick and dirty.
It consists of writing up a property in Form2 which is public. When the user makes a choice, it goes in the property. Then Form1 reads the property before it disposes Form2.
Here's a little code snippet to better illustrate what I mean:
Public Class Form1
' This form has a button which opens a Form2 instance
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim form2 As New Form2 ' instantiate Form2
form2.ShowDialog() ' the user chooses a value
MessageBox.Show(form2.Result) ' get it before it's out of scope!
End Sub
End Class
Public Class Form2
Public Property Result As String ' the value is stored in there, it can be any type
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Result = "Clicked!" ' store the value you want to share between forms
Me.Close()
End Sub
End Class
Have fun!
I have a form (frmwizard) which I am using to create a wizard like interface. The form contains a usercontrol and a button (for testing). There is also a function on the form called "NextPage"
The form loads a usercontrol (ucpage1) on load and the usercontrol has a button on it that when clicked attempts to call a function on the main form as per below:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
frmWizard.Nextpage(me.name)
End Sub
Within my function called "NextPage" have the following for testing.
Public Sub NextPage(ByVal CurrentPage As String)
MessageBox.Show(UserControl1.Controls.Count)
End Sub
When I call the function from the form itself (via the button) I get the result of 1, when i call the function via the User Control, I get the result as 0
I'm sure there is something simple I need to do, but i'm unsure what i've overlooked.
I am trying to make the button on the user-control Save the data within the control and then to browse to the next wizard page. Hopefully this is enough information
Codependance is a bad idea, as it locks the two types together for the future. If your user control really needs to invoke the form, you should instead have it raise an event and handle the event in your form.
Public Class MyUserControl
Public Event OnNextPage(ByVal sender As Object, ByVal e As EventArgs)
Private Sub btnNextPage_Click(sender As Object, e As EventArgs) Handles btnNextPage.Click
RaiseEvent OnNextPage(Me, New EventArgs)
End Sub
End Class
Public Class Form1
Private Sub MyUserControl_OnNextPage(sender As Object, e As EventArgs) Handles MyUserControl1.OnNextPage ' , MyUserControl2.OnNextPage, etc...
MessageBox.Show(DirectCast(sender, MyUserControl).Controls.Count)
End Sub
End Class
This is my situation, there are 2 Classes and my main form Form1:
Class1: has a method doSomethingAndCall(callback) which creates a new thread
Class2: has dynamic created controls with a button that fires Class1.doSomethingAndCall(newCallback)
in code it looks like this (it starts at Class2.Button_Click):
Class Class1
public shared sub doSomethingAndCallAsync(state as object)
Console.WriteLine(Form1.InvokeRequired) 'output: false
Console.WriteLine(Form1.IsHandleCreated) 'output: false
Form1.Invoke(state.callback) 'throws System.InvalidOperationException
end sub
public shared sub doSomethingAndCall(callback as object)
System.Threading.ThreadPool.QueueUserWorkItem(AddressOf doSomethingAndCallAsync, New With {.callback = callback})
end sub
End Class
Class Class2
Public Delegate Sub doSomethingDelegate()
Public Sub doSomething()
Console.WriteLine("success!")
End Sub
Public Sub Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Class1.doSomethingAndCall(New doSomethingDelegate(AddressOf doSomething))
End Sub
End Class
The exact exception I get is:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created
and as I can see the console.WriteLine in line 4 shows me that the form is realy not created. So I added this handlers, and now it get's really confusing:
Private Sub Form1_HandleCreated(sender As Object, e As System.EventArgs) Handles Me.HandleCreated
Console.WriteLine("Handle created") 'Output: Handle created, when running program
End Sub
Private Sub Form1_HandleDestroyed(sender As Object, e As System.EventArgs) Handles Me.HandleDestroyed
Console.WriteLine("Handle destroyed") 'Will never Output!
End Sub
So it's created and never destroyed but if i click the button it's nevertheless not avaible? -Can anyone explain me what is going on and how to call a callback correct, thanks!
The instance of My.Forms.Form1 aka. Form1 will be different in each thread. You need a handle to the correct instance. Drop a button onto your Form1 and add the following code:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Threading.Tasks.Task.Factory.StartNew(Sub() Class1.Wrong())
Threading.Tasks.Task.Factory.StartNew(Sub() Class1.Correct(Me))
End Sub
End Class
Public Class Class1
Public Shared Sub Wrong()
Debug.WriteLine(String.Format("(Other thread, wrong) InvokeRequired={0}, IsHandleCreated={1}", Form1.InvokeRequired, Form1.IsHandleCreated))
End Sub
Public Shared Sub Correct(instance As Form1)
Debug.WriteLine(String.Format("(Other thread, correct) InvokeRequired={0}, IsHandleCreated={1}", instance.InvokeRequired, instance.IsHandleCreated))
End Sub
End Class
Output
(Other thread, correct) InvokeRequired=True, IsHandleCreated=True
(Other thread, wrong) InvokeRequired=False, IsHandleCreated=False