Prevent ToolStripMenuItem context menu from closing on click - winforms

I'm trying to have my context menu stay on screen even after I click one of its dropdown items (when the shift key is pressed, though I don't think that matters to the issue). You can see an example of the behavior in Windows XP when you click on Start > All Programs > Accessories > [now hit your shift key] and click on Windows Explorer... The menu stays up.
It's a C# app, using Winforms, development machine is Windows 7, production is either XP, Vista or 7.
The toolstripmenuitem doesn't seem to have a closing event; only a closed one. Those familiar with a closing event will know that you can set a cancel flag to prevent the control from closing.
Also, when I try a workaround of remaking it visible from within either its click event or its closed event, it doesn't work. Although that would have been a tolerable workaround in the immediate, it is not for production.
Any suggestions or related info greately appreciated.
Thank you

I was able to have a dynamically created submenu of my ContextMenu stay on screen upon being clicked by setting the AutoClose property of its Parent DropDown menu to "False" like this:
ParentMenu.DropDown.AutoClose = false;
where ParentMenu is a ToolStripMenuItem.
The use of the Closing event of the DropDown's Parent ToolStripDropDownMenu to acheive this by setting a "Cancel" flag was not a viable solution because it caused inconsistant showing/hiding behavior in either of its 2 levels of Parent menus as well as causing unexpected visual artifacts on screen that I could not get rid of when later being hidden through code. It also seemed to cause certain events of the dynamically created menu's Parents to no longer fire, such as its MouseEnter event.
An interesting find during this experience was that although Visual Studio 2010's intellisense lists a LostFocus event for a DropDown of a context menu item; when adding this event to dynamically created menus it does not get fired; this is apparently a known behavior as mentionned here:

Here's what I ended up using. With this method, the dropdown's autoclose is only disabled while the mouse pointer is on the drop down control. MyMenuItem is type ToolStripMenuItem.
AddHandler MyMenuItem.DropDown.MouseEnter, AddressOf DisableDropMenuClose
AddHandler MyMenuItem.DropDown.MouseLeave, AddressOf EnableDropMenuClose
Private Sub DisableDropMenuClose(ByVal sender As System.Object, ByVal e As System.EventArgs)
CType(sender, ToolStripDropDownMenu).AutoClose = False
End Sub
Private Sub EnableDropMenuClose(ByVal sender As System.Object, ByVal e As System.EventArgs)
CType(sender, ToolStripDropDownMenu).AutoClose = True
End Sub

The ToolStripDropDownMenu has the Closing event.
private void MyContextMenuStrip_Closing(object sender, ToolStripDropDownClosingEventArgs e) {
if (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked) {
e.Cancel = true;
}
}

Related

How to Close a Window Containing a Frame, NavigationService and Timer

In my project I've got a MainWindow that opens up a second Window. Inside the second Window there is a Frame and I start a navigationservice inside the Frame. Also in the second Window I've got a KeyDown method that calls Me.Close when the user presses the Escape key. Anyway, when the second Window closes a System.Windows.Threading.DispatcherTimer() inside one of the pages in the navigation service doesn't end. Any ideas on how can I close the second Window and terminate the DispatcherTimer inside the navigationservice?
Thanks
Mike
p.s. I can supply source code if anyone wants to look at what I've got...
(Hey EkoostikMartin - this is a follow up to your comments..)
So I've made some progress on this. I've add:
AddHandler Me.KeyDown, AddressOf Page_KeyDown
AddHandler Me.PreviewKeyDown, AddressOf Page_PreviewKeyDown
to the Page that has the timer. And inside the page I've defined both methods like:
Private Sub Page_KeyDown(sender As Object, e As KeyEventArgs)
If e.Key = Key.Escape Then
dTimer.Stop()
MessageBox.Show("Exit Page")
End If
End Sub
Private Sub Page_PreviewKeyDown(sender As Object, e As KeyEventArgs)
If e.Key = Key.Escape Then
dTimer.Stop()
MessageBox.Show("Exit Page")
End If
End Sub
And the second Window has this:
Private Sub Window_KeyDown(sender As System.Object, e As System.Windows.Input.KeyEventArgs)
'Escape Key Exits Program
If e.Key = Key.Escape Then
Me.Close()
End If
End Sub
So when I'm in the navigation service and navigated to the page with the Timer and I press "Esc" I get the message "Exit Page" and then the Window closes. This is good!
(I don't think I need both the KeyDown and PreviewKeyDown. When I press "Esc" I'm actually getting two "Exit Page" pop-ups)
There is a problem though: It seems like the Page doesn't get the KeyDown events unless I move the focus to a textbox or combobox and if I don't do this pressing the "Esc" key calls the second Window's Window_KeyDown and not the Page's KeyDown event which means the timer on the page doesn't get stopped even after the second Window is closed. Does anyone know a way to get the page focus when the page loads so that I can get the KeyDown event without manually changing the focus to a control on the Page?
Thanks!
Ok - I finally resolved this situation by way of a work around. In my second Window I've created a list of type DispatcherTimer:
Public clndTimer As New List(Of System.Windows.Threading.DispatcherTimer)
I can access this list from a Page that is inside my Navigation service. Here's the code in the Page:
Dim dTimer As New DispatcherTimer()
dTimer.Start()
Dim wSecondWindow As New SecondWindow
wSecondWindow = Window.GetWindow(Me)
If wSecondWindow IsNot Nothing Then
wSecondWindow.clndTimer.Add(dTimer)
End If
And then I capture the key event in the second Window. This is the method in the second Window:
Private Sub Window_KeyDown(sender As System.Object, e As System.Windows.Input.KeyEventArgs)
'Escape Key Exits Program
If e.Key = Key.Escape Then
For Each dt In clndTimer
dt.Stop()
Next
Me.Close()
End If
End Sub
Doing it this way I don't need either the Page_KeyDown or PreviewKeyDown methods in my Page which is good because they were behaving unreliably. (see answer above)
So what do you think? I'm not totally sure about the way I get the Second window or how I check it for null in the Page but otherwise this seems to make sense.
Thanks!

MessageBox pops up behind ComboBox drop down list, obscuring content in MessageBox

In a project I am working on, I have a ComboBox with dates that calculates the persons age depending on certain other criteria elsewhere in the application.
If the user selects a date, we pop up a notification to the user that we changed a few things on the form due to this.
As you can see in the picture, the combobox's items end up still staying on top of the messagebox when it pops up. The user can still click OK, and can move the box, but this is an odd way to present information to the user.
Is there a way to pop up the message box above this list, or hide the list before the messagebox comes up? I tried setting IsDropDownOpen, but that doesn't work.
Update: The MessageBox.Show event happens in the selection changed, which is why I figured it hadn't closed the drop down yet. How could one get around this though?
Update 2: Code I currently have goes like this. ComboBox uses a 'LostFocus' event (to handle typing and/or selecting) to call a CheckDOB method. CheckDOB is where I then show the MessageBox
Well I can't reprodouce your issue, but I believe that using the Dispatcher can help.
Try this:
VB.NET
Private Sub ComboBox_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
'Do what you need..
Me.Dispatcher.BeginInvoke(Sub()
MessageBox.Show("Message", "Caption", MessageBoxButton.OK, MessageBoxImage.Information)
End Sub)
End Sub
C#
private void ComboBox_SelectionChanged(System.Object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
//Do what you need..
Dispatcher.BeginInvoke(new ThreadStart(() =>
{
MessageBox.Show("Message", "Caption", MessageBoxButton.OK, MessageBoxImage.Information);
}));
}

Winforms Databound ComboBox doesn't update when close Window

I have a combobox that is databound and updates with no issues. The problem I have is if the user types something into the combobox and then uses the [X] close button in the window without tabbing out, the data is not updated. I've been looking all over the web, but can't find any relevant help/tips. The only idea is have is to force an out of focus and maybe that would force the combobox to see the update.
Try adding the FormClosing event on your form.
The FormClosing event occurs as the form is being closed.
private void Form1_FormClosing(Object sender, FormClosingEventArgs e)
{
//force an event to have the cbo updates fire.
txtFoo.Focus();
}
or VB.NET
Private Sub Form1_FormClosing(sender as Object, e as FormClosingEventArgs) _
Handles Form1.FormClosing
'force an event to have the cbo updates fire.
txtFoo.Focus()
End Sub
From there, you can call the method/logic to have your combobox's contents saved to your datastore.

Equivalent to a keypreview property in WPF

I'm pondering taking the plunge to WPF from WinForms for some of my apps, currently I'm working on the combined barcode-reader/text-entry program (healthcare patient forms).
To be able to process the barcode characters, I rely on the Keypreview property in WinForms (because barcodes can be scanned regardless of what control has the focus).
But I cannot seem to find a KeyPreview property in neither VS2008 or VS2010, for a WPF app.
Is there an alternative approach/solution to handle my barcode characters in WPF?
Rgrds Henry
use the override in your own UserControls or Controls (this is an override from UIElement)
protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e) {
base.OnPreviewKeyDown(e);
}
if you want to preview the key down on any element which you dont create you can do this:
Label label = new Label();
label.PreviewKeyDown += new KeyEventHandler(label_PreviewKeyDown);
and then have a handler like so :-
void label_PreviewKeyDown(object sender, KeyEventArgs e) {
}
if you mark the event as handled (e.Handled = true;) this will stop the KeyDown event being raised.
Thanks got it working! Only problem was I'm coding in VB not C#, but the basic idea holds. Neat to create a label out of thin air and use it to insert yourself in the event stream.
If someone else is interested of the same solution but in VB for WPF, here's my test program, it manages to toss all 'a' characters typed, no matter what control has the focus:
Class MainWindow
Dim WithEvents labelFromThinAir As Label
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
AddHandler MainWindow.PreviewKeyDown, AddressOf labelFromThinAir_PreviewKeyDown
End Sub
Private Sub labelFromThinAir_PreviewKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
TextBox1.Text = e.Key ' watch 'em coming
If (44 = e.Key) Then e.Handled = True
End Sub
End Class
P.S. This was my first post on stackoverflow, really a useful site. Perhaps I'll be able to answer some questions in here myself later on :-)
WPF uses event bubbling and tunneling. In other words the events travel down and up the visual element tree. Some events will have a corresponding Preview event. So MouseDown will have a PreviewMouseDown that you can respond to. Check out this link and scroll down to the WPF Input Events section.

Drag window from a grid

I have created a custom window set to windowStyle="none", (no title or maximize - minimize buttons) and i am trying to implement a DragMove operation when the user clicks and drags on a grid. (this is wired up by calling DragMove on a MouseLeftButtonDown Handler)
First weird issue that this event nevers gets fired if the grid has no backround.
Adding some background color does make the event to get fired, but after the first drag i get this error:
"Can only call DragMove when primary mouse button is down"
Code Snipet:
Private Sub Grid1_MouseLeftButtonDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles Grid1.MouseLeftButtonDown
DragMove()
End Sub
I know that this would work fine for a label, but isnt there a way to make it work for a grid?
OK, I found the answer..
I used a border to wrap the grid and then caught the Border1_MouseLeftButtonDown event.
I also had to set the borders Background to "Transparent", and now everything works like a charm.
Private Sub Border1_MouseLeftButtonDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles Border1.MouseLeftButtonDown
DragMove()
End Sub

Resources