I have a WinForm app that has a ListView in it, with a DoubleClick event handler assigned to it.
Theoretically , only items are "clickable" , so it should be impossible to enter the event handler with no selected items, and so it is for 99% of cases.
However, every once in a while, I catch an exception of InvalidAgrument as my handler try to access list_view.SelectedItems[0], and there I see that it does actually empty.
When I try to reproduce, it takes an aggressive clicking session to do it. But it's done, I can sometimes see the cursor in the middle of a valid entity , which makes me suspect it might be some racing condition.
This can certainly go wrong, a double-click doesn't guarantee that an item is selected. It may also de-select an item, your code will crash. Short from adding the test to check that SelectedItems isn't empty, the possible better mouse trap is to find the exact item that was double-clicked. Use the MouseDoubleClick event instead so you get the mouse position, then use the ListView.HitTest() method. Like this:
private void listView1_MouseDoubleClick(object sender, MouseEventArgs e) {
var item = ((ListView)sender).HitTest(e.Location);
if (item != null) {
// etc..
}
}
Related
In my UWP app I am using DynamicOverflowItemsChanging event of a CommandBar my usecase is not very complex, I tried to solve another problem I had by using this event, see the answer of this post.
So as shown in the answer in the link I provided above, I am trying to invoke this event and sync the visibility of MoreButton now the problem is this event is fired when the secondary overflow menu of command bar doesn't have the item removed or added yet, so they still show the items which they had before the event was fired, but when this event is completed then transfer of items is also completed.
So in short I want to raise another event somehow or get notified when the DynamicOverflowItemsChanging completes its execution, so I can have the correct data and set the visibility of more button correctly. or in other words I want to implement a behaviour of DynamicOverflowItemsChanged event, which unfortunately doesn't exist in the api, so I am looking for an alternative way.
I know I can invoke another event right at the end (just before DynamicOverflowItemsChanging event is finished), but that way I still don't get the correct values, correct values only become available when this event finishes its execution. Like we can do with Task.Then() so when a task completes another can automatically start, so I want something like that here . any suggestions would be appreciated.
protected override void OnApplyTemplate()
{
var barr = (CommandBar)GetTemplateChild("MediaControlsCommandBar");
//the event to control the dynamicoverflow automatically.
barr.DynamicOverflowItemsChanging += (s, e) =>
{
if (_secondarycontrols.Items.Count == 0 && e.Action == CommandBarDynamicOverflowAction.AddingToOverflow)
_moreButton.Visibility = Visibility.Visible;
else if (_secondarycontrols.Items.Count == 1 && e.Action == CommandBarDynamicOverflowAction.RemovingFromOverflow)
_moreButton.Visibility = Visibility.Collapsed;
};//when this event finishes here I want to get notified.
//base implementation
base.OnApplyTemplate();
}
Warm greetings, fellow Microsoft Student Partner :-) .
The quick and dirty hack would be to add a Task.Delay to the end of the handler and hope the items will have changed until the delay is over. This is of course really ugly and error prone. I would suggest a different solution.
You already have access to the _secondarycontrols which is a CommandBarOverflowPresenter. This is very useful, because Items property has a VectorChanged event which is fired whenever an item is added, removed or changed. This means, you can actually use this event and move your logic inside it instead of using the DynamicOverflowItemsChanging event. You can then directly check for the number of items which will be accurate.
I am trying to use the MouseClick event from the properties of a listView to handle left and right mouse clicks.
Unfortunately the event never seems to fire. (Double clicked on the event to create a property, entered a bit of simple code and placed a breakpoint on the first line). The same is true of several other events listed in the properties (ItemSelectionChanged seems to work but the other events I have tried don't fire.
Here is the code added:
In form.designer.cs:
this.listView1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.listView1_MouseClick);
In form.cs:
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
Some code
}
That method never gets called when I click on the listview. The listview is inside a tab on top of the stack.
I guess I am probably forgetting something very basic but what?
ListView is a bit unusual, its MouseClick event doesn't fire unless you click an item in the view. Workaround is to use the MouseDown or MouseUp event instead. You typically are much more interested in the ItemSelectionChanged event btw. You probably need its HitTest() method to see exactly what was clicked if you use MouseDown/Up.
When I am having a value item selected in my WPf DropDown Combo Box then navigating using keys Left and right arrow keys result in firing of selected changed event for each item.
How to overcome this problem
The most easy and suitable way I found to overcome this problem is as follows:
rather than using SelectedIndexChanged event I used on DropDownClosed event and all code that is wriiten earlier inside selected index changed moved to this event under a if condition that checks whether a item is selected or not. Like this.
private void OnCmbOperatorsListDropDownClosed(object sender, EventArgs e)
{
if (cmbOperatorsList.SelectedIndex != -1)
InsertText(cmbOperatorsList.SelectedValue.ToString());
//Do whatever u want with selected item
}
So in this way when i navigate through Arrow keys SelectedIndexChagned event will not fired or since i haven't used that event so it will not create any problem.
As per my knowledge this is not possible straight away. I could have implemented this in a kind of "selection simulated" manner.
Handle arrow keys on combobox dropdown in PreviewKeyDown event by setting e.Handled = true. So that usual navigation based selection wont happen.
Inthese handlers based on Keys, change the Background and Foreground of the previous or next item from the drop down list so that it will look as if its selected and highlighted.
Then perform selection of the item which curently has the "simulated selection background - foreground" when dropdown closes. After dropdown closure, revert the background and foreground style.
But thats just my way of doing it.
You can use the PreviewKeyDown event like
private void combo_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key.Equals(Key.Left) || (e.Key.Equals(Key.Right)))
{
((ComboBox)sender).SelectionChanged -= combo_SelectionChanged;
}
}
and if u want to attach that event you can add this PreviewMouseDown event.
This is what i tried and may not be a proper method of doing such cases
If the user click on a item in the listbox, the listboxItems_SelectedIndexChanged is called. But, even if the user miss an item and randomly clicks inside the listbox (not on items) the listboxItems_SelectedIndexChanged is still called.
How can I change this? I only want action on item click.
Note: removing the ability to navigate the application with keyboard is not a option.
I guess that in some cases you don't have enough list items in your control, therefore you have some space that you can click on and then SelectedIndexChanged is fired.
I guess you cannot dynamically resize the control to always fit the number of list items or else you wouldn't be asking this question.
Now, what should happen when the user click (selects) the same list item? Should some logic happen even though the selected index is the same (so when it was clicked the first time the same logic happend)?
If you require that selecting the same index more than once should be ignored then you could use the following hack:
Keep a variable at the form scope (the form containing the listbox control) and each time the selection index changes set that variable. Then use it later to check if the same selection has been made to ignore handling the event. Here is an example:
private int _currSelIdx = -1; // Default value for the selected index when no selection
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listBox1.SelectedIndex == _currSelIdx)
return;
Console.WriteLine(listBox1.SelectedIndex);
_currSelIdx = listBox1.SelectedIndex;
}
It ain't pretty, but hey...whatever works!
Maybe SelectedIndexChanged is not the right place to put your logic, since it is triggered even when you change the selection with the keyboard.
I would use MouseClick instead, checking if the click occurred over the selected item, i.e. something like this:
private void listBox1_MouseClick(object sender, MouseEventArgs e)
{
if (listBox1.SelectedIndex < 0 || !listBox1.GetItemRectangle(listBox1.SelectedIndex).Contains(e.Location))
MessageBox.Show("no click");
else
MessageBox.Show("click on item " + listBox1.SelectedIndex.ToString());
}
This link may help, instead of double click, implement the same for single click
i want to detect an item double click in a winforms listbox control. [how to handle click on blank area?]
I'm writing a custom control and I'd like the control to switch from an editing state to it's normal state when a user clicks off of the control. I'm handling the LostFocus event and that helps when a user tabs away or if they click on to another control that's Focusable. But if they don't click on something Focusable, it won't switch out of it's editing state. So I have two solutions in mind:
Walk up the tree to the top most element when it goes in to an editing state and add a handler for MouseDownEvent (and handle "handled" events). In the handler I'd kick the control out of it's editing state and remove the handler from the top most element. This seems like a bit of a hack, but it would probably work well.
Example code:
private void RegisterTopMostParentMouseClickEvent()
{
_topMostParent = this.FindLastVisualAncestor<FrameworkElement>();
if ( _topMostParent == null )
return;
_topMostParent.AddHandler( Mouse.MouseDownEvent, new MouseButtonEventHandler( CustomControlMouseDownEvent ), true );
}
private void UnRegisterTopMostParentMouseClickEvent()
{
if ( _topMostParent == null )
return;
_topMostParent.RemoveHandler( Mouse.MouseDownEvent, new MouseButtonEventHandler( CustomControlMouseDownEvent ) );
_topMostParent = null;
}
Use Mouse.PreviewMouseDownOutsideCapturedElement and add a handler to my control. In the handler I'd kick the control out of it's editing state. But I don't seem to get the event to fire. When does the Mouse.PreviewMouseDownOutsideCapturedElement get kicked off?
Example code:
AddHandler( Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler( EditableTextBlockPreviewMouseDownOutsideCapturedElementEvent ), true );
Just to clarify the answer provided about mouse focus - it was useful but I had to do some further digging + mucking about to get something that actually worked:
I was trying to implement something like a combobox and needed similar behaviour - to get the drop down to disapear when clicking on something else, without the control having knowledge of what something else was.
I had the following event for a drop down button:
private void ClickButton(object sender, RoutedEventArgs routedEventArgs)
{
//do stuff (eg activate drop down)
Mouse.Capture(this, CaptureMode.SubTree);
AddHandler();
}
The CaptureMode.SubTree means you only get events that are outside the control and any mouse activity in the control is passed through to things as normal. You dont have the option to provide this Enum in UIElement's CaptureMouse, this means you will get calls to HandleClickOutsideOfControl INSTEAD of calls to any child controls or other handlers within the control. This is the case even if you dont subscribe to the events they are using - full Mouse capture is a bit too much!
private void AddHandler()
{
AddHandler(Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(HandleClickOutsideOfControl), true);
}
You would also need to hang on to + remove the handler at the appropriate points but I've left that out here for the sake of clarity/brevity.
Finally in the handler you need to release the capture again.
private void HandleClickOutsideOfControl(object sender, MouseButtonEventArgs e)
{
//do stuff (eg close drop down)
ReleaseMouseCapture();
}
Capture the mouse.
When an object captures the mouse, all mouse related events are treated as if the object with mouse capture perform the event, even if the mouse pointer is over another object.
I usually get the parent window and add a preview handler, even if already handled. Sometimes when MouseCapture is not enough, this technique comes handy:
Window.GetWindow(this).AddHandler
(
UIElement.MouseDownEvent,
(MouseButtonEventHandler)TextBox_PreviewMouseDown,
true
);
I would approach this a different way - have the form that contains the control remove focus from the control when a user clicks on another part of the form.
Having the control actually loose focus is far cleaner than attempting to have the control "simulate" focus being lost in certain situations, when in fact it hasn't. Bear in mind that unless the control has really lost focus it will still accept things like keyboard input.