I have a simple form like this:
I open the combobox and at the time dropdown is open, I click the button. On button click I show a simple message but the message is not shown at that time.
It shows when I click it again.
The same problem for textbox. When the dropdown is open, the textbox click is not working.
Why does combobox prevent clicking other controls when it is open?
You can create an event for ComboBox DropDownClosed and with the hittestfunction, find the other control that the user has clicked.
private void ComboBox_DropDownClosed(object sender, EventArgs e)
{
Point m = Mouse.GetPosition(this);
VisualTreeHelper.HitTest(this, this.FilterCallback, this.ResultCallback, new PointHitTestParameters(m));
}
Then in the FilterCallback function after finding that control, raise the mouse down event on that control.
private HitTestFilterBehavior FilterCallback(DependencyObject o)
{
var c = o as Control;
if ((c != null) && !(o is MainWindow))
{
if (c.Focusable)
{
if (c is ComboBox)
{
(c as ComboBox).IsDropDownOpen = true;
}
else
{
var mouseDevice = Mouse.PrimaryDevice;
var mouseButtonEventArgs = new MouseButtonEventArgs(mouseDevice, 0, MouseButton.Left)
{
RoutedEvent = Mouse.MouseDownEvent,
Source = c
};
c.RaiseEvent(mouseButtonEventArgs);
}
return HitTestFilterBehavior.Stop;
}
}
return HitTestFilterBehavior.Continue;
}
private HitTestResultBehavior ResultCallback(HitTestResult r)
{
return HitTestResultBehavior.Continue;
}
The combobox is implemented the way that it captures the mouse when the dropdown is open. This is done to easyly figure out when the user clicks outside of the combobox (in fact it's a one-liner). When the user clicks outside of the combobox it releases the mouse, closes the dropdown and marks the click as handled. The last action of course stops further processing and the click is not passed to the control you thought you clicked on.
My personal opinion is this behavior has pros and cons. Microsoft decided the way it is.
Related
My winform contains a TextBox that is the main control of the form. When I do CtrL + C, and often end up with an empty clipboard because for some reason the ActiveControl of the form is set to another control, like for instance the TabControl, the SplitContainer, etc. I tried to set those control TabStop = fasle, but to avail. Is there a way to prevent all controls from getting focus ? Not only regarding the mouse clicks, but also the tab keys.
I can imagine trying with some native windows messages like WM_SetFocus, or some native windows styles like WS_TABSTOP, or some control styles like ControlStyles.Selectable, or ControlStyles.UserMouse, but I'd say you cannot find a generic solution which handles all controls.
What I'm proposing here (according to your comments, looking for a better way of handling focus rather than trying to make things focusable, which makes more sense in UX point of view as well) is handling TabControl and SplitterContainer focus related events:
Tab control: Handle SelectedIndexChanged, and then move the focus to the first focusable control of the tab.
Splitter container: Handle MouseDown to trap the start of splitting, then store the active panel; later handle SplitterMoved and move the focus to he first focusable control of the active panel.
Here is what worked for me:
SplitterPanel activePanel;
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
{
splitContainer1.SelectNextControl(activePanel,
forward: true, tabStopOnly: true, nested: true, wrap: true);
}
private void splitContainer1_MouseDown(object sender, MouseEventArgs e)
{
activePanel = splitContainer1.Panel1.ContainsFocus ? splitContainer1.Panel1 :
splitContainer1.Panel2.ContainsFocus ? splitContainer1.Panel2 : null;
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
var page = this.tabControl1.SelectedTab;
page.SelectNextControl(null,
forward: true, tabStopOnly: true, nested: true, wrap: true);
}
Instead of preventing focus, which might cause problems for those trying to navigate your app using only the keyboard, implement the IMessageFilter interface and trap Ctrl-C for your whole app. Then you can simply put the contents of your "main textbox" on the clipboard manually:
public partial class Form1 : Form, IMessageFilter
{
public Form1()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (m.Msg == 0x0100 &&
(Keys)m.WParam.ToInt32() == Keys.C &&
ModifierKeys == Keys.Control)
{
Console.WriteLine("Ctrl-C Trapped!");
if (textBox1.SelectionLength > 0)
{
Clipboard.SetText(textBox1.SelectedText);
}
else
{
Clipboard.SetText(textBox1.Text);
}
return true;
}
return false;
}
}
Given the following insane setup (a ComboBox inside a UserControl inside a ToolStripControlHost inside a ContextMenuStrip):
there's something odd going on with clicking on different items in the ComboBox popup. If the item is inside the menu bounds (i.e. Amsterdam, Brussel or Luxemburg) the item is selected. If the item is outside the menu bounds (i.e. Berlijn and further) the menu is closed immediately.
Ignoring any spiffy remarks regarding the sheer crazy, anyone know what's going on and how to stop the menu from closing if a distant combobox item is selected?
The problem is due to a check deep in the ToolStripManager determining the mouse event is not on a child.
Basically you need to reject the ContextMenuStrip.OnClosing event if the ComboBox is displaying. There's inevitably a cleaner solution but I didn't see one.
public bool IsDropDownShowing { get; private set; }
private void InitializeContextMenu()
{
var userControl = new ComboMenuUserControl();
var toolStripHost = new ToolStripControlHost(userControl);
contextMenuStrip1.Items.Add(toolStripHost);
userControl.comboBox1.DropDown += (o, args) => IsDropDownShowing = true;
userControl.comboBox1.DropDownClosed += (o, args) => IsDropDownShowing = false;
contextMenuStrip1.Closing += (o, args) =>
{
if (IsDropDownShowing == true)
args.Cancel = true;
};
}
I have a WPF application in which on a click of a menu item a window is opened. If the same menu item is clicked again when the window is already open, it is opening a new window but I don't want a new window to be opened every time.
What I need is, if the window is already open, the same window should be focused not a new window.
//First we must create a object of type the new window we want the open.
NewWindowClass newWindow;
private void OpenNewWindow() {
//Check if the window wasn't created yet
if (newWindow == null)
{
//Instantiate the object and call the Open() method
newWindow= new NewWindowClass();
newWindow.Show();
//Add a event handler to set null our window object when it will be closed
newWindow.Closed += new EventHandler(newWindow_Closed);
}
//If the window was created and your window isn't active
//we call the method Activate to call the specific window to front
else if (newWindow != null && !newWindow.IsActive)
{
newWindow.Activate();
}
}
void newWindow_Closed(object sender, EventArgs e)
{
newWindow = null;
}
I think this solve your problem.
Att,
If your opened windows is used as simple dialog box you can use following code
window.ShowDialog();
when the dialog will show you cannot press any menu items unit you close this window
A rather brute force approach like this also works:
bool winTest = false;
foreach (Window w in Application.Current.Windows)
{
if (w is testWindow)
{
winTest = true;
w.Activate();
}
}
if (!winTest)
{
testWindow tw = new testWindow();
tw.Show();
}
You can create a field and check if it's set:
private Window _dialogue = null;
private void MaekWindowButton_Click(object sender, RoutedEventArgs e)
{
if (_dialogue == null)
{
Dialogue diag = new Dialogue();
_dialogue = diag;
diag.Closed += (s,_) => _dialogue = null; //Resets the field on close.
diag.Show();
}
else
{
_dialogue.Activate(); //Focuses window if it exists.
}
}
I have a Window with seven buttons; I use it as a menu in a simple game I am working on, but I display it as a dialog. How can I know which button user has pressed, since DialogResult in WPF only offers true, false and null?
If you're making a custom Window in this way, you don't really need to worry about DialogResult.
You can keep track of this in a property within your Window, and just read the property after the dialog is closed.
MyDialog window = new MyDialog();
if (window.ShowDialog() == false)
{
// user closed the window...
}
var choice = window.CustomPropertyContainingChoice;
Define your own enum and offer a static method to display the window that return your enum.
The code below does the same thing it is part of a window that allows users to review their changes and accept or cancel. As I only need true and false I used a bool however it would be trivial to change to an enum.
public static bool DisplayChanges(List<INormalizedMessage> LstMessages)
{
var retlist = LstMessages.Where(( INormalizedMessage NM ) => { return NM.Status != NormalizedMessageStatus.NoChange; });
ReviewChanges RC = new ReviewChanges();
RC.Messages = retlist.ToList();
RC.ShowDialog();
return RC.Result;
}
private void cmdCancle_Click( object sender, RoutedEventArgs e )
{
Result = false;
Hide();
}
private void cmdOK_Click( object sender, RoutedEventArgs e )
{
Result = true;
Hide();
}
i want to have a tooltip for each item in a treeview, and each item in a listview, and different for each subitem (i.e. column) in the listview.
i can determine the text i want to show (using hit testing with the current mouse position, etc):
private void toolTip1_Popup(object sender, PopupEventArgs e)
{
if (e.AssociatedControl == listView1)
{
toolTip1.SetToolTip(listView1, "foo");
}
}
but any attempt to set the tooltip text causes a stackoverflow.
How can i customize the tooltip (icon, title, text) just before it appears?
You need to guard your code in the Popup event handler so that if you are calling SetToolTip from within it, you don't call SetToolTip again.
Something like:
private bool updatingTooltip;
private void toolTip1_Popup(object sender, PopupEventArgs e)
{
if (!this.updatingTooltip && (e.AssociatedControl == listView1))
{
this.updatingTooltip = true;
toolTip1.SetToolTip(listView1, "foo");
this.updatingTooltip = false;
}
}