I have written a custom Control, based on other code, which includes a call to SetStyle(ControlStyles.Selectable, false);
This prevents the following code from being called (which works if I remove the above line and click on the Control first to 'focus' it):
protected override void OnMouseWheel(MouseEventArgs e)
{
ScrollBar.Value = Math.Min(ScrollBar.Value - e.Delta, ScrollBar.Maximum - ScrollBar.LargeChange);
LayoutChanged();
}
My control does not need focus so Selectable=false is correct.
What do I need to do so that I can run this code whenever the mouse is over my control and the MouseWheel is moved regardless of where the focus is?
Thanks for the comments. A additional info:-
I am running Windows 7 but any solution must work on any OS.
The Control is inherited via Control/ControlBase/BaseControl/BaseStyleControl where the latter 3 are DevExpress controls.
The real focus should not be changed just because the cursor hovers over my control
There are no child controls other than a scrollbar (an inherited DevExpress one) which is normally invisible and shows itself when the mouse hovers over where it would be when visible.
The control does nothing but draw itself (imagine a number of bookshelves one under the other with book covers drawn on them - the number of shelves increases to accommodate all the books and the scrollbar allows all to be scrolled into view.
Since this is a use-anywhere Control, getting MouseWheel events from a Parent that knows about its requirements isn't a good idea.
Maybe there is a way of registering for a first-look (is that a MessagePreview?) to pinch MouseWheel events before normal processing happens.
Related
I am currently using MSCharts in one of my windows forms. One of the quirky things about MSCharts is that you cannot trigger a MouseWheel event in the chart unless the chart has focus. To combat this, most people are saying that one should add a MouseEnter event to the chart and then Focus() the chart to allow one's MouseWheel events to fire (see here: Enabling mouse wheel zooming in a Microsoft Chart Control).
Let's say that I pull up a completely different window (call it Window A) that just so happens to be partially in front of my chart (call it window is Window B). If I accidentally move the mouse over the chart in Window B for even 10 milliseconds, Window B will take focus and Window A will be placed behind it, which is incredibly frustrating.
I've explored different options.
Setting Window B's TopMost property to true. The problem with this is that the user has to either close the window or minimize it to hide it. If there are a lot of windows up, it seems to be just as frustrating as the initial issue.
Instead of giving the MouseEnter event the ability to Focus(), let the MouseClick or MouseHover event to Focus(). The problem with MouseClick is that the user will always have to click on the chart first to zoom, which isn't bad, but can be annoying. MouseHover is okay, but the time that the event considers to be a hover is really short.
In the end, I want it so that I can put my mouse over the chart and scroll in without having to do anything (mouse clicks, or anything else). In addition to this, I don't want the form that contains the chart to jack the focus back to it if I accidentally move my mouse over it for just a second.
EDIT:
It seems that according to #TaW, the chart doesn't need focus to trigger MouseWheel events in Window 10. This is not the case in Windows 7, unfortunately.
This may seem slightly hacky, but it works in this case:
This works through the use of the FindForm method. I never knew it was a thing until now. You can read more about it here: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.findform(v=vs.110).aspx
myChart.MouseEnter+= delegate(object sender, EventArgs args) //add a mouse enter event to your chart
{
if (!chart.Focused) //if chart isn't focused
{
if (chart.FindForm().ContainsFocus) //check if the form the chart is in contains focus
chart.Focus(); //if the chart isn't focused, but the form is focused, focus on the chart
}
};
This will still give the chart focus when you move your mouse into it and it will not allow the form that contains the chart to jack the focus from the form you're in.
Screen of my user control
I have an user control (black rectangle) that contains some controls.
When the mouse is hover the user control, I want to show the two images in the red rectangle.
At the moment I have implemented the mouseEnter (show the images) and mouseLeave (hide the images) events from the user control.
The problem is that when the mouse is hover another control (a label for instance), the mouseLeave event from the user control is fired and the images disappear.
I could implement the mouse enter / mouse leave events for all the controls but it seems a waste of time, lots of code deduplication and a poor usage of memory.
I also thought about disable events for the other controls, but not sure how to do it and it seems to be more of a hack than anything. Il tried to disable the controls but the color changes and again, a dirty hack.
Maybe I can prevent the user control mouseLeave event to be fired if the mouse is hover one of its control ?
I'm sure there is a proper way to do it, but cannot figure it out.
If anyone thinks about a better title, I will change it.
Thank you for your help,
Regards
Thank you very much both of you.
You pointed me to the right direction.
I ended up to simply register the ControlUser.MouseEnter event to all controls by doing :
foreach (Control control in Controls)
{
control.MouseEnter += new EventHandler(this.MyUserControl_MouseEnter);
}
Simple and efficient, this is perfect.
I have a custom ZoomBox control based on ScrollableControl which controls its scrollbars through AutoscrollMinSize property.
I would like to be able to handle WM_MOUSEWHEEL events to adjust control's Zoom.
I made necessary steps to make sure that the control receives the mouse events even when it is not in focus, by filtering them on the parent form.
It seems though that the events only reach the OnMouseWheel method if the control does not have its scrollbars active. If it does, it appears that the mousewheel events are being redirected to the scrollbars which handle them (by scrolling).
I would like the scrollbars to be there but only be controlled in a "traditional way", i.e. by dragging the slider or clicking on arrows etc. and handle the wheel myself. Is it possible to achieve that?
Inherit from the ScrollableControl in question and override OnMouseWheel(). In that method don't call base.OnMouseWheel().
As far as I can tell there's no other way to stop ScrollableControl from scrolling if the scroll bars are present.
A side effect is that you will no longer get MouseWheel events. Fixing that is another question. Conceptually you want to call base.base.OnMouseWheel().
I just wasted my entire evening on something which I thought would be very simple but it seems WPF and Google are letting me down completely.
I need a grid, 6x6 of which I fill every row and column with a custom control. I want to be able to navigate through this grid via the keyboard (I can get those events, no problem) but I cannot seem to find how I can always have the selected grid row/column in the center of my window.
I found some carousel alike implementations, but most of them only work in a single direction and I want two way navigation, yet none seem to support this nor can I extend them to do this.
I essentially want to create a PSP alike grid navigation.
One easy way is to do this:
Create a scrollable form.
Add a 6x6 grid of child controls.
In the GotFocus (or similar) event for all the controls, set the parent form scroll offset to an appropriate position to centre the child.
This is pretty straight-forward thing to implement, with a little bit of maths to work out how to centre the x,y position of a control by setting the scroll offsets (it can be tricky/confusing, but as long as you understand the coordinate systems used for scrolling, not too bad)
Or, another approach that avoids scrolling via the windows APIs and using custom controls:
Create a form
Override OnPaint to draw your grid of 6x6 "controls" as simple graphical shapes or bitmap images centred on the selected "control".
Handle keyboard (KeyDown/Up) and mouse handling (MouseDown/Up) events to make the 36 areas of the graphic respond to user inputs in the way you desire. You'll have to track the selected item and force the window to redraw its graphics to show the new state. Enable double buffering to stop it flickering.
The first approach gives you a lot of windows-based handling for free (tabbing between controls, remembering where the input focus is, and directing events to separate classes for each "control", for example). The second approach strips away all this "help" but gives you complete control over everything, which can often help avoid unintended behaviours (e.g. it won't move the input focus when the user presses Tab unless you specifically write the code to make it do that).
I have a WPF ListView which currently scrolls everytime I click on an item which is only partially visible. How can I keep the control from scrolling that item into view (instead simply selecting the partially visible one)? This behavior is very annoying when doing a drag from this control.
Thanks.
Added: I am looking for a solution to keep the control itself from scrolling when contents are clicked that the control believes are not fully visible. Often this is by a few pixels and the scroll is not necessary.
The items scroll into view because the default behavior on list item click is to call BringIntoView(). You can add an event handler for the RequestBringIntoView event and catch it before it bubbles up from the ListViewItems to the ScrollViewer. In your handler, check the bounds of the sender against the visible region and if you decide that you don't need to scroll, set the event's Handled flag to true.
Since I'm currently on the road, I cannot try this, but have you tried playing around with CanContentScroll, and/or wrapping the scrollable content into a Panel, as suggested by the ScrollViewer Overview on MSDN?
In the worst case, you might want to replace the ListView's ItemsPanel by a hacked ScrollViewer with a "fuzz" factor, e.g. by capturing the RequestBringIntoView event.
have you tried this approach?
just as an addon, I don't know how much you know about the subject, but here is a good place to read more about it.
added:
I just found that you can prevent the mouse wheel of scrolling as well.