I'm writing some code to detect toggling of selections in a WindForms ListBox with MultiSelect turned on. Since SelectedIndexChanged only lets me see what is selected after the click, I was looking for a way to detect what was selected before the ListBox was clicked. I implemented the MouseDown event and I can get exactly what I want, but an unfortunate side effect is that I have killed the SelectedIndexChanged event. It will not fire.
Is this known behavior? Are there any thoughts about getting to the selection list before the click?
Thanks.
Edited to include code snippets as requested.
Designer generated events:
this.lbPhysicianClinic.SelectedIndexChanged += new System.EventHandler( this.lbPhysicianClinic_SelectedIndexChanged );
this.lbPhysicianClinic.MouseDown += new System.Windows.Forms.MouseEventHandler( this.lbPhysicianClinic_MouseDown );
Code snippet showing MouseDown event:
private void lbPhysicianClinic_MouseDown( object sender, MouseEventArgs e )
{
List<Clinic_List_ByPhysicianResult> Selected = this.PhysicianGetSelectedClinics( this.lbPhysicianClinic.SelectedIndices );
}
Code snippet showing SelectedIndexChanged event:
private void lbPhysicianClinic_SelectedIndexChanged( object sender, EventArgs e )
{
try
{
if ( this.FormInitComplete && this.RefreshUIComplete )
{
List<Clinic_List_ByPhysicianResult> Selected = this.PhysicianGetSelectedClinics( this.lbPhysicianClinic.SelectedIndices );
Clinic_List_ByPhysicianResult DroppedClinic = new Clinic_List_ByPhysicianResult();
I set a breakpoint in each event and if the MouseDown event is there, the SelectedIndexChanged event never fires. It only fires when the MouseDown event is gone.
Hopefully this clarifies things.
The ListBox changes its selection before it raises the MouseDown or SelectedIndexChanged events.
What you need to do is capture the underlying Win32 message and raise an event yourself. You can subclass ListBox to do this.
class MyListBox : ListBox
{
private const int WM_LBUTTONDOWN = 0x201;
public event EventHandler PreSelect;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_LBUTTONDOWN:
OnPreSelect();
break;
}
base.WndProc(ref m);
}
protected void OnPreSelect()
{
if(null!=PreSelect)
PreSelect(this, new EventArgs());
}
}
You can use the MyListBox class, and add a handler for the PreSelect event like so:
this.lbPhysicianClinic.PreSelect +=
new EventHandler(this.lbPhysicianClinic_PreSelect);
Inside the event handler you can access the selected indices before the listbox has changed them.
Related
I have a WPF Datagrid that is bound to a model.
Inside the model I have a property defined as
public String status
{
get
{
return m_status;
}
set
{
m_status = value;
OnPropertyChanged("status");
}
}
This property informs the grid of changes via OnPropertyChanged.
I also handle the SelectionChanged event to trigger different activities.
SelectionChanged="gridSongs_SelectionChanged"
private void gridSongs_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Console.WriteLine("gridSongs_SelectionChanged " + sender.ToString());
}
During testing this I have noticed that every time I change the property "status" in code the grid updates automatically (which is what I want) but also fires the SelectionChanged Event as well.
Is there any way I can stop the event from firing when I change the model from code but let it go through when user clicks an item in the grid ?
Maybe I could use a different event for the manual selection of items in the grid ?
thanks a lot in advance.
Is there any way I can stop the event from firing when I change the model from code but let it go through when user clicks an item in the grid?
No, but there is a simple workaround. Add a private bool isLocal variable and set it to true before you make any changes and back to false afterwards:
isLocal = true;
status = "Some Value";
isLocal = false;
Then, in your SelectionChanged handler, check this variable and only react if it is false:
private void gridSongs_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!isLocal ) Console.WriteLine("gridSongs_SelectionChanged " + sender.ToString());
}
I created a WPF UserControl, that handles all GotFocus/LostFocus events of its child controls. I call the OnGotFocus/OnLostFocus of the UserControl, but the IsFocused property of the UserControl will not set:
void MyUserControl_Initialized(object sender, EventArgs e)
{
foreach (UIElement control in (Content as Panel).Children)
{
control.LostFocus += control_LostFocus;
control.GotFocus += control_GotFocus;
}
}
void control_GotFocus(object sender, RoutedEventArgs e)
{
if (!IsFocused)
{
e.Handled = false;
OnGotFocus(e);
}
}
void control_LostFocus(object sender, RoutedEventArgs e)
{
bool hasAnythingTheFocus = false;
foreach (UIElement control in (Content as Panel).Children)
{
if (control.IsFocused)
{
hasAnythingTheFocus = true;
}
}
if (!hasAnythingTheFocus)
{
OnLostFocus(e);
}
}
How can I set it?
Instead of the IsFocused you can use IsKeyboardFocusWithin
use the event UIElement.IsKeyboardFocusWithinChanged and it should worked perfectly.
The GotFocus method will be called when the relevant control receives logical focus... from the UIElement.GotFocus Event page on MSDN:
Logical focus differs from keyboard focus if focus is deliberately forced by using a method call but the previous keyboard focus exists in a different scope. In this scenario, keyboard focus remains where it is and the element where a Focus method is called still gets logical focus.
A more precise interpretation of this event is that it is raised when the value of the IsFocused property of an element in the route is changed from false to true.
Because this event uses bubbling routing, the element that receives focus might be a child element instead of the element where the event handler is actually attached. Check the Source in the event data to determine the actual element that gained focus.
it will get focus when the user clicks on the relevant control in the UI and/or when you call control.Focus() in your code. The IsFocused is readonly and cannot be set.
If I type a letter into a TextBox, and its content changes according to my keypress, why does the KeyDown event continue bubbling up? I would have thought this would be 'handled' at this stage.
Since KeyDown event is a bubbling event, that's why its bubbled to its parent in your case Window. If you don't want that to bubbled to your window, you need to mark it as handled in your textBox handler itself like this -
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
Whereas, if you try to hook the event PreviewKeyDown in your textBox, you will see that - Window's PreviewKeyDownEvent gets called first and later that of your textBox. Reason behind that is, it's a tunelling event. For routing strategies, refer to this link - Routing Strategies
EDIT
Morevoer, if you want to check if the KeyDown event comes from textBox, you can check the OriginalSource of your eventArgs -
private void Window_KeyDown(object sender, KeyEventArgs e)
{
// Check to make sure event comes from window and not from textbox.
if(e.OriginalSource is Window)
{
}
}
I'm working in WPF to create a ListView component. The items in the list are based on another user control that reacts to MouseLeftDown events. The List also reacts to SelectionChanged events.
Right now, if I mouse down on any item on the list and move the cursor, the other items I pass along react to the SelectionChanged event (which is expected since the selection is changing as per the Mouse Down event in List view). I need to be able to disable this reaction when its down through a drag-to-scroll behavior, but to keep it active when the user selects an item on the list.
Does anyone have any ideas how this can be achieved?
Thanks everyone,
RK
I believe one of the ways that could help you is to implement your own handlers of MouseUp and MouseDown events of your items to select item on MouseUp instead of MouseDown. You could start from a sample like this:
public class MyListView : ListView
{
protected override DependencyObject GetContainerForItemOverride()
{
return new MyListViewItem();
}
}
public class MyListViewItem : ListViewItem
{
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
return;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
}
}
I have a UserControl in my WPF application.
I want to call a click event and do some things when the user clicked the UserControl.
The problem is- the UserControl doesn't have a click event.
I searched on the web and found that you can use the MouseLeftButtonUp event.
I tried it- but it doesn't respond to my clicks.
You didn't write what you are trying to do but if you need a click event maybe you are writing some kind of button (the Button class is actually "something you can click" with the visual representation in a control template you can replace)
If you need a button with complex content inside - put your user control inside a button
If you need a button that doesn't look like a button write a custom control template for button
If you need a button with extra functionality subclass button, add the extra data/behavior in code and put the display XAML in a style.
I think for your needs PreviewMouseLeftButtonUp(Down) event is more suitable. Then you need to handle ClickCount for counting amount of clicks and then raise your own event, on which other controls will know, that your control is clicked. There are much more methods on handling click event. You should look at this msdn article and this
UPDATE to handle both Click and DoubleClick
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_myCustomUserControl.MouseLeftButtonUp += new MouseButtonEventHandler(_myCustomUserControl_MouseLeftButtonUp);
_myCustomUserControl.MouseDoubleClick += new MouseButtonEventHandler(_myCustomUserControl_MouseDoubleClick);
}
bool _doubleClicked;
void _myCustomUserControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
_textBlock.Text = "Mouse left button clicked twice";
_doubleClicked = true;
e.Handled = true;
}
void _myCustomUserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_doubleClicked)
{
_doubleClicked = false;
return;
}
_textBlock.Text = "Mouse left button clicked once";
e.Handled = true;
}
}
To test this example name your control as _myCustomUserControl and add a TextBlock named _textBlock to your MainWindow.xaml
Why not just use MouseDown?
Put the event in the User Control and simply do this:
private void MyControl_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
MessageBox.Show("Clicked!");
}
}