.Net Toolstrip/MenuStrip Focus Issues - c#

No matter what the scenario may be I'm able to recreate this annoying problem 100% of the time.
Create a .Net project, C# or VB.Net. Add a ToolStrip control to the form. Create a few simple DropDownButton(s) that contain at least 2 menu items. Add any other controls you wish, a list box (populate it so it can receive focus correctly) and a ComboBox control. Either assign shortcut keys or enable TabStop on the ToolStrip so that it can receive focus by Keyboard.
Run the project (Debug/Release, which ever you fancy). Use your Keyboard to give the ToolStrip Focus (by tab or shortcut key). Arrow down into a sub item. Now select the escape key to collapse the Toolstrip sub menu. Tab to the ListBox or ComboBox that contains a few Items.
All looks great right? Now use your arrow keys to navigate in these controls... Surprise! your back on the ToolStrip and the control you thought had focus doesn't!
I've tried multiple things to force focus on the ListBox. One example is I'd add the event handler for OnEnter (ListBox.Enter+=...) and add some code like:
ListBox.Focus();
ListBox.Select();
Nothing was a success... It seems like once the menu expands on a toolstrip you will be forever stuck on this control using your Keyboard...
This is important for me to resolve due to the fact that i work with blind users whom use keyboard navigation only... Is this a bug? I cannot reproduce this in MFC...
Any suggestions?
Update
I was able to find a control that doesn't reproduce this strangeness...
System.Windows.Forms.MainMenu is the only "Tool Bar object" that doesn't behave like the others...
I'd still like some feedback on the above though (Help for others and myself)...
Update 2
The underlying issue is within [ToolStripObject].TabFocus property... if set to false all seems to work ok... giving focus back to the control that "looks" like it's focused. But having that capability to allow a blind user to navigate throughout all UI controls via tab is a handy thing to implement... it's too bad this property doesn't work like it should....

I got it to work by overriding the ToolStripMenuItem:
public class ToolStripMenuItemEx : ToolStripMenuItem {
protected override bool ProcessCmdKey(ref Message m, Keys keyData) {
if (keyData == Keys.Escape) {
ToolStripDropDownButton tb = this.OwnerItem as ToolStripDropDownButton;
if (tb != null) {
tb.HideDropDown();
return false;
}
}
return base.ProcessCmdKey(ref m, keyData);
}
}

Related

C# Combobox, give focus to Popup on keydown

I've created a class that inherits from Combobox, the class is editable. When a user have searched for a given predicate, I want them to be able to iterate the list with pressing the down key. This is somewhat implemented as it works sporadically. Is it any way to force focus to the popup element? I would like to do this by code, and not in XAML.
I've tried
var popup = Template.FindName("PART_Popup", this) as Popup;
popup.Focus();
Which return false as expected as this didn't work.
I've also tried
var s1 = Keyboard.FocusedElement;
if (s1 is FrameworkElement)
{
((FrameworkElement)s1).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
But this focus on the next element in the TAB stack.
This focus approach didn't work either,
Keyboard.Focus(popup);
Edit:
I solved (achieved the desired effect) by setting the animation to .None, and then close followed by open the popup again.

How to create a custom .NET ComboBox with the textbox active during edit

I'm trying to implement my own combobox like a lot of folks before me. What I want to accomplish is a combobox that filters and highlights items in the dropdown list while the user is typing in the combo textbox. The behaviour of a regular combobox after you click the arrow button is that the dropdown pops up and the focus stays in the textbox. This way you can start typing right away.
In order to customize the dropdown control you have to implement something from scratch. Most of the implementations that I've come across use either a Form or a ToolStripDropDown to host the custom control. Both are toplevel controls which means that you have to somehow close it yourself if the user clicks somewhere outside the dropdown. ToolStripDropDown does this automatically if AutoClose is true, but also somehow steals the combo textbox the focus on show if it is activated. A Form must be shown using ShowWithoutActivation() in order to prevent it from stealing the focus.
The problem is that the dropdown does never close unless I click somewhere within the dropdown and therefore activate it.
Another twist is that the combobox control is supposted to be hosted in an MFC application instead of a pure WinForms app.
The dropdown never being activated (gaining focus) is the main complication here. Otherwise you could just use the forms Deactivate event to hide it. The way to go here is to add an IMessageFilter to the WinForms application and catch mouse click messages. The message filter then determines whether the click took place outisde of the dropdown and closes it. If you are creating a WinForms application you are done.
Some extra work is necessary if you are for example in a MFC application hosting the control in a MFC window. In that case your IMessageFilter is useless. The reason is that the WinForms Application is never being run and therefore the event pump is never being invoked. Instead the MFC message pump does all the message handling. To solve this issue I've come accross a neat trick to activate the Application message pump in MFC applications.
In MFC applications there is usually an equivalent to the WinForms Application which is CWinApp (or CWinAppEx). The trick is to tap into the PreTranslateMessage method and serve the WinForms Application message pump before (or after) the MFC message pump:
BOOL CWinApp::PreTranslateMessage(MSG* pMsg)
{
if (FilterWindowsFormsMessages(pMsg))
{
return TRUE;
}
return CWinApp::PreTranslateMessage(pMsg);
}
BOOL CWinApp::FilterWindowsFormsMessages(MSG* pMsg)
{
Message message = Message::Create(IntPtr(pMsg->hwnd),
int(pMsg->message),
IntPtr((void*)pMsg->wParam),
IntPtr((void*)pMsg->wParam));
if (Application::FilterMessage(message))
{
return TRUE;
}
return FALSE;
}
This way the registered IMessageFilters are being served and everything works fine.

How to get started with c# for testing keypressed event?

I am quite new to c#, and the first thing i want to do is being familiar with that environment by trying Key's Combination Events.
In particular, alt+k.
I'm working on Microsoft Visual c# 2010 Express.
I want to test if that code works.
If errors are found, please notify me :)
public void begin(object sender, KeyEventArgs ev)
{
if (ev.KeyCode == Keys.K && ev.Modifiers == Keys.Alt)
{
//display a message
}
}
but even if I know theoretically what are the different models of projects that are proposed when clicking on new project and what are their uses, I tried unsuccessfully several models to test that code.
In short, i don't know what models to choose and where to put code for testing that kind of simple code, and more precisely working on events(key+mouse) with a minimalist gui.
Someone could help me to tell me how to concretly get started with events stuff in c#?
Thks in advance :)
The MSDN Documentation has a good example of what you need to do. Here are a few important parts:
Start with a simple "Windows Forms" application. While there are many other types of applications, WinForms is the simplest to start with.
Wire up the KeyPress event. In the Form constructor, you need to tell it what to do when it gets a KeyPress event. If you change the name of your function begin in your question above to Form1_KeyPress (to more-accurately describe what it does), the following code should work:
this.KeyPress += new KeyPressEventHandler(Form1_KeyPress);
Use KeyPressEventArgs instead of KeyEventArgs. It may or may not make a huge difference, but it is good to use the most-specific EventArgs when you can so you can use properties specific to it.
Pay attention to KeyPressEventArgs.Handled. If you have KeyPress (or some other keyboard events) on other objects on your forms, such as buttons or text boxes, you need to say whether the event was handled there or whether it should bubble up to the parent (in your case, to the Form).
EDIT (Thanks #RBarryYoung):
Set this.KeyPreview = true in the form constructor. In order for your form to receive keyboard events that happen on child controls (such as buttons and text boxes) as mentioned in the tip immediately above this one, you need to set this property to true to allow the form to get a first look at Keyboard events that happen on the child. (Note: of course, this tip and the previous one only apply if you want the form to see these events. Sometimes, you might want keyboard shortcuts to be trapped in the child control instead of being handled in the parent.)
It can help you:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.K && this.AcceptButton == null)
{
MessageBox.Show("Key K pressed");
}
return base.ProcessCmdKey(ref msg, keyData);
}
It will detect if key K is pressed in any control of your form and return msgbox
Test it and try make work your way.

How can I locate an invisible menu item?

I'm refactoring a body of code, looking through it all, line by line.
I came across an event handler:
private void mnuUpdate_Click(object sender, EventArgs e)
...and, not recognizing which menu item called this (the menu item names do not always match their labels, or even come close), was curious.
The main menu on the form has no such menu item among its children.
I r-clicked the event handler, selected "Find Usages*" and was led here:
this.mnuUpdate.Text = "Update";
this.mnuUpdate.Click += new System.EventHandler(this.mnuUpdate_Click);
(This is an antedeluvial app that predates .NET's partial class goodness, so this is in the same file)
On the form in the designer, when I select "mnuUpdate" from the properties page combobox, the mainMenu on the form disappears altogether.
How can I track down this fugitive menu item? There is no popupMenu or contextMenu on the form, just the mainMenu control...???
The only other usage is:
if (ResetConnectionFetchForm)
mnuUpdate_Click(sender, e);
Is it possible that this is simply a phantom menu item that should be converted into a "regular old" method?
UPDATE
As the most intelligent George used to say, "Curiouser and Curiouser." Now I find this:
public void btnCancel_Click(object sender, EventArgs e)
...and though it is called from seven places in the code, there is no btnCancel on the form...It is a "fake" button click event. Oh my Lanta!!!
So, I replaced it with a parameterless private method with the exact same code (it didn't use either sender or event args).
If the cat who wrote this cockamamie glob of fruitcake-battered spaghetti was deliberately trying to drive the next cat (me) crazy, it's working pretty well, and would make a good Poe-style story or Hitchcock-style flick.
...I see...Dead Code!!!
Okay, mystery solved. mnuUpdate is dynamically added (conditionally) to mnuSetup (which is a top level menu item with the Text property "Fetch") like so:
if (!mnuSetup.MenuItems.Contains(mnuUpdate))
{
mnuSetup.MenuItems.Add(mnuUpdate);
UpdateMenuItemSelectable = true;
}
I reckon selecting mnuUpdate from the combobox in the form's Properties page is because there is no visual representation to show at that point.
Selecting "mnuSetup" highlights the "Fetch" menu item, but selecting "mnuUpdate" causes it all to scurry away faster than cockroaches from the light.
So the bizarre thing about it now is: why is the menu item not dynamically created as necessary, instead of being explicitly created and then dynamically added; seems like a strange way for a cat to skin a cat.
I'd suggest you turn it into "regular old menu" so someone else doesn't waste time figuring it out.
Me - I would have thought it obsolete code because it doesn't have a Handles clause.
You can use .Visible and .Enabled to control what the user sees.

keep focus in a WPF combo box in an addin

I have a combobox in a Word addin. The contents of the combobox are often long enough to drape over Word's Zoom slider-bar control. However, selecting an item directly over the zoom control (which is hidden from view) causes the zoom control to get focus, close the combobox, and change your zoom setting! The selection in the combobox is untouched.
How can I make the combo box keep focus and change the selected value to the item (over the zoom bar) selected? Thanks...
I've run into this same issue with WPF, and it looks like it has something to do with the way Word is handling events from child windows. Whenever the dropdown list (or probably any other "popup" control like a context menu) is drawn over one of Word's windows, it gets a bit greedy and assumes you're clicking on the underlying window.
I don't know a whole lot about how messaging/events works in Windows and I haven't had time to figure out the best way to address the problem, but based on a post somewhere about creating borderless windows I tried modifying the window styles of the WinForms user control as follows (windows style constants from http://www.pinvoke.net/default.aspx/Constants/Window%20styles.html):
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
if (!DesignMode)
{
unchecked
{
p.Style = (int)(WindowStyles.WS_VISIBLE |
WindowStyles.WS_POPUP |
WindowStyles.WS_CLIPSIBLINGS |
WindowStyles.WS_CLIPCHILDREN);
}
}
return p;
}
}
Curiously (or maybe not so curiously for people that are more familiar with Windows messages), the dropdown DOES respond to keyboard events (e.g. click to pop up the list, then use the keyboard to select an item).
Functionally there doesn't seem to be a problem with the above code...but I'm not sure what the ramifications are of saying the user control is a popup instead of a child.
Another post that relates to this is WPF ComboBox doesn't stay open when used in a Task Pane.

Categories

Resources