I have a C# ComboBox using WPF. I have code that executes when the ComboBox's GotFocus is activated. The issue is that the GotFocus event is executed every time a selection is made from the ComboBox. For example, the GotFocus is executed when you first click on the ComboBox and then when you make a selection even though you have not click on any other control.
Is it possible to prevent this event from firing if a selection is being made in the list or is there a flag or something else in the event handler that can be used to determine if the GotFocus event handler was fired as a result of the user selecting an item in the list?
You can solve this problem with next verification:
private void myComboBox_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource.GetType() == typeof(ComboBoxItem))
return;
//Your code here
}
This code will filter all focus events from items (because they use bubble routing event). But there is another problem - specific behaviour of WPF ComboBox focus: when you open drop-down list with items your ComboBox losing focus and items get. When you select some item - item losing focus and ComboBox get back. Drop-down list is like another control. You can see this by simple code:
private void myComboBox_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource.GetType() != typeof(ComboBoxItem))
{
Trace.WriteLine("Got " + DateTime.Now);
}
}
private void myComboBox_LostFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource.GetType() != typeof(ComboBoxItem))
{
Trace.WriteLine("Lost " + DateTime.Now);
}
}
So you will get anyway atleast two focus events: when you select ComboBox and when you selecting something in it (focus will return to ComboBox).
To filter returned focus after selecting item, you can try to use DropDownOpened/DropDownClosed events with some field-flag.
So the final code with only 1 event of getting focus:
private bool returnedFocus = false;
private void myComboBox_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource.GetType() != typeof(ComboBoxItem) && !returnedFocus)
{
//Your code.
}
}
private void myComboBox_LostFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource.GetType() != typeof(ComboBoxItem))
{
ComboBox cb = (ComboBox)sender;
returnedFocus = cb.IsDropDownOpen;
}
}
Choose from this examples what you actually need more for your application.
I'm not too hot on WPF; but if you're trying to detect changes to the list (click on new value etc) you can use SelectedIndexChanged events..
On the other hand, if you really do want to know simply when the control is focussed, can you filter it by saying something like;
if (combo1.Focused && combo1.SelectedIndex == -1)
{
...
}
.. ? It really depends on what youre trying to detect, exactly.
Another solution is used is to determine whether the new focused element is an existing item in the combobox. If true then the LostFocus event should not be performed, because the combobox still has focus. Otherwise an element outside the combobox received focus.
In the code snipplet below I added the functionality in a custom combobox class
public class MyComboBox : System.Windows.Controls.Combobox
{
protected override void OnLostFocus(RoutedEventArgs e)
{
//Get the new focused element and in case this is not an existing item of the current combobox then perform a lost focus command.
//Otherwise the drop down items have been opened and is still focused on the current combobox
var focusedElement = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this));
if (!(focusedElement is ComboBoxItem && ItemsControl.ItemsControlFromItemContainer(focusedElement as ComboBoxItem) == this))
{
base.OnLostFocus(e);
/* Your code here... */
}
}
}
Related
I have a CheckedListBox in a form. Each item represents an email message subject of a logged in user.
What I try to achieve is that when only one item is selected, both the Edit and Delete buttons should be enabled, otherwise disabled.
I have tried to use the following event handler after setting the CheckOnClick property to true, but it's not working:
private void clbEmailsSubjects_Click(object sender, EventArgs e)
{
btnEdit.Enabled = btnDelete.Enabled = (clbEmailsSubjects.CheckedItems.Count == 1);
}
Any suggestions?
Edit:
I had selected an item, but both buttons were still disabled.
Now, after selecting the second item they have become enabled:
The effect seems to be contrary. I think the value of CheckedItems.Count might be updated after the event_handler is executed.
It would be more correct to use the ItemCheck event than the Click event (as the click may not have landed on a checkbox). But either way, the event gets fired before the Checked property on the CheckBox gets changed, so you can't set the enabled states within either of those event handlers. You can, however, defer the handling until events are processed using BeginInvoke, like this:
private void clbEmailsSubjects_ItemCheck(object sender, ItemCheckEventArgs e)
{
BeginInvoke((Action)(() =>
{
btnEdit.Enabled = btnDelete.Enabled =
(clbEmailsSubjects.CheckedItems.Count == 1);
}));
}
You need to register for the ItemCheck event on your CheckedListBox. Then the following code will give you the desired result:
private void clbEmailsSubjects_ItemCheck(object sender, ItemCheckEventArgs e)
{
btnEdit.Enabled = btnDelete.Enabled =
(clbEmailsSubjects.CheckedItems.Count == 2 && e.NewValue == CheckState.Unchecked) ||
(clbEmailsSubjects.CheckedItems.Count == 0 && e.NewValue == CheckState.Checked);
}
In my .NET 4,5 Winforms application, the ListView control's MouseUp event is firing multiple times when I open a file from that event as follows:
private void ListView1_MouseUp(object sender, MouseEventArgs e)
{
ListViewHitTestInfo hit = ListView1.HitTest(e.Location);
if (hit.SubItem != null && hit.SubItem == hit.Item.SubItems[1])
{
System.Diagnostics.Process.Start(#"C:\Folder1\Test.pdf");
MessageBox.Show("A test");
}
}
Note: When clicking on the SubItem1 of the listview, the file opens but the message box appears at least twice. But, when I comment out the line that opens the file the message box appears only once (as it should). I do need to open the file whose name is clicked by the user in the listview. How can I achieve this without the MoueUp event firing multiple times?
Please also note that the MouseClick event for listview does not always work as also stated here. So, I have to use MouseUp event.
EDIT: I should mention the ListView is in Details mode.
Avoid HitTest() and use ListView's native function GetItemAt(). An example from MSDN looks like this:
private void ListView1_MouseDown(object sender, MouseEventArgs e)
{
ListViewItem selection = ListView1.GetItemAt(e.X, e.Y);
// If the user selects an item in the ListView, display
// the image in the PictureBox.
if (selection != null)
{
PictureBox1.Image = System.Drawing.Image.FromFile(
selection.SubItems[1].Text);
}
}
Here is the problem: I have a Windows Forms application that I'm developing, and in one segment I'm using a ListView control.
What I'm trying can be simply stated as: on event ListViewItemSelectionChange show a MessageBox for user to confirm the change, if not confirmed change to let's say the first item. This change to the first item would again fire ListViewItemSelecionChange, so I unregister and re-register the event handler method, so everything should be good, right?
What actually happens is that the handler method is called twice (actually ListView should fire two events on Selection change, one for deselect, other for newly selected item, but I have an e.IsSelected statement at the beginning to catch only selected items, so actually you could say that there are four events fired).
The problem is, if I generated the first event with mouse click on ListView item, and I've unsubscribed before programatically changing to the first item, what generates the second event firing? Is it some focus change because of the MessageBox call? Is there any way to prevent the second event to fire?
I have a simple example solution here, it can't be more simlified (25 SLOC), so if you can, please take a look. Note that commenting the line "if (ShowMessageBox())" stops the second event from firing, is this some focus change problem?
http://www.filedropper.com/listviewtestwithmsgbox
Edit: the relevant code:
private void listViewWithSelection1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
// listview actually generates two ItemSelectionChanged events,
// one for deselect of a item, and another event for a newly selected item (which we want here).
if (e.IsSelected)
{
if (ShowMessageBox())
Button1_Click(null, EventArgs.Empty);
label1.Text += "item selected ";
}
}
private bool ShowMessageBox()
{
return MessageBox.Show("Change to first item instead?", "test", MessageBoxButtons.YesNo) == DialogResult.Yes;
}
private void Button1_Click(object sender, EventArgs e)
{
// change ti first ListView item
listView1.ItemSelectionChanged -= listViewWithSelection1_ItemSelectionChanged;
listView1.Items[0].Selected = true;
listView1.ItemSelectionChanged += listViewWithSelection1_ItemSelectionChanged;
}
Hmm, can you describe how the selection is being changed to begin with? If it's by the user clicking to select an item, perhaps catch the Click or DoubleClick event rather than the ItemSelectionChanged event? I have this snippet I'm using on a program currently. If the user double clicks the list box (listView, in your case), do something with the selected item.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private bool ShowMessageBox()
{
return MessageBox.Show("Change to first item instead?", "test", MessageBoxButtons.YesNo) == DialogResult.Yes;
}
private void listView1_Click(object sender, EventArgs e)
{
if (ShowMessageBox())
listView1.TopItem.Selected = true;
label1.Text += "item selected ";
}
}
Edited to include relevant code.
One way to do this is to have a flag which says should the on change code run.
In your ListViewItemSelecionChange code you check the value of the flag and run code accordingly.
In short
When I type a character in a ComboBox, press Alt+Down followed by Enter or Tab, the SelectedIndexChanged event doesn't fire, even though the SelectedIndex value does change! Why doesn't the event fire?
Update
The same error occurs if you type a character, press Alt+Down and then type Esc. You would expect the Esc to cancel the change. However, the SelectedIndex does change, and the SelectedIndexChanged event doesn't fire.
What should happen if you just type Alt+Down, use the arrow keys to browse to an entry, and then type Esc? Should the selected index be set back to its original value?
Not so short
I have a WinForm application with a ComboBox on it. The ComboBox' SelectedIndexChanged event is wired up to a event handler that shows the SelectedItem in a Label control. The ComboBox' Items collection has three values: "One", "Two", and "Three".
When I select an item with the mouse, the event fires.
When I scroll the mouse, the event fires.
When I use Alt+Down to expand the combobox and walk through the items with Up and Down, the event fires.
But... When I type in the first character of a value, then press Alt+Down, followed by Enter or Tab, the value does get selected and is shown in the combobox, but the event doesn't fire.
I've also added a button that shows the SelectedIndex. It shows the SelectedIndex has changed. So even though the SelectedIndex does change, the SelectedIndexChanged event does not fire!
If I just type in a valid value like One the event doesn't fire either, but in that case a click on the button reveals the SelectedIndex indeed hasn't changed. So in that case the behavior is normal.
To reproduce, create a Form and add a ComboBox, a Label and a Button. Place the following code in the Form1.cs:
using System;
using System.Windows.Forms;
namespace ComboBoxSelectedIndexChanged
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
comboBox1.Items.AddRange(new object[] {
"One",
"Two",
"Three"
});
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
label1.Text = "Selected index: " + comboBox1.SelectedIndex;
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Selected item: " + comboBox1.SelectedItem +
"\nSelected index: " + comboBox1.SelectedIndex);
}
}
}
I've tried several google searches in order to find a definitive answer on this but didn't find one before. Just now I found a thread that actually refers to a Microsoft knowledge base article about the problem. Article KB948869 describes the problem.
The knowledge base article suggest to create your own combobox and override the ProcessDialogKey method.
using System.Windows.Forms;
public class MyComboBox : ComboBox
{
protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == Keys.Tab)
this.DroppedDown = false;
return base.ProcessDialogKey(keyData);
}
}
I've tried it, but unfortunately, it doesn't seem to have any effect. Which is a bit strange. I would expect a workaround described in a knowledge base article to be accurate.
I found another workaround though, which is to use the DropDownClosed event in stead.
private void comboBox1_DropDownClosed(object sender, EventArgs e)
{
label1.Text = "DroDownClosed Selected index: " + comboBox1.SelectedIndex;
}
This does seem to work, but only when using DropDownStyle.DropDown. When you set the DropDownStyle to DropDownList, typing a character does not fire the DropDownClosed (as there is no actual drop down in that case). Only if you actually open up the drop down list and select a value the DropDownClosed event is fired.
So, both options are not really a good answer.
Update
I've even tried overriding property SelectedIndex in MyComboBox, having it call OnSelectedIndexChanged(EventArgs.Empty). After typing a character and pressing Alt+Down, the setter is executed, but it's setting the value to -1, which it already is. After pressing Tab, the setter isn't executed again, although somehow the SelectedIndex value does change. It looks like the ComboBox is directly changing the backing field for SelectedIndex, bypassing the setting. I believe something like this probably also happens in the real ComboBox.
The appropriate DropDown property value here is DropDownList. It doesn't have this problem.
Coming up with a workaround for your specific problem with the DropDown style set to DropDown is quite difficult. It allows the user type arbitrary text and even a perfect match with one of the dropdown items doesn't change the SelectedIndex. You'd have to implement the Validating event and look for a match yourself. The DropDownClosed event would be good for your specific scenario. But really, always use DropDownList if you want perfect matches.
I had the ESC problem on a DropDownList-style combobox. I slightly modified what worked for me to accommodate your needs:
public class MyComboBox : System.Windows.Forms.ComboBox
{
private bool _sendSic;
protected override void OnPreviewKeyDown(System.Windows.Forms.PreviewKeyDownEventArgs e)
{
base.OnPreviewKeyDown(e);
if (DroppedDown)
{
switch(e.KeyCode)
{
case System.Windows.Forms.Keys.Escape:
_sendSic = true;
break;
case System.Windows.Forms.Keys.Tab:
case System.Windows.Forms.Keys.Enter:
if(DropDownStyle == System.Windows.Forms.ComboBoxStyle.DropDown)
_sendSic = true;
break;
}
}
}
protected override void OnDropDownClosed(System.EventArgs e)
{
base.OnDropDownClosed(e);
if(_sendSic)
{
_sendSic = false;
OnSelectedIndexChanged(System.EventArgs.Empty);
}
}
}
What this does is listening to keystrokes that come in while the dropdown is open. If it's ESC, TAB or ENTER for a DropDown-style ComboBox or ESC for a DropDownList-style ComboBox, a SelectedIndexChanged-Event is triggered when the DropDown is closed.
I have never ever used ComboBoxStyle.Simple and don't really know how it does or should work, but since it to the best of my knowledge never displays a DropDown, this should be safe even for that style.
If you don't want to derive from ComboBox to build your own control, you can also apply similar logic to a ComboBox on a form by subscribing to it's PreviewKeyDown and DropDownClosed events.
Correct me if I'm wrong. Here is code I've used.
comboBox1.Items.AddRange(new object[] {
"One",
"Two",
"Three"
});
comboBox1.SelectedIndexChanged+=(sa,ea)=>
{
label1.Text = "Selected index: " + comboBox1.SelectedIndex;
};
comboBox1.TextChanged+= (sa, ea) =>
{
comboBox1.SelectedIndex = comboBox1.FindStringExact(comboBox1.Text);
//OR
//comboBox1.SelectedIndex = comboBox1.Items.IndexOf(comboBox1.Text);
comboBox1.SelectionStart = comboBox1.Text.Length;
};
I ended up deriving my own class from ComboBox:
public class EditableComboBox : ComboBox
{
protected int backupIndex;
protected string backupText;
protected override void OnDropDown(EventArgs e)
{
backupIndex = this.SelectedIndex;
if (backupIndex == -1) backupText = this.Text;
else backupText = null;
base.OnDropDown(e);
}
protected override void OnSelectionChangeCommitted(EventArgs e)
{
backupIndex = -2;
base.OnSelectionChangeCommitted(e);
}
protected override void OnSelectionIndexChanged(EventArgs e)
{
backupIndex = -2;
base.OnSelectionIndexChanged(e);
}
protected override void OnDropDownClosed(EventArgs e)
{
if (backupIndex > -2 && this.SelectedIndex != backupIndex)
{
if (backupIndex > -1)
{
this.SelectedIndex = backupIndex;
}
else
{
string oldText = backupText;
this.SelectedIndex = -1;
this.Text = oldText;
this.SelectAll();
}
}
base.OnDropDownClosed(e);
}
}
I have a list box with some items. Is there anyway I can attach a double click event to each item?
Item 1
Item 2
Item 3
If i was to double click Item 2, a Messagebox saying "Item 2" would pop up
How would i do this?
void listBox1_MouseDoubleClick(object sender, MouseEventArgs e)
{
int index = this.listBox1.IndexFromPoint(e.Location);
if (index != System.Windows.Forms.ListBox.NoMatches)
{
MessageBox.Show(index.ToString());
}
}
This should work...check
WinForms
Add an event handler for the Control.DoubleClick event for your ListBox, and in that event handler open up a MessageBox displaying the selected item.
E.g.:
private void ListBox1_DoubleClick(object sender, EventArgs e)
{
if (ListBox1.SelectedItem != null)
{
MessageBox.Show(ListBox1.SelectedItem.ToString());
}
}
Where ListBox1 is the name of your ListBox.
Note that you would assign the event handler like this:
ListBox1.DoubleClick += new EventHandler(ListBox1_DoubleClick);
WPF
Pretty much the same as above, but you'd use the MouseDoubleClick event instead:
ListBox1.MouseDoubleClick += new RoutedEventHandler(ListBox1_MouseDoubleClick);
And the event handler:
private void ListBox1_MouseDoubleClick(object sender, RoutedEventArgs e)
{
if (ListBox1.SelectedItem != null)
{
MessageBox.Show(ListBox1.SelectedItem.ToString());
}
}
Edit: Sisya's answer checks to see if the double-click occurred over an item, which would need to be incorporated into this code to fix the issue mentioned in the comments (MessageBox shown if ListBox is double-clicked while an item is selected, but not clicked over an item).
Hope this helps!
I know this question is quite old, but I was looking for a solution to this problem too. The accepted solution is for WinForms not WPF which I think many who come here are looking for.
For anyone looking for a WPF solution, here is a great approach (via Oskar's answer here):
private void myListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
DependencyObject obj = (DependencyObject)e.OriginalSource;
while (obj != null && obj != myListBox)
{
if (obj.GetType() == typeof(ListBoxItem))
{
// Do something
break;
}
obj = VisualTreeHelper.GetParent(obj);
}
}
Basically, you walk up the VisualTree until you've either found a parent item that is a ListBoxItem, or you ascend up to the actual ListBox (and therefore did not click a ListBoxItem).
For Winforms
private void listBox1_DoubleClick(object sender, MouseEventArgs e)
{
int index = this.listBox1.IndexFromPoint(e.Location);
if (index != System.Windows.Forms.ListBox.NoMatches)
{
MessageBox.Show(listBox1.SelectedItem.ToString());
}
}
and
public Form()
{
InitializeComponent();
listBox1.MouseDoubleClick += new MouseEventHandler(listBox1_DoubleClick);
}
that should also, prevent for the event firing if you select an item then click on a blank area.
It depends whether you ListBox object of the System.Windows.Forms.ListBox class, which does have the ListBox.IndexFromPoint() method. But if the ListBox object is from the System.Windows.Control.Listbox class, the answer from #dark-knight (marked as correct answer) does not work.
Im running Win 10 (1903) and current versions of the .NET framework (4.8). This issue should not be version dependant though, only whether your Application is using WPF or Windows Form for the UI.
See also: WPF vs Windows Form
This is very old post but if anyone ran into similar problem and need quick answer:
To capture if a ListBox item is clicked use MouseDown event.
To capture if an item is clicked rather than empty space in list box check if listBox1.IndexFromPoint(new Point(e.X,e.Y))>=0
To capture doubleclick event check if e.Clicks == 2
The post is old but there is a simple solution for those who need it
private void listBox1_DoubleClick(object sender, EventArgs e)
{
if (listBox1.SelectedIndex > -1)
{
MessageBox.Show(listBox1.Items[listBox1.SelectedIndex].ToString());
}
}