i have a problem in Windows Forms. I've created a form where I can enter the first and last name and click on the Search-Button to show another form with the following code:
Private Sub btnSearchUser_Click(sender As Object, e As EventArgs) Handles btnSearchUser.Click
If Me._clsfrmChild Is Nothing Then
Me._clsfrmChild = New clsFrmChild
End If
If Me._clsfrmChild.ShowDialog = False Then
Me._clsfrmChild.ShowDialog(Me)
End If
In the second form I have a MaskedTextbox:
Empty MaskedTextBox
Whatever I do, If I close the second form with Visible = False and reopen it again, the MaskedTextBox looks like this:
MaskedTextBox without underlines
I close the second form that way:
Private Sub btnAbort_Click(sender As Object, e As EventArgs) Handles btnAbort.Click
Me.Visible = False
End Sub
Does someone of you know why this problem is caused?
This is really not the way you supposed to work with dialogs. The proper way is this
dim result as DialogResult
using f as Form = New MyForm()
result = f.ShowDialog()
' here you can work with form's public properties etc
end using
' optionally here you can continue massaging the result
if result = DialogResult.Ok then
' do for ok result
else
' do for other result. You can have severul results - add elseif
end if
Here is how to make a dialog with results in general. This will be similar to how MessageBox.Show() works.
Public Class clsFrmChild
Private Sub New()
InitializeComponent()
End Sub
Public Shared Shadows Function Show() As DialogResult
Dim result As DialogResult
Using f As New clsFrmChild()
result = f.ShowDialog()
End Using
Return result
End Function
Private Sub OkButton_Click(sender As Object, e As EventArgs) Handles OkButton.Click
DialogResult = DialogResult.OK
Close()
End Sub
Private Sub CancelButton_Click(sender As Object, e As EventArgs) Handles CancelButton.Click
DialogResult = DialogResult.Cancel
Close()
End Sub
End Class
Note the constructor is privatized so there is no more Me._clsfrmChild = New clsFrmChild. You will see the displaying of the modal dialog is much simpler when called like MessageBox
Private Sub btnSearchUser_Click(sender As Object, e As EventArgs) Handles btnSearchUser.Click
Dim result = clsFrmChild.Show() ' static method call instead of instance method
Select Case result
Case DialogResult.OK
MessageBox.Show("Ok")
Case DialogResult.Cancel
MessageBox.Show("Cancel")
End Select
End Sub
If you are not interested in returning a standard DialogResult, you could change the return type to whatever you like such as a custom class, with more information (such as you want to return a string in addition to DialogResult) i.e.
Public Class clsFrmChildResult
Public Property Text As String
Public Property DialogResult As DialogResult
End Class
...
Public Shared Shadows Function Show() As clsFrmChildResult
Dim result As clsFrmChildResult
Using f As New clsFrmChild()
Dim dr = f.ShowDialog()
result = New clsFrmChildResult With {.Text = TextBox1.Text, .DialogResult = dr}
End Using
Return result
End Function
...
Private Sub btnSearchUser_Click(sender As Object, e As EventArgs) Handles btnSearchUser.Click
Dim result = clsFrmChild.Show()
Select Case result.DialogResult
Case DialogResult.OK
MessageBox.Show("Ok")
Dim myString = result.Text
Case DialogResult.Cancel
MessageBox.Show("Cancel")
End Select
End Sub
Related
I have a form Main that calls open a dialog, which gets a text value response and that value would need to be used in Main. I was wondering how to achieve this where Main would have access to this after the dialog closes. The minimal example of this is as follows:
Private Class Main
Private Sub btn_Click(sender As Object, e As EventArgs) Handles btnOpenDialog.Click
Dim dialog As New Dialog
dialog.Show()
End Sub
End Class
and Dialog
Private Class Dialog
Private response As String
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
response = txtResponse.Text
Me.Close()
End Sub
End Class
Just provide a pass-through property for the Text of the TextBox:
Public ReadOnly Property Response As String
Get
Return txtResponse.Text
End Get
End Property
It's hard to know for certain but you probably out to be displaying the form as a modal dialogue:
Dim response As String = Nothing
Using dlg As New Dialog
If dlg.ShowDialog() = DialogResult.OK Then
response = dlg.Response
End If
End Using
'...
Now you don't need a Click event handler for your Button as you can just set its DialogResult property to OK in the designer.
In that case the simple solution could be the following:
Private Class Main
Private Sub btnOpenDialog_Click(sender As Object, e As EventArgs) Handles btnOpenDialog.Click
Dim AskDialog As New Dialog
Dim Response As String
Response = AskDialog.DialogShow()
End Sub
End Class
and Dialog
Public Class Dialog
Private response As String
Public Function DialogShow() As String
Me.ShowDialog()
Return response
End Function
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
response = txtResponse.Text
Me.Close()
End Sub
End Class
I want to ask user for example "Do you want to go right or left?".
To have simple Code I use MSGBOX with a prompt like:
"Do you want to go right or left"
Press "YES for 'right' / NO for 'left'"
Then I process Yes/No/Cancel that was pressed. This works but is ugly and in some cases hard to understand.
Also in Addition in some cases I have more than 2 choices - but that is probable another question...
You can create one dynamically
Public Module CustomMessageBox
Private result As String
Public Function Show(options As IEnumerable(Of String), Optional message As String = "", Optional title As String = "") As String
result = "Cancel"
Dim myForm As New Form With {.Text = title}
Dim tlp As New TableLayoutPanel With {.ColumnCount = 1, .RowCount = 2}
Dim flp As New FlowLayoutPanel()
Dim l As New Label With {.Text = message}
myForm.Controls.Add(tlp)
tlp.Dock = DockStyle.Fill
tlp.Controls.Add(l)
l.Dock = DockStyle.Fill
tlp.Controls.Add(flp)
flp.Dock = DockStyle.Fill
For Each o In options
Dim b As New Button With {.Text = o}
flp.Controls.Add(b)
AddHandler b.Click,
Sub(sender As Object, e As EventArgs)
result = DirectCast(sender, Button).Text
myForm.Close()
End Sub
Next
myForm.FormBorderStyle = FormBorderStyle.FixedDialog
myForm.Height = 100
myForm.ShowDialog()
Return result
End Function
End Module
You see you have options as to what buttons are present, the message, and title.
Use it like this
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim result = CustomMessageBox.Show(
{"Right", "Left"},
"Do you want to go right or left?",
"Confirm Direction")
MessageBox.Show(result)
End Sub
End Class
In my example, the prompt is "Do you want to go right or left?" and the options are "Right" and "Left".
The string is returned as opposed to DialogResult because now your options are unlimited (!). Experiment with the size to your suiting.
You need to create your own "custom" msgbox form according to your needs, and its better to create a reusable control - pass your "question" string via the constructor of your custom control.
You need some "way" to get the user decision from out side your custom msgbox - one way is use DialogResult Enum for that.
Here is a basic example i just wrote to demonstrate that, please see the comments i have added inside the code.
create a new project with 2 forms, Form1 will be the main form that will call the custom msgbox and Form2 will be the custom msgbox:
Form1:
Form2:
Code for Form1:
Public Class Form1
Private Sub btnOpenCustomMsgbox_Click(sender As Object, e As EventArgs) Handles btnOpenCustomMsgbox.Click
Dim customMsgbox = New Form2("this is my custom msg, if you press yes i will do something if you press no i will do nothing")
If customMsgbox.ShowDialog() = DialogResult.Yes Then
' do something
MsgBox("I am doing some operation...")
Else
' do nothing (its DialogResult.no)
MsgBox("I am doing nothing...")
End If
End Sub
End Class
Code for Form2:
Public Class Form2
' field that will contain the messege
Private PromtMsg As String
Sub New(ByVal promtmsg As String)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
' set global field with the argument that was passed to the constructor
Me.PromtMsg = promtmsg
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' set the msg label
Me.lblPromtMsg.Text = Me.PromtMsg
End Sub
Private Sub btnCustomYes_Click(sender As Object, e As EventArgs) Handles btnCustomYes.Click
' user choosed yes - return DialogResult.Yes
Me.DialogResult = DialogResult.Yes
Me.Close()
End Sub
Private Sub btnCustomNo_Click(sender As Object, e As EventArgs) Handles btnCustomNo.Click
' user choosed no - DialogResult.no
Me.DialogResult = DialogResult.No
Me.Close()
End Sub
End Class
it can be much more sophisticated but if you explore that example i hope you will understand the general idea.
I have 2 Datagridview controls in same form. Each Datagrid has some columns where user will write long texts, so I designed form with RichTextBox that opens when user double-clicks these columns to enlarge text-entry. Code works, but I want to use same form for both Datagrids, so I should somehow return text to active datagridview cell. Here is my code (for Datagridview1):
TextZoomForm:
Public Class TextZoomForm
Public OpenedForm1 As New Form1
Private Sub RichTextBox1_DoubleClick(sender As Object, e As EventArgs) Handles RichTextBox1.DoubleClick
OpenedForm1.DataGridView1.CurrentCell.Value = RichTextBox1.Text
OpenedForm1.Label24.Focus()
Me.Close()
End Sub
Private Sub TextZoom_Load(sender As Object, e As EventArgs) Handles Me.Load
RichTextBox1.Text = OpenedForm1.DataGridView1.CurrentCell.Value
End Sub
End Class
DataGridView1_CellMouseDoubleClick in Form1:
Private Sub DataGridView1_CellMouseDoubleClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.CellMouseDoubleClick
If e.ColumnIndex = 1 Then
Dim cp = Cursor.Position
cp.Y += CInt(Cursor.Size.Height * (-0.5))
cp.X += CInt(Cursor.Size.Width * 0.8)
Dim f As New TextZoomForm()
f.OpenedForm1 = Me
f.Show()
f.Location = New Point(cp)
End If
End Sub
Any ideas on how to return text to active datagridview cell?
Change your zoomed form so that it doesn't know where its data comes from. Instead the control using it will pass the data.
Public Class TextZoomForm
Public Property ZoomedText As String
Get
Return RichTextBox1.Text
End Get
Set(value As String)
RichTextBox1.Text = value
End Set
End Property
Private Sub RichTextBox1_DoubleClick(sender As Object, e As EventArgs) Handles RichTextBox1.DoubleClick
Me.Close()
End Sub
End Class
To call the form change your code to the following:
Private Sub DataGridView1_CellMouseDoubleClick(sender As Object, e As DataGridViewCellMouseEventArgs) Handles DataGridView1.CellMouseDoubleClick
...
Dim f As New TextZoomForm()
f.ZoomedText = DataGridView1.CurrentCell.Value
f.ShowDialog()
'Great breakpoint location.
DataGridView1.CurrentCell.Value = f.ZoomedText
Label24.Focus()
....
End Sub
Using ShowDialog prevents the user from changing the current cell part way through your call.
If you need it modeless then you should:
store the cell that the user has selected
handle the FormClosing event.
Test the DialogResult to make sure the user pressed ok
write the data back to the stored cell.
Here is the code I have:
Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddNewButton()
End Sub
Public Sub AddNewButton()
Dim buttonTop As Integer = 100
For Each item As String In Globals.candidates
Dim btn As New System.Windows.Forms.Button()
Dim Location As New Point(100, (buttonTop + 20))
btn.Location = Location
btn.Text = item
btn.Width = 150
AddHandler btn.Click, AddressOf Me.buttonClick
Me.Controls.Add(btn)
buttonTop += 20
Next
End Sub
Private Sub buttonClick()
Dim result As Integer = MessageBox.Show(String.Format("Did you select {0} ?", ???????????), "Confirmation", MessageBoxButtons.YesNo)
If result = DialogResult.Yes Then
MessageBox.Show("Yes pressed")
Else
MessageBox.Show("No pressed")
End If
End Sub
End Class
Globals.candidates is a global string array variable that holds a name "LastName, FirstName" and when the form is loaded I call the AddNewButton() Sub and it creates buttons for each item in the string array. No problem.
If you see in my code the "??????????" section, I don't know how to reference the dynamically created buttons's text so that I can display the proper "Did you select thisButton.text" properly.
Any help is appreciated.
Thanks!
EDIT:
Code changed as per suggestions: (Working)
Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddNewButton()
End Sub
Public Sub AddNewButton()
Dim buttonTop As Integer = 100
For Each item As String In Globals.candidates
Dim btn As New System.Windows.Forms.Button()
Dim Location As New Point(100, (buttonTop + 20))
btn.Location = Location
btn.Text = item
btn.Width = 150
AddHandler btn.Click, AddressOf Me.buttonClick
Me.Controls.Add(btn)
buttonTop += 20
Next
End Sub
Private Sub buttonClick(sender As Object, e As EventArgs)
Dim btn As Button = DirectCast(sender, System.Windows.Forms.Button)
Dim result As Integer = MessageBox.Show(String.Format("Did you select {0} ?", btn.Text), "Confirmation", MessageBoxButtons.YesNo)
If result = DialogResult.Yes Then
MessageBox.Show("Yes pressed")
Else
MessageBox.Show("No pressed")
End If
End Sub
End Class
You need to put the proper signature on your event handler:
Private Sub buttonClick(sender As Object, e As EventArgs)
Then, you can use the sender object (which will be whichever button was clicked)
Dim button As Button = DirectCast(sender, System.Windows.Forms.Button)
Dim result As Integer = MessageBox.Show(String.Format("Did you select {0} ?", button.Text), "Confirmation", MessageBoxButtons.YesNo)
To get a reference to the button clicked you need to declare the event handler of the button click with the two parameters that are passed to it by the form engine.
Private Sub buttonClick(sender as Object, e as EventArgs)
Now, this correct event handler receives a parameter named sender that is the control reference to the button clicked. You could cast it to a button and then extract the Text property
Private Sub buttonClick(sender as Object, e as EventArgs)
Dim btn = DirectCast(sender, System.Windows.Forms.Button)
if btn IsNot Nothing then
Dim result As Integer = MessageBox.Show(String.Format("Did you select {0} ?", btn.Text), "Confirmation", MessageBoxButtons.YesNo)
If result = DialogResult.Yes Then
MessageBox.Show("Yes pressed")
Else
MessageBox.Show("No pressed")
End If
End If
End Sub
This should be enough in this simple case where you have just a string data, but, if you need to associate a more complex object (like an instance of a Person class for example) you could use the Tag property of every dynamically added button to store there a reference to the instance of the class
As a side note, your code works also without the declaration of the two parameters because you have the Option Strict configuration set to OFF. This is a bad practice because it introduces subtle errors in you parameters usage and in automatic conversions of type. If you are just starting with a new project remember to set its property Option Strict to ON
So basically you have a main form that shows only a label (lblTotalPrice) and a button that when you click it, it opens a second form that contains a listbox with different prices packages (lstPackages). How do do you select an item from lstPackages that is on the second form and return it to lblTotalPrice, which is located on the main form?
I attempted this code using a function (thought it would be useful for calculations), but it seems like it didn't work:
On my second form, I populated the lstPackages with it's name using parallel arrays:
Dim strPackages As String = {"Package 1", "Package 2", "Package 3", "Package 4", "Package 5"}
Dim decPrice As String = {100D, 200D, 300D, 400D, 500D}
Private Sub SecondForm_Activated(sender As Object, e As EventArgs) Handles MyBase.Activate
Dim frmMain as New
Dim i As Integer
For i = 0 To strPackages.Length - 1
lstPackages.Items.Add(strPackages(i))
Next
End Sub
After I tried to select a value on lstPackages, but I just was not too sure if I was using the right code to select the item and return it to the main form:
Function CalcPackage()
Dim decPackages As Decimal = 0D
If lstPackages.SelectedIndex = -1 Then
MessageBox.Show("Select a package")
Else
For i = 0 To lstPackages.SelectedItem - 1
decPackages = lstPackages.SelectedItem(i)
Next i
End If
Return decPackages
End Function
When I have selected the item in the lstPackages, I tried to send the selected item back to the main form, but ran into a problem here:
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnClose.Click
Dim frmMainForm As New MainForm
Dim decTotal As Decimal
decTotal = CalcPackage()
'I was stuck in this part and wasn't sure how to return it to lblTotalPrice (and also close the second form too)
frmMainForm.lblTotalPrice.Text = decTotal.ToString("c")
frmMainForm.Show()
Me.Close()
End Sub
So yea sorry if my code is weird, but I'm hoping to get help on how to return the value of the item to lblTotalPrice on the main form, thanks guys
Part of your problem is that you are newing up an instance of the main form in the button exit click event handler of the second form, as shown in this code posted:
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnClose.Click
Dim frmMainForm As New MainForm
Dim decTotal As Decimal
decTotal = CalcPackage()
'I was stuck in this part and wasn't sure how to return it to lblTotalPrice (and also close the second form too)
frmMainForm.lblTotalPrice.Text = decTotal.ToString("c")
frmMainForm.Show()
Me.Close()
End Sub
Instead pass an instance of the main form to the second form via its constructor, like this:
Public Class SecondForm Inherits Form
Dim theMainForm As MainForm
Public Sub New(mainForm As MainForm)
theMainForm = mainForm
End Sub
End Class
Now you can reference the lblTotalPrice in the MainForm class via the theMainForm variable, like this:
theMainForm.lblTotalPrice.Text = lstPackages.SelectedItem
The theMainForm.lblTotalPrice.Text should only be updated in response to an item being selected from lstPackages not when the second form is closed; so handle the updating of the main form's label in the SelectedIndexChanged event of lstPackages, like this:
Protected Sub lstPackages_IndexChanged(sender As Object, e As EventArgs)
Dim decTotal As Decimal
decTotal = CalcPackage()
theMainForm.lblTotalPrice.Text = decTotal.ToString("c")
End Sub
All the exit button click event handler should do is to show the main form and close the second form, like this:
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnClose.Click
theMainForm.Show()
Me.Close()
End Sub
Just display SecondForm with ShowDialog(), then retrieve the value with the reference to it that you already have:
Public Class MainForm
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim sf As New SecondForm
If sf.ShowDialog = Windows.Forms.DialogResult.OK Then
lblTotalPrice.Text = sf.TotalPrice
End If
End Sub
End Class
Over in SecondForm:
Public Class SecondForm
Private _TotalPrice As Decimal
Public ReadOnly Property TotalPrice As Decimal
Get
Return _TotalPrice
End Get
End Property
Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnClose.Click
If lstPackages.SelectedIndex = -1 Then
MessageBox.Show("Select a package")
Else
_TotalPrice = lstPackages.SelectedItem(0)
Me.DialogResult = Windows.Forms.DialogResult.OK
End If
End Sub
End Class