How to deselect textbox if user clicks elsewhere on the form? - c#

Currently in my application it is impossible to deselect a textbox. The only way is to select another textbox. My users and I agree that clicking anywhere else on the form should deselect the current textbox. I tried overriding the MouseDown on many controls and having the focus set to a random label but it doesn't work for some controls like the MenuStrip or scrollbars. Any ideas?

Assuming you have no other controls on your forum, try adding a Panel control that can receive focus.
Set the TabIndex on the Panel control to something less than your TextBox or NumericUpDown control has.
Now, when your main form receives focus, the Panel should receive the focus instead of the TextBox area.

I had a similar issue recently. My interface is very complex with lots of panels and tab pages, so none of the simpler answers I found had worked.
My solution was to programatically add a mouse click handler to every non-focusable control in my form, which would try to focus any labels on the form. Focusing a specific label wouldn't work when on a different tab page, so I ended up looping through and focusing all labels.
Code to accomplish is as follows:
private void HookControl(Control controlToHook)
{
// Add any extra "unfocusable" control types as needed
if (controlToHook.GetType() == typeof(Panel)
|| controlToHook.GetType() == typeof(GroupBox)
|| controlToHook.GetType() == typeof(Label)
|| controlToHook.GetType() == typeof(TableLayoutPanel)
|| controlToHook.GetType() == typeof(FlowLayoutPanel)
|| controlToHook.GetType() == typeof(TabControl)
|| controlToHook.GetType() == typeof(TabPage)
|| controlToHook.GetType() == typeof(PictureBox))
{
controlToHook.MouseClick += AllControlsMouseClick;
}
foreach (Control ctl in controlToHook.Controls)
{
HookControl(ctl);
}
}
void AllControlsMouseClick(object sender, MouseEventArgs e)
{
FocusLabels(this);
}
private void FocusLabels(Control control)
{
if (control.GetType() == typeof(Label))
{
control.Focus();
}
foreach (Control ctl in control.Controls)
{
FocusLabels(ctl);
}
}
And then add this to your Form_Load event:
HookControl(this);

Since you probably have a label, or any other control on your winform, I would go with the solution recommended here and just give the focus to a label when the Form gets clicked.
Worst case, you can even add a label situated at the -100, -100 position, set him as the first in the tab order and Focus() it on form click.

I have some kind of "workaround" for you. Just but another control (that can get the focus) in the background. I tested this for a GridView (which will paint your control grey) - but you should be able to do it with a custom control in the color you want or just set the backgroundcolor of the gridview (doh).
This way everytime the user clicks the background this backgroundcontrol will get the focus.

This is generic answer: To deselect TextBoxes when user clicks anywhere else on the form, first make controls to lose focus. For this subscribe to Click Event of the form:
private void Form1_Click(object sender, EventArgs e)
{
this.ActiveControl = null;
}
Next subscribe your TextBoxes to Focus Leave event and set SelectionLength to 0 (to deselect text, somehow it is not deselected although textbox does not show selection when loses focus):
private void textBoxes_Leave(object sender, EventArgs e)
{
TextBox txbox = sender as TextBox;
txbox.SelectionLength = 0;
}
If you have your TexBoxes nested in custom user control, you have to add events within that user control in a similar manner. Hope that helps to anyone else.

Related

WPF ListViewItem focus selects second row

I have search TextBox, and I want to when I press Arrow Down to navigate through the ListView, and if I press Left/Right Arrow on ListView I want to navigate with Caret through the text, and if I press down again I navigate through the ListView again. Just regular process like Chrome Search suggestions.
I just want to get Focus like when pressing TAB button. With TAB all works great.
On TextBox I use PreviewKeyDown event and detect Key.Down and I set my ListView.Focus() - That works great. ListView is focused and I can Navigate through items.
On ListView I use PreviewKeyDown event and detect any key that is not Key.Down or Key.Up and set focus on TextBox and set selection to the end of text. - That works great. Textbox is focused and I can Navigate through the text with Left/Right arrow.
Problem is now if I press Down arrow. Nothing happens. I can't get focus on ListView again. Don't know why.
I used Keyboard.Focus(), Keyboard.ClearFocus(), FocusManager... Nothing helps, focus remain on TextBox.
I used ItemContainerGenerator with SelectedIndex and that works but Selects second item in ListView. I think it is because I select first index and when I press down ListView gets focus and pass that Arrow Down event. And I ending with second row in ListView.
TextBox on PreviewKeyDown:
private void SearchTermTextBox_OnKeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Down) {
SuggestionsListView.Focus();
}
else if (e.Key == Key.Escape)
{
SearchTermTextBox.Clear();
}
}
ListView on PreviewKeyDown:
private void SuggestionsListView_OnPreviewKeyDown(object sender, KeyEventArgs e) {
if (e.Key != Key.Down && e.Key != Key.Up) {
SearchTermTextBox.Focus();
SearchTermTextBox.Select(SearchTermTextBox.Text.Length, 0);
}
}
I used this code with ItemContainerGenerator. But, like I said, it's selecting second row from some reason.
SuggestionsListView.SelectedItem = SuggestionsListView.Items[0];
ListViewItem item = SuggestionsListView.ItemContainerGenerator.ContainerFromItem(SuggestionsListView.SelectedItem) as ListViewItem;
item.Focus();

To Enable right click in disabled control

I Have a textbox which is disabled and has value .And i want to enable right click option to copy the disabled value from textbox (Windows application).Pls help me to do this.
Try this, keeping in mind that you have to have your contextmenustrip added:
private void YourFormName_Load(object sender, EventArgs e)
{
ContextMenu mnu = new ContextMenu();
MenuItem mnuCopy = new MenuItem("Copy");
mnuCopy.Click += (sen, ev) =>
{
System.Windows.Forms.Clipboard.SetText(YourTextBoxName.Text);
};
mnu.MenuItems.AddRange(new MenuItem[] { mnuCopy });
YourTextBoxName.ContextMenu = mnu;
}
private void YourFormName_MouseUp(object sender, MouseEventArgs e)
{
Control ctl = this.GetChildAtPoint(e.Location);
if (ctl != null && !ctl.Enabled && ctl.ContextMenu != null)
ctl.ContextMenu.Show(this, e.Location);
}
When you click on a disabled element in a page, the event is handled by the parent element of the disabled element.
for example if your textbox is in a page then the page handles it. if the text box is in a different container like div, then that container will handle the mouse click event.
for your situation, you could write a handler on the parent element.
a javascript function that will catch the event and it can read the value for you. for instance, the JS function can change the disabled property to false, read the value, and then disable the textbox again.
Coming back to Vijaya's answer, I handled the problem by just placing the control with Dock=Fill into a panel control with zero padding and margin. So you would do your things in the MouseUp event of the panel instead.

Preventing user to click a Tab Page

I haven't found a solution yet related to this problem. I just to disable other tabpages in my Winforms TabControl when a certain tabpage is open. So not hiding them but disable the function to open them when one clicks a tab page. It just should be displayed grey. Is this possible? I've read something about a "Selected" event but don't know how to use that.
You can use the Selecting event:
Create a class level variable:
int lockedPage = -1;
If it is set to the index of a TabPage you can select it but you can't leave it, i.e. you can't select any other page.
private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
{
if (lockedPage >= 0 && e.TabPageIndex != lockedPage) e.Cancel = true;
}
If you set lockedPage = 0; you prevent the user from leaving the 1st page etc..
To re-enable the selection of other pages set it to -1

How to change listview selected row backcolor even when focus on another control?

I have a program which uses a barcode scanner as input device so that means I need to keep the focus on a text box.
The program has a listview control and I select one of the items programatically when a certain barcode is scanned. I set the background color of the row by:
listviewitem.BackColor = Color.LightSteelBlue;
Things I have tried:
listview.HideSelection set to false
call listview.Focus() after setting the color
listviewitem.Focused set to true
call listview.Invalidate
call listview.Update()
call listview.Refresh()
different combinations of the above
I've also did combinations above stuff in a timer so that they are called on a different thread but still no success.
Any ideas?
More info:
The key here is the control focus. The listview control does not have the focus when I select one of the items.
I select one item by doing:
listView1.Items[index].Selected = true;
the Focus is always in the textbox.
the computer does not have keyboard or mouse, only a barcode reader.
I have this code to keep the focus on the textbox:
private void txtBarcode_Leave(object sender, EventArgs e)
{
this.txtBarcode.Focus();
}
You need to have a textbox add that code to simulate my problem.
What you describe works exactly as expected, assuming that you've set the HideSelection property of the ListView control to False. Here's a screenshot for demonstration purposes. I created a blank project, added a ListView control and a TextBox control to a form, added some sample items to the ListView, set its view to "Details" (although this works in any view), and set HideSelection to false. I handled the TextBox.Leave event just as you showed in the question, and added some simple logic to select the corresponding ListViewItem whenever its name was entered into the TextBox. Notice that "Test Item Six" is selected in the ListView:
Now, as I suspected initially, you're going to mess things up if you go monkeying around with setting the BackColor property yourself. I'm not sure why you would ever want to do this, as the control already uses the default selection colors to indicate selected items by default. If you want to use different colors, you should change your Windows theme, rather than trying to write code to do it.
In fact, if I add the line item.BackColor = Color.LightSteelBlue in addition to my existing code to select the ListViewItem corresponding to the name typed into the TextBox, I get exactly the same thing as shown above. The background color of the item doesn't change until you set focus to the control. That's the expected behavior, as selected items look different when they have the focus than they do when their parent control is unfocused. Selected items on focused controls are painted with the system highlight color; selected items on unfocused controls are painted with the system 3D color. Otherwise, it would be impossible to tell whether or not the ListView control had the focus. Moreover, any custom BackColor property is completely ignored by the operating system when the ListView control has the focus. The background gets painted in the default system highlight color.
Explicitly setting the focus to the ListView control, of course, causes the custom background color to be applied to the ListViewItem, and things render with a color that very much contrasts with the color scheme that I've selected on my computer (remember, not everyone uses the defaults). The problem, though, becomes immediately obvious: you can't set the focus to the ListView control because of the code you've written in the TextBox.Leave event handler method!
I can tell you right now that setting the focus in a focus-changing event is the wrong thing to do. It's a hard rule in Windows you're not allowed to do things like that, and the documentation even warns you explicitly not to do it. Presumably, your answer will be something along the lines of "I have to", but that's no excuse. If everything were working as expected, you wouldn't be asking this question in the first place.
So, what now? Your application's design is broken. I suggest fixing it. Don't try and monkey with setting the BackColor property yourself to indicate that an item is selected. It conflicts with the default way that Windows highlights selected items. Also, don't try and set the focus in a focus-changing event. Windows explicitly forbids this, and the documentation is clear that you're not supposed to do this. If the target computer doesn't have a mouse or keyboard, it's unclear how the user is going to set focus to anything else in the first place, unless you write code to do it, which you shouldn't be doing.
But I have surprisingly little faith that you'll want to fix your application. People who ignore warnings in the documentation tend to be the same people who don't listen to well-meaning advice on Q&A sites. So I'll throw you a bone and tell you how to get the effect you desire anyway. The key lies in not setting the ListViewItem's Selected property, which avoids the conflict between your custom BackColor and the system default highlight color. It also frees you from having to explicitly set the focus to the ListView control and back again (which, as we established above, isn't actually happening, given your Leave event handler method). Doing that produces the following result:
And here's the codeā€”it's not very pretty, but this is just a proof of concept, not a sample of best practice:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
listView1.View = View.Details;
listView1.HideSelection = false;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
foreach (ListViewItem item in listView1.Items)
{
if (item.Text == textBox1.Text)
{
item.BackColor = Color.LightSteelBlue;
return;
}
}
}
private void textBox1_Leave(object sender, EventArgs e)
{
this.textBox1.Focus();
}
}
A standard ListView does not let you set the background color of a selected row. The background (and foreground) colors of a selected row are always controlled by the theme of the OS.
You have to owner draw your ListView to get around this OR you can use ObjectListView. ObjectListView is an open source wrapper around .NET WinForms ListView, which makes it much easier to use, as well as easily allowing things that are very difficult in a normal ListView -- like changed the colors of selected rows.
this.objectListView1.UseCustomSelectionColors = true;
this.objectListView1.HighlightBackgroundColor = Color.Lime;
this.objectListView1.UnfocusedHighlightBackgroundColor = Color.Lime;
This shows the ObjectListView when it does not have focus.
Here's a solution for a ListView that does not allow multiple selections and
does not have images (e.g. checkboxes).
Set event handlers for the ListView (in this example it's named listView1):
DrawItem
Leave (invoked when the ListView's focus is lost)
Declare a global int variable (i.e. a member of the Form that contains the ListView,
in this example it's named gListView1LostFocusItem) and assign it the value -1
int gListView1LostFocusItem = -1;
Implement the event handlers as follows:
private void listView1_Leave(object sender, EventArgs e)
{
// Set the global int variable (gListView1LostFocusItem) to
// the index of the selected item that just lost focus
gListView1LostFocusItem = listView1.FocusedItem.Index;
}
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
// If this item is the selected item
if (e.Item.Selected)
{
// If the selected item just lost the focus
if (gListView1LostFocusItem == e.Item.Index)
{
// Set the colors to whatever you want (I would suggest
// something less intense than the colors used for the
// selected item when it has focus)
e.Item.ForeColor = Color.Black;
e.Item.BackColor = Color.LightBlue;
// Indicate that this action does not need to be performed
// again (until the next time the selected item loses focus)
gListView1LostFocusItem = -1;
}
else if (listView1.Focused) // If the selected item has focus
{
// Set the colors to the normal colors for a selected item
e.Item.ForeColor = SystemColors.HighlightText;
e.Item.BackColor = SystemColors.Highlight;
}
}
else
{
// Set the normal colors for items that are not selected
e.Item.ForeColor = listView1.ForeColor;
e.Item.BackColor = listView1.BackColor;
}
e.DrawBackground();
e.DrawText();
}
Note: This solution will result in some flicker. A fix for this involves subclassing the ListView control so you
can change the protected property DoubleBuffered to true.
public class ListViewEx : ListView
{
public ListViewEx() : base()
{
this.DoubleBuffered = true;
}
}
On SelectedIndexChanged:
private void lBxDostepneOpcje_SelectedIndexChanged(object sender, EventArgs e)
{
ListViewItem item = lBxDostepneOpcje.FocusedItem as ListViewItem;
ListView.SelectedIndexCollection lista = lBxDostepneOpcje.SelectedIndices;
foreach (Int32 i in lista)
{
lBxDostepneOpcje.Items[i].BackColor = Color.White;
}
if (item != null)
{
item.Selected = false;
if (item.Index == 0)
{
}
else
{
lBxDostepneOpcje.Items[item.Index-1].BackColor = Color.White;
}
if (lBxDostepneOpcje.Items[item.Index].Focused == true)
{
lBxDostepneOpcje.Items[item.Index].BackColor = Color.LightGreen;
if (item.Index < lBxDostepneOpcje.Items.Count-1)
{
lBxDostepneOpcje.Items[item.Index + 1].BackColor = Color.White;
}
}
else if (lBxDostepneOpcje.Items[item.Index].Focused == false)
{
lBxDostepneOpcje.Items[item.Index].BackColor = Color.Blue;
}
}
}
You cant set focus on listview control in this situation. txtBarcode_Leave method will prevent this. But if you are desire to be able select listview items by clicking on them, just add code below to MouseClick event handler of listview:
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
ListView list = sender as ListView;
for (int i = 0; i < list.Items.Count; i++)
{
if (list.Items[i].Bounds.Contains(e.Location) == true)
{
list.Items[i].BackColor = Color.Blue; // highlighted item
}
else
{
list.Items[i].BackColor = SystemColors.Window; // normal item
}
}
}
This change color of selected item. but only in state listview not have focus.
Make sure HideSelection is !TRUE! and simple use this code:
private void ListView_SelectedIndexChanged(object sender, EventArgs e){
foreach(ListViewItem it in ListView.Items)
{
if (it.Selected && it.BackColor != SystemColors.Highlight)
{
it.BackColor = SystemColors.Highlight;
it.ForeColor = SystemColors.HighlightText;
}
if (!it.Selected && it.BackColor != SystemColors.Window)
{
it.BackColor = SystemColors.Window;
it.ForeColor = SystemColors.WindowText;
}
}
}
Just do like this:
Set property UnfocusedHighlighForegroundColor = "Blue"
Set property UnfocusedHighlighBackgroundColor = "White"
Set property UserCustomSelectionColors = true
Good luck :)

C# - Passing focus to a tabcontrol/page and not being able to mousewheel scroll

I have a combobox with four items that correspond to tabs in a tabcontrol. When the user selects an item from the combobox (by left clicking and left clicking again to select an item) the corresponding tabpage in the tabcontrol is selected. The tabpage is set to autoscroll but when the tabpage is selected in this way mousewheel scrolling does not work. (If I click a control inside that tabpage manually I can then mousewheel scroll..)
If the user mousewheels to select an item from the same combobox (and successfully passes control to the corresponding tabpage) mousewheel scrolling works fine on that tabpage and I cant figure out why.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
switch (comboBox1.SelectedIndex)
{
case 0:
tabControl1.SelectedTab = tabPage3;
tabPage3.Focus();
break;
}
...
}
I can't get a repro of this problem. Something that might help is to set the focus to the first control of the page instead, just like what happens when you fix the problem by clicking a control. And to do so later, after the combobox event is completed. Use this:
private void setFocusToPage(TabPage page) {
var ctl = page.Controls.Count > 0 ? page.Controls[0] : page;
this.BeginInvoke((MethodInvoker)delegate { ctl.Focus(); });
}
Call setFocusToPage instead of the Focus() method in your SelectedIndexChanged event handler.

Categories