How to get Parent control from context menu item - VB.NET [duplicate] - vb.net

i am attaching a single context menu to multiple text box. so, i need to get the control name/reference that used to show the context menu.
below is the sample image of my context menu:
Below is the code for green marked "paste" item click event:
Dim objTSMI As ToolStripMenuItem
Dim objCMS As ContextMenuStrip
Dim objTxtBox As System.Windows.Forms.TextBox
objTSMI = CType(sender, ToolStripMenuItem)
objCMS = CType(objTSMI.Owner, ContextMenuStrip)
objTxtBox = CType(objCMS.SourceControl, System.Windows.Forms.TextBox)
If Clipboard.ContainsText(TextDataFormat.Text) = True Then
objTxtBox.SelectedText = Clipboard.GetText(TextDataFormat.Text)
End If
it works very fine.
but below is my code for red marked "Page count" item click event:
Dim objTSMI As ToolStripMenuItem
Dim objCMS As ContextMenuStrip
Dim objTxtBox As System.Windows.Forms.TextBox
objTSMI = CType(sender, ToolStripMenuItem)
objCMS = CType(objTSMI.Owner, ContextMenuStrip)
objTxtBox = CType(objCMS.SourceControl, System.Windows.Forms.TextBox)
MessageBox.Show(objTxtBox.Name)
but above throws following error :
Unable to cast object of type 'System.Windows.Forms.ToolStripDropDownMenu' to type 'System.Windows.Forms.ContextMenuStrip'.
here is the screenshot of the error:
so, i can't figure it out what is the issue.
any help would be highly appreciated

If you check this C# thread the accepted answer notes it is a bug. The workaround presented there uses a private variable to store the SourceControl on the Opening event of the ContextMenuStrip. I've converted to VB.NET and used the Tag of the ContextMenuStrip instead of using the variable. You then refer to the Tag property instead of the faulty SourceControl property:
Imports System.ComponentModel
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.TextBox1.ContextMenuStrip = Me.ContextMenuStrip1
Me.TextBox2.ContextMenuStrip = Me.ContextMenuStrip1
End Sub
Private Sub ContextMenuStrip1_Opening(sender As Object, e As CancelEventArgs) Handles ContextMenuStrip1.Opening
Me.ContextMenuStrip1.Tag = CType(Me.ContextMenuStrip1.SourceControl, Control)
End Sub
Private Sub TestToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles TestToolStripMenuItem.Click
' first level of context menu strip
Dim Strip As ContextMenuStrip = CType(sender, ToolStripMenuItem).Owner
Dim Box As TextBox = Strip.Tag
MessageBox.Show(Box.Name)
End Sub
Private Sub ChildToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ChildToolStripMenuItem.Click
' second level of context menu strip
Dim Strip As ContextMenuStrip = CType(sender, ToolStripMenuItem).OwnerItem.Owner
Dim Box As TextBox = Strip.Tag
MessageBox.Show(Box.Name)
End Sub
End Class

Dim ControlsName as string
Private Sub ContextMenuStrip1_Opening(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles ContextMenuStrip1.Opening
ControlsName= ContextMenuStrip1.SourceControl.Name.ToString
End Sub

Related

How to update a control in a user control in a tabpage in VB.Net?

I am a self learning student in VB.Net. I have created a user control with TabControl.
I have added it dynamically to a new TabControl while add button is clicked. But I don't know how to modify the content of the control in the user control. Kindly help me in accessing the control inside user control in a tabcontrol.
Public Class Form1
Dim n As Integer
Dim a As string
Dim b As Object
Private Sub btnAddTabPage_Click(sender As Object, e As EventArgs) Handles btnAddTabPage.Click
Dim tabpage As New TabPage
tabpage.Text = "USERPanel-" + n.ToString
tabpage.Name = "USERTab-" + n.ToString
tcUsers.TabPages.Add(tabpage)
tcUsers.SelectedTab = tabpage
n = n + 1
'Dim lbltabname As Label = New Label
'lbltabname.Text = tpUsers.SelectedTab.Name.ToString
'tpUsers.SelectedTab.Controls.Add(lbltabname)
Dim ctrl As Userdata = New Userdata
ctrl.Name = "USERdata-" + n.ToString
tcUsers.SelectedTab.Controls.Add(ctrl)
ctrl.lblUserName.Text = tcUsers.SelectedTab.Name.ToString
ctrl.lblUserAge.Text = tcUsers.SelectedTab.Text.ToString
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
n = 1
End Sub
Private Sub btnDeleteTabPage_Click(sender As Object, e As EventArgs) Handles btnDeleteTabPage.Click
tcUsers.TabPages.Remove(tcUsers.SelectedTab)
End Sub
End Class
For Eg.
I have a textbox in the user control. I need to modify the content of the text box.

VB fires changetext event before control is loaded

I need to add some controls to a Visual Basic 2017 form programmatically. One of the controls is a textbox that needs a changetext event handler. Below is some code that accomplishes that task.
HOWEVER, the changetext event handler seems to fire right away, before the form even loads... before the textbox itself even loads! A "click" handler works fine, as expected. But changetext? Nope.
I've thrown together a simplified version to demonstrate. The line with the "DIES RIGHT HERE" comment causes the problem (not the comment, but the code to the left of it).
A textbox that is added at design time will work fine, not cause this problem, but that isn't an option.
What's causing this the changetext handler to be run early? How do I work around this?
Public Class Form1
Dim txtTest As TextBox
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim pntTextBox As Point
pntTextBox.X = 100
pntTextBox.Y = 100
txtTest = New TextBox
With txtTest
.Location = pntTextBox
.Width = 100
AddHandler txtTest.TextChanged, AddressOf txtTest_TextChanged
End With
Me.Controls.Add(txtTest)
End Sub
Private Sub txtTest_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyClass.TextChanged
Dim strTest As String
strTest = Str(txtTest.Width) ' ****** DIES RIGHT HERE
MsgBox(strTest)
End Sub
End Class
Made a few changes. Works.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For x As Integer = 1 To 2 'create multiple TB's
Dim pntTextBox As Point
pntTextBox.X = 100 * x
pntTextBox.Y = 100
Dim txtTest As TextBox = New TextBox
With txtTest
txtTest.Name = "tb_" & x.ToString
AddHandler txtTest.TextChanged, AddressOf txtTest_TextChanged
.Location = pntTextBox
.Width = 100
End With
Me.Controls.Add(txtTest)
Next
End Sub
Private Sub txtTest_TextChanged(ByVal sender As Object,
ByVal e As System.EventArgs) 'no handler at design time
Dim tb As TextBox = DirectCast(sender, TextBox)
Dim strTest As String
strTest = tb.TextLength.ToString
Debug.WriteLine("{0} {1}", tb.Name, strTest) 'put breakpoint here
End Sub
End Class

ContextMenuStrip Requires Two Right Clicks to Display

I like to create my contextmenu's programmatically. I generally do not add the items to the contextmenustrip until it is opening as the items that get displayed are dependent on other aspects of the design that are variable.
I have found that the contextmenustrips seem to require two right clicks to display. I've tried adding the menu items in different events (opening, opened, etc) and also manually setting the contextmenustrip's visibility to true to no avail.
I can't for the life of me figure out why two right clicks are necessary. If you create a blank winforms project and then replace all the code with this, it'll demonstrate the issue.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim currContextMenuStrip As New ContextMenuStrip
Me.ContextMenuStrip = currContextMenuStrip
AddHandler currContextMenuStrip.Opening, AddressOf ContextMenuStrip1_Opening
End Sub
Private Sub ContextMenuStrip1_Opening(sender As Object, e As CancelEventArgs)
Dim currContextMenuStrip As ContextMenuStrip = sender
Dim menuTxt As String = "&Find"
'only add the menu if it doesn't already exist
If (From f In currContextMenuStrip.Items Where f.text = menuTxt).Count = 0 Then
Dim newMenuItem As New ToolStripMenuItem
newMenuItem.Text = menuTxt
currContextMenuStrip.Items.Add(newMenuItem)
End If
End Sub
End Class
EDIT: Just figured out it seems to be connected to the fact that the contextmenustrip doesn't have any items on the first right click. If I add a dummy item, then hide it once other items are added, it works on the first right click. So confused!
This works:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim currContextMenuStrip As New ContextMenuStrip
Me.ContextMenuStrip = currContextMenuStrip
AddHandler currContextMenuStrip.Opening, AddressOf ContextMenuStrip1_Opening
'add a dummy item
Dim newMenuItem As New ToolStripMenuItem
newMenuItem.Text = "dummy"
currContextMenuStrip.Items.Add(newMenuItem)
End Sub
Private Sub ContextMenuStrip1_Opening(sender As Object, e As CancelEventArgs)
Dim currContextMenuStrip As ContextMenuStrip = sender
Dim menuTxt As String = "&Find"
'only add the menu if it doesn't already exist
If (From f In currContextMenuStrip.Items Where f.text = menuTxt).Count = 0 Then
Dim newMenuItem As New ToolStripMenuItem
newMenuItem.Text = menuTxt
currContextMenuStrip.Items.Add(newMenuItem)
End If
'hide the dummy item
Dim items As List(Of ToolStripMenuItem) = (From f As ToolStripMenuItem In currContextMenuStrip.Items Where f.Text = "dummy").ToList
items.First.visible = False
End Sub
End Class
If you really need to do things this way, one option is to create your own custom ContextMenuStrip that accounts for the behaviour when there are no items and the requirement for a dummy item. I used this code:
Imports System.ComponentModel
Public Class Form1
Private WithEvents menu As New ContextMenuStrip
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ContextMenuStrip = menu
End Sub
Private Sub menu_Opening(sender As Object, e As CancelEventArgs) Handles menu.Opening
If menu.Items.Count = 0 Then
menu.Items.AddRange({New ToolStripMenuItem("First"),
New ToolStripMenuItem("Second"),
New ToolStripMenuItem("Third")})
End If
End Sub
Private Sub menu_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles menu.ItemClicked
MessageBox.Show(e.ClickedItem.Text)
End Sub
End Class
and saw the same behaviour you describe. I then defined this class:
Public Class ContextMenuStripEx
Inherits ContextMenuStrip
Private dummyItem As ToolStripItem
Public ReadOnly Property IsInitialised As Boolean
Get
Return dummyItem Is Nothing
End Get
End Property
Public Sub New()
dummyItem = Items.Add(CStr(Nothing))
End Sub
''' <inheritdoc />
Protected Overrides Sub OnItemAdded(e As ToolStripItemEventArgs)
If Not IsInitialised Then
Items.Remove(dummyItem)
dummyItem = Nothing
End If
MyBase.OnItemAdded(e)
End Sub
End Class
and changed my form code to this:
Imports System.ComponentModel
Public Class Form1
Private WithEvents menu As New ContextMenuStripEx
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ContextMenuStrip = menu
End Sub
Private Sub menu_Opening(sender As Object, e As CancelEventArgs) Handles menu.Opening
If Not menu.IsInitialised Then
menu.Items.AddRange({New ToolStripMenuItem("First"),
New ToolStripMenuItem("Second"),
New ToolStripMenuItem("Third")})
End If
End Sub
Private Sub menu_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles menu.ItemClicked
MessageBox.Show(e.ClickedItem.Text)
End Sub
End Class
and it worked as desired. Note that the last code snippet uses the custom type and its custom property.
Thanks for all the help and suggestions! I ultimately decided to build the superset of menus in the Designer and then just show/hide at run time. That's probably faster on each right click then rebuilding the menu each time.
Microsoft has old style and new style context menus. The Popup event was used for the old style context menus and it received a plain EventArgs object. The new context menus use the Opening event which receives a CancelEventArgs object. If currContextMenuStrip.Items doesn't contain any items, e.Cancel will be set to True when the event handler is called (which caused the problem you encountered). The fix is to add the menu items and then set e.Cancel to False. It should display fine after that. To make sure items were actually added, the assignment of e.Cancel can be guarded with an if statement as follows:
If currContextMenuStrip.Items.Count <> 0 Then
e.Cancel = False
End If

Visual Indication of Focus and Adding Click Event To all TextBoxes

I'm working on a program wherein I have around 400 Text Boxes and I need to program an effect to make them show that they have focus. I can get the visual part down (Unless someone knows how to add a soft blue outline to a text box in VB), but I'm having trouble with creating GotFocus and LostFocus events that handle all of my Text Boxes at once. I've tried
Dim txtBox = Me.Controls.OfType(Of TextBox)
Private Sub TextBox_GotFocus(sender As Object, e As EventArgs) Handles txtBox.GotFocus
But I get a "Must have WithEvents variable" error which I don't quite understand how to fix. I've tried
Public Sub txtBoxGotFocusHandler(ByVal sender As Object,
ByVal e As System.EventArgs)
For Each txtBox As TextBox In Me.Controls 'References all text boxes in form
If txtBox.Focus = True Then
txtBox.BackColor = Color.Black
End If
Next
And I've tried a few other somewhat related things I've seen around the internet, but to no avail. Any help would be appreciated
You can make your app at runtime with any controls. You could query the layout of your app from SQL and from a simple change your app layout changes.
Private FocusRectangle As System.Drawing.Graphics
Private OldRectangle As System.Drawing.Graphics
Private MyTextBoxes As New List(Of TextBox)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MyTextBoxes.Clear()
For xcount = 0 To 399
MyTextBoxes.Add(New TextBox)
With MyTextBoxes.Item(xcount)
.Name = "MyTextBoxes" & (xcount + 1).ToString
.Text = ""
.Location = New Point(0, 0)
.Size = New Size(50, 13)
.Visible = True
AddHandler .GotFocus, AddressOf MyTextBoxes_GotFocus
AddHandler .LostFocus, AddressOf MyTextBoxes_LostFocus
End With
Me.Controls.Add(MyTextBoxes.Item(xcount))
'add them to a panel....
'Panel1.Controls.add(MyTextBoxes.Item(xcount))
Next
End Sub
Sub MyTextBoxes_GotFocus(sender As Object, e As EventArgs)
Dim ThisTextBox As TextBox = DirectCast(sender, TextBox)
Dim xPen As New System.Drawing.Pen(Color.LightBlue)
FocusRectangle = Me.CreateGraphics()
FocusRectangle.DrawRectangle(xPen, ThisTextBox.Location.X - 1, ThisTextBox.Location.Y - 1, ThisTextBox.Size.Width + 1, ThisTextBox.Size.Height + 1)
OldRectangle = FocusRectangle
End Sub
Sub MyTextBoxes_LostFocus(sender As Object, e As EventArgs)
Dim ThisTextBox As TextBox = DirectCast(sender, TextBox)
OldRectangle.Dispose()
End Sub
Private Sub Form1_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
MyTextBoxes.Item(0).Focus()
End Sub
If you created your form with the Designer, the WithEvents is added for you automatically.
If you are declaring 400 text boxes as Private fields, you would declare them as Private WitheEvents txtBox As TextBox
If you're creating the text boxes programatically and adding them to a collection of textboxes or something, then you can't do WithEvents.
But all WithEvents does is allow you to add Handeles TextBox.SomeEvent to a function. Instead you can do this:
Dim txtBox As New TextBox
...
AddHandler txtBox.GotFocus, AddressOf txtBoxGotFocusHandler
Me.Controls.Add(txtBox)

How do you get the control that was clicked to open a ContextMenuStrip?

I'm using a ContextMenuStrip for multiple controls and I'm trying to figure out the best way to get the control that was actually clicked on to open the Context Menu. The sender just gives the ToolStripMenuItem reference, which has an Owner property that references the ContextMenuStrip, but I cannot figure out how to tell which control the click came from. There must be a simple way to check this, right? I'm checking it in the ToolStripMenuItem's click event.
Friend WithEvents mnuWebCopy As System.Windows.Forms.ToolStripMenuItem
...
Private Sub mnuWebCopy_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuWebCopy.Click
I found a similar post about this, but that mentions using a SourceControl property which I do not see on here.
I'm using Visual Studio 2008, VB.Net winforms.
Private Sub mnuWebCopy_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuWebCopy.Click
Dim myItem As ToolStripMenuItem = CType(sender, ToolStripMenuItem)
Dim cms As ContextMenuStrip = CType(myItem.Owner, ContextMenuStrip)
MessageBox.Show(cms.SourceControl.Name)
End Sub
Your sender is a ToolStripMenuItem -- cast it.
Its owner is a ContextMenuStrip -- get it.
SourceControl is a property on the ContextMenuStrip and references the last control from which the ContextMenuStrip was displayed.
Private Sub kdgToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles kdgToolStripMenuItem.Click
Dim sms = (sender.GetCurrentParent()).SourceControl.name
MsgBox(sms)
End Sub
'///Faster
Private Sub cmsRightClick_Click(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cmsRightClick.MouseClick
Dim s As String = CType(sender, ContextMenuStrip).GetItemAt(CType(sender, ContextMenuStrip).DisplayRectangle.X, _
CType(sender, ContextMenuStrip).DisplayRectangle.Y + e.Y).Text.Trim()
MsgBox(s)
Select Case s
Case Is = "Select Summary Total"
Dim x = 0
Case Is = "Select Collections"
Dim x = 1
Case Is = "UnSelect"
Dim x = 2
Case Is = "Reconcile"
Dim x = 3
Case Is = "Undo Reconciliation"
Dim x = 4
End Select
End Sub
On VB.NET 2013 this work so fine:
Dim cms As ContextMenuStrip = CType(sender, ContextMenuStrip)
MessageBox.Show(cms.SourceControl.Name)