How can I find the sender of ContextMenuStrip? - vb.net

Situation:
I have a context menu in a VB.NET form with fires an event handler on ItemClicked. The auto-generated subroutine receives sender and e as parameters. As I don't reinvent the wheel multiple times, I linked this context menu to three text boxes. Let's name them Textbox1, Textbox2 and Textbox3.
Problem: How can I figure out in which textbox the menu was opened?
Okay, what did I already try?
sender contains the menu itself,
e.ClickedItem just returns the single menu item that was selected.
sender.Parent is always nothing
sender.OwnerItem is also always Nothing`
Me.Textbox1.Focused is always False, even if its the "parent" control of the menu.

Okay, I found a solution that works fine, and here's the code for all VB.NET coders that have the same problem.
The context menu is linked in TextBox1, so we need to add another event handler that saves the sending control into the menu:
Private Sub TextBox1_MouseUp(sender As Windows.Forms.Control, e As System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseUp
If e.Button = Windows.Forms.MouseButtons.Right Then
ContextMenu.Tag = sender
End If
End Sub
And this is the code of the event handler when clicking a menu item:
Private Sub ContextMenu_ItemClicked(sender As System.Object, e As System.Windows.Forms.ToolStripItemClickedEventArgs) Handles ContextMenu.ItemClicked
ContextMenu.Close()
If ContextMenu.Tag Is Nothing Then
Debug.Print("debug info: forgot to set sender? well ... KABOOM!")
Exit Sub
End If
Dim oParent As Windows.Forms.Control
Try
oParent = ContextMenu.Tag
Catch ex As Exception
Debug.Print("debug info: tag contains data other than sender control. well ... KABOOM!")
Exit Sub
End Try
' Do fancy stuff here.
' Release sender
ContextMenu.Tag = Nothing
End Sub

The ContextMenuStrip has a SourceControl property which contains a reference to the control which opened the menu. The event fires from one of the ToolStripMenuItems in the ContextMenuStrip, so you have to get the "parent" first:
' cast sender to menuitem
Dim mi = CType(sender, ToolStripMenuItem)
' cast mi.Owner to CMS
Dim cms = CType(mi.Owner, ContextMenuStrip)
' the control which opened the menu:
Console.WriteLine(cms.SourceControl.Name)
Under certain conditions the SourceControl can get lost but you can track it yourself with a variable and the parent ContextMenuStrip Opening event:
' tracking var:
Private MenuSourceControl As Control
Private Sub cms_Opening(sender As Object, e As CancelEventArgs) Handles cms.Opening
' set reference in case/before it is lost
MenuSourceControl = CType(sender, ContextMenuStrip).SourceControl
End Sub
Private Sub CutToolStripMenuItem_Click(sender...
If MenuSourceControl IsNot Nothing Then
' do your stuff
' optionally clear the tracking var
MenuSourceControl = Nothing
End If
End Sub
It should be also easier to get the SourceControl when dealing with sub items
You can also capture the related control in the MouseDown event of the related Control in cases where the same menu is used with different controls:
Private Sub lv2_MouseDown(sender As Object,
e As MouseEventArgs) Handles lv2.MouseDown,
myLV.MouseDown, lv1.MouseDown ...
If e.Button = Windows.Forms.MouseButtons.Right Then
MenuSourceControl = DirectCast(sender, Control)
End If
End Sub

Related

VB.Net code in class FormClosing error

I've created a VB.Net application which consists of a form and a series of classes. One class (GUI) contains all of the routines that handle the logic associated with the controls on my form. These controls are placed at design time.
My application runs in the system tray so I need to trap the FormClosing event so that the application is minimised rather than closed. Before moving everything into the GUI class my code worked but I'm now getting an error on this line:
Private Sub Monitor_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
which is "Event 'FormClosing' cannot be found"
I'm completely stumped by this. I've changed changing the MyBase parameter to refer to the form by name, leaving it off and also adding:
Private WithEvents Monitor As System.Windows.Forms.Form
but none work.
EDIT: Here's a stripped down version of my GUI class containing the bits that handle minimising and restoring the form to the system tray
Note that there's no reference to the menu bar and menu items but VS doesn't complain about those.
Public Class GUI
' Make form controls accessible to this class (partial list only
' there's a lot of controls)
Private WithEvents tabControl As TabControl
Private WithEvents btn_status_start As Button
Private WithEvents btn_status_stop As Button
Private WithEvents btn_dataSource As Button
' **************************************************************************************
' * *
' * Control form behaviour so that it can be put into and taken from system tray *
' * *
' **************************************************************************************
Private Sub cms_menu_exit_Click(sender As Object, e As EventArgs) Handles cms_menu_exit.Click
' Handles the saving and shutting down of the program
' Save any settings
My.Settings.Save()
' Remove the icon
Monitor.Dispose()
' Finally close the program
Monitor.Close()
End Sub
Private Sub Notify_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles notify.MouseDoubleClick
' Code to restore the program window from the system tray when double clicked
Me.Restore()
End Sub
Private Sub Notify_MouseClick(sender As Object, e As MouseEventArgs) Handles notify.MouseClick
' Code to restore the program window from the system tray when single clicked
Me.Restore()
Monitor.Focus()
End Sub
Private Sub Restore()
If Monitor.WindowState = FormWindowState.Minimized Then
Monitor.WindowState = FormWindowState.Normal
End If
Monitor.Visible = True
End Sub
Private Sub Monitor_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
' Catch the click on the X on the main form and set program to minimise instead
If (e.CloseReason = CloseReason.UserClosing) Then
e.Cancel = True
Monitor.Hide()
End If
End Sub
Private Sub cms_menu_open_Click(sender As Object, e As EventArgs) Handles cms_menu_open.Click
' Maximise the main form if selected
Monitor.Show()
Monitor.Focus()
End Sub
End Class

How do I set the tabIndex for a Button at run time in WinForms?

I have a generic message box where we have a few panels in which we add controls at runtime.
Here in my form, I've already pnlBottom on which pnlButtons is there along with some other controls.
Now at run time I'm adding an OK button to pnlButtons which is there on pnlBottom. I'm not setting a TabIndex for any controls in the Designer.vb file.
I'm trying to keep the focus on the OK button using the code below, but it's not working.
For Each control As Control In Me.Controls
If TypeOf (control) Is Panel Then
Dim pnlBottons As Panel = CType(control, Panel)
If pnlBottons.Name = "pnlBottom" Then
For Each ctrl As Control In control.Controls
Dim pnlButtons As Panel = CType(ctrl, Panel)
If pnlButtons.Name = "pnlButtons" Then
For Each ctrlbtn As Control In ctrl.Controls
If TypeOf (ctrlbtn) Is Button Then
Dim textBox As Button = CType(ctrlbtn, Button)
textBox.Parent.Parent.TabIndex = 0
textBox.Parent.TabIndex = 0
textBox.TabIndex = 0
End If
Next
End If
Next
End If
End If
Next
Here I'm setting the TabIndex for pnlBottom, pnlButtons and OK button as 0.
Please suggest how to focus the OK button.
You can set the focus on a control during Form Load event using either of the following options. (Ref: How to set the focus on a control when the form loads in Visual Basic .NET or in Visual Basic 2005. The original link is broken, so use Wayback Machine.)
1- Control.Select Method
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Mybase.Load
Me.OKButton.Select()
End Sub
Focus is a low-level method intended primarily for custom control
authors. Instead, application programmers should use the Select method
or the ActiveControl property for child controls, or the Activate
method for forms.
2- Form.ActiveControl Property
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Mybase.Load
Me.ActiveControl = Me.OKButton
End Sub
3- Control.Focus Method
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Mybase.Load
Me.Show()
Me.OKButton.Focus()
End Sub
The reason that we call Me.Show() is to set forms visible to true. according to:
You can use the Control.Focus method in the Load event of the form to
set the focus on a control only after the Visible property of the form
is set to True.
In cases you can not use Me.OKButton you can find the control you want like this:
Dim control = Me.Controls.Find("OKButton", True).FirstOrDefault()
If Not control Is Nothing Then
control.Select() 'or other stuff
End If

e.target in VB.net

When using flash, I was able to get to the focus of an event by accessing the event's "target" attribute.
so if I remember, it was something similar to.
button1.addEventListener(mouse_click, doSomething);
doSomething(e: Event){
e.target.size = 50000;
}
And I'm looking for the equivalent in VB.
If you can give me it's common name across all languages, I'd be doubly grateful. I don't quite know what to search for aside from "event.target VB.net equivalent, and that's not returning anything.
Thanks in advance.
edit: for those new to flash. By focus, I mean the physical object that was clicked on. So the example given would be accessing the clicked button's size.
In VB you can wire up event handlers declaratively using the WithEvents keyword or imperatively using AddHandler.
Private WithEvents myButton
' OR
Public Sub New
Dim newButton = New Button()
AddHandler newButton.Click, AddressOf MyClickHandler
End New
'To consume it you declare a method as follows:
' The Handles clause is used when declaring WithEvents
Private Sub MyClickHandler(sender As Object, e As EventArgs) Handles myButton.Click
' The sender has a handle on the object that raised the event (aka the button)
Dim btn = DirectCast(sender, Button)
btn.Size = New Size(500, 500)
End Sub
Got it!
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.button.onclick.aspx#Y0
Sub GreetingBtn_Click(ByVal sender As Object, ByVal e As EventArgs)
'When the button is clicked,
'change the button text, and disable it.
Dim clickedButton As Button = sender
clickedButton.Text = "...button clicked..."
clickedButton.Enabled = False
End Sub
The first parameter (sender by default) references the focused object. You can access it as you would any other normal variable, but it's information won't appear in the auto complete list unless you set it "As" that specific data type.
So I ended up with this
Private Sub nw_btn_Click(ByVal sender As System.Windows.Forms.Button, ByVal e AsSystem.EventArgs) Handles nw_btn.Click
sender.Hide()
End Sub

combobox not being populated

I have a windows form project with a main form. There is a textbox leave event that opens a new form. In that new forms load event i have a combobox item loop that populates the combobox items. It works perfectly fine if run on the main form but doesnt work on the second form. Why doesn't the comboboxes on the secondary form populate when that form is opened via a textbox_leave event from the main form?
this is the leave event
Private Sub tbChartTitle_Leave(sender As Object, e As System.EventArgs) Handles tbChartTitle.Leave
If Not tbChartTitle.Text = Nothing Then
frmTitleAttributes.Show()
End If
End Sub
This is the code that populates one of the comboboxes on the second form (it works if run on a combobox on the main form)
Private Sub frmTitleAttributes_Load(sender As Object, e As System.EventArgs) Handles Me.Load
InitializeComponent()
AddFonts()
End Sub
Private Sub AddFonts()
' Get the installed fonts collection.
Dim allFonts As New Drawing.Text.InstalledFontCollection
' Get an array of the system's font familiies.
Dim fontFamilies() As FontFamily = allFonts.Families
' Display the font families.
For i As Integer = 0 To fontFamilies.Length - 1
cbxTitleFonts.Items.Add(fontFamilies(i).Name)
Next
End Sub
make sure that the Load handler is hit after you show your form (use break point)
also you can try to call it in the Shown event
Private Sub frmTitleAttributes_Shown(sender as Object, e as EventArgs) _
Handles frmTitleAttributes.Shown
AddFonts()
End Sub

how do I catch events on runtime objects

I'm creating a form with a few buttons and a combobox at runtime.
dim f as new form
(blah blah)
then the buttons acceptDescription and rejectDescription are set up...
then the combobox descriptionCombo is set up...
then...
AddHandler acceptDescription.Click, AddressOf handleAcceptedDescription
AddHandler rejectDescription.Click, AddressOf handleRejectedDescription
then I have these two methods to catch the click events... but can't figure out how to reference the other runtime generated controls. (combobox if accepted, form if rejected)
Private Sub handleAcceptedDescription(ByVal sender As System.Object, ByVal e As System.EventArgs)
'stub
'will need to reference the chosen combobox value here
dim acceptedDescription as string = descriptionCombo.selectedValue .tostring
End Sub
Private Sub handleRejectedDescription(ByVal sender As System.Object, ByVal e As System.EventArgs)
'I want to close the runtime created form here, but can't reference it
f.close()
'and return user to main form
Me.Focus()
End Sub
If the code for generating the form is in your main form, then declare the Form variable at the class level of the main form class so you can access it from the event handlers. Same goes for your combobox and text field- you need to make sure the variables are declared outside the scope of the handlers so you can reference them within the handlers.
Why can't you reference it? Just save it as a module/form-level variable and you're set.
In order to avoid global definitions, the best answer is
Private Sub handleRejectedDescription(ByVal sender As System.Object, ByVal e As System.EventArgs)
'sender is a button in this case.
'get the button
dim b as new button
b = ctype(sender,button)
'now get the button's parent form
dim f as new form
f = ctype(b.parent, form)
'now close the form
f.close()
End Sub