First: I know that there are literally thousands of answers like: "Add a handler to Keyboard.KeyDownEvent and have fun!". But in my situation this does not work.
I have a custom control CustomControl which derives from Canvas but has no Children. Instead it draws its "children" directly to the DrawingContext in the OnRender. My Control is HitTestVisible, it is tab stop but is not focusable. It is often reused and sometimes in a ScrollViewer.
This CustomControl has a custom implementation for selecting something like text, and should copy that selected text to the ClipBoard on Ctrl+C.
To do this, I added a handler in the constructor:
public CustomControl()
{
//// ... other stuff
AddHandler(Keyboard.KeyDownEvent, (KeyEventHandler)CopyMarkedNucleotidesToClipboard);
}
And here is the Problem: When my control is inside a ScrollViewer, and I hit Ctrl+C, the KeyDownEvent is raised on the ScrollViewer and bubbles up to the window, and therefore never reaches my Control.
What can I do inside my CustomControl to capture every Ctrl+C in the window where it resides?
PS: I already set IsTabStop="False" and Focusable="False". But then the next sibling of the ScrollViewer would raise the event which would still bubble up to the window. And I don't want to go through all controls which are higher in the visual tree and set IsTabStop="False" and Focusable="False" which would be wrong...
I already found this article http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx but I think, that there must be a more wpf-like way!
The suggestion of Sinatr was correct! Thanks!
The solution is to find the parent window in the load and subsribe to his KeyDownEvent.
public CustonControl()
{
Loaded += HookToCtrlC;
}
private void HookToCtrlC(object sender, EventArgs e)
{
var parentWindow = Window.GetWindow(this);
parentWindow.KeyDown += CopySelectedTextToClipboard;
}
private void CopyMarkedNucleotidesToClipboard(object sender, KeyEventArgs e)
{
Clipboard.SetText("Hello World!");
}
Related
do you know a "simple" way to detect when a user control loses focus?
I am creating a console output inspired by Visual Studio.
Visual Studio console output collapses when I click anywhere.
Obviously in my application if I click on a Panel (which has no focus) I do not receive any events.
I had thought about using the mouse_down event on the application, but I would have to do an evaluation with every click!
Do you know any way to know when you click out of your control?
UserControl has a LostFocus event. In the code behind of your UserControl you can simply add an event handler.
public partial class MainView : UserControl
{
public MainView()
{
InitializeComponent();
LostFocus += OnFocusLost;
}
private void OnFocusLost(object sender, RoutedEventArgs e)
{
// Do something here.
}
}
If you are referencing it from another class then you can create an event handler on that class and subscribe to the event from the UserControl.
UserControl control = new UserControl();
control.LostFocus += OnFocusLost;
With that being said, if you have a TextBox or some other control inside of the UserControl then the UserControl will lose focus every time a TextBox gets the focus so you will most likely need to write a method to determine if any of the elements inside the UserControl have focus.
I have a visual tree that looks like this:
A Border, containing a ScrollViewer, which contains a TexBlock.
The ScrollViewer takes up 100% of the space of the Border, and the TextBlock may or may not take up 100% of the space of the ScrollViewer depending on how the user configures it.
I want to capture a MouseDown event for when a user clicks anywhere in the Border. When I register a MouseDown event for either the Border, or the ScrollViewer, the callback does not get invoked. When I register a MouseDown event to the TextBlock, the callback does get invoked, but of course only in the clickable area of the TextBlock and not the full area of the Border.
One idea that I have is to create some sort of top level element that will go over the whole control, set it's visibility to hidden, then get the MouseDown from that.
Any suggestions? If something is not clear about this question let me know and I will fix it.
Showing Example code per request
// Need to know when a user clicks on anything inside of the border, but the
// because there are items above it, the mouse event doesn't get invoked.
Border border = new Border();
ScrollViewer viewer = new ScrollViewer();
TextBlock textBlock = new TextBlock();
border.Content = viewer;
viewer.Child = textBlock;
You can register PreviewMouseDown event on the Border. It will also fire if a containing element is clicked.
private void Border_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var clickedElement = e.OriginalSource;
}
One more approach (although not that clean IMO) but it could be a useful if you require some custom behavior... maybe :)
We create custom scroll viewer and then use it instead of the standard one, our custom control will just propagate it's mouse down event to it's parent (this example is oversimplified so it's not suitable for use in production in the current state).
public class CustomScrollViewer : ScrollViewer
{
protected override void OnMouseDown(MouseButtonEventArgs e)
{
((e.Source as FrameworkElement).Parent as UIElement).RaiseEvent(e);
}
}
Some info for people like me that weren't familiar with the PreviewMouseDown approach - it's using Routing strategy called Tunneling (from top to bottom) which is the opposite of Bubbling (from bottom to top)
This must have been asked before, but I am fairly new and don't quite know how to express myself...
1) I have a UserControl that basically acts as a toolbar. I re-use the toolbar in each window, hence the need for a uc.
2) The toolbar is filled with buttons
3) the usercontrol doesn't act on the button (no code), but it should pass the event back to the parent window so the code in the parent window fires up.
How can I do this? Is this a routed event? any sample code in vb.net would be appreciated!
On you user control, you need events that you can fire when the buttons are clicked. Then in your form, you handle the events just like you do for every other control. IE:
public event Button1_ClickedEventHandler Button1_Clicked;
public delegate void Button1_ClickedEventHandler(object sender);
private void Button1_Click(object sender, EventArgs e)
{
if (Button1_Clicked != null) {
Button1_Clicked(this);
}
}
You can call the event whatever you want and pass whatever you want. Here you will notice I am NOT sending the button but THIS, which in this case should be the User Control.
I developping one win-form application which having one custom control with one label and text box, and placed the custom control in one panel with docksytle as fill,
there is mouse click event for panel and custom control both, but when i click only custom control mouse click event is firing not the panel click event,
so anyone please let me know how to call the panel mouse click event.
Are you sure that you really need to invoke click of parent control? In general it would be, in my opinion, a code smell if you will do something like that - especially when it requires some strange constructions.
If you need to react in a same way when clicking on panel and on any child control inside the panel, it should be enough just to call the same method from two event handlers (that is from event handler of parent panel and event handler of child control. If you need, for example, mouse pointer location inside parent panel, you can easily calculate the position of mouse pointer using, for example, PointToScreen() and PointToClient() methods.
This is not a general solution, but maybe it's what you're looking for:
private void CustomControl_MouseClick(object sender, MouseEventArgs e)
{
panel_MouseClick(sender, e);
}
private void panel_MouseClick(object sender, MouseEventArgs e)
{
}
Create Click Event for each control in panel and invoke the parent :
private void This_Click(object sender, EventArgs e)
{
this.InvokeOnClick(this, null);
}
How to capture mouse wheel on panel in C#?
I'm using WinForms
EDIT:
I try to do it on PictureBox now.
My code:
this.pictureBox1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseClick);
this.pictureBox1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseClick);
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
MessageBox.Show("Click");
}
Clicking works. Wheelling doesn't.
Why?
If you can't see the "MouseWheel" event on a component, then you need to create it manually. Also, we need to focus that component, otherwise the "MouseWheel" event will not work for that component. I will show you how to create a "MouseWheel" event for "pictureBox1" and how it works.
INSIDE THE CONSTRUCTOR, create a mousewheel event on that component.
InitializeComponent();
this.pictureBox1.MouseWheel += pictureBox1_MouseWheel;
CREATE THE FUNCTION manually. According to my example, call it "pictureBox1_MouseWheel"
private void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
//you can do anything here
}
CREATE a MouseHover event on that component (Go to properties in PicureBox1, select event, locate "MouseHover" and double-click the "MouseHover" event).
CALL "Focus()"; method inside that MouseHover event.
pictureBox1.Focus();
Now run the program.
Windows sends the WM_MOUSEWHEEL message to the control that has the focus. That won't be Panel, it is not a control that can get the focus. As soon as you put a control on the panel, say a button, then the button gets the focus and the message.
The button however has no use for the message, it's got nothing to scroll. Windows notices this and sends the message to the parent. That's the panel, now it will scroll.
You'll find code for a custom panel that can get the focus in this answer.
UPDATE: note that this behavior has changed in Windows 10. The new "Scroll inactive windows when I hover over them" option is turned on by default. The makes the mouse wheel behavior more consistent with the way it works in a browser or, say, an Office program. In this specific case the picturebox now will get the event. Watch out for this.
To wire it up manually...
this.panel1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseWheel);
private void panel1_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
{
///process mouse event
}
Easier method is in visual studio click on panel, goto properties viewpanel, select events, locate and double click the "mousewheel" event.
In Winforms, this is achieved using the Control.MouseWheel event
Getting mousewheel events is tricky. The easiest way is using
this.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseWheel);
instead of
this.panel1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseWheel);
This way the form gets the event instead of control. This way is easy but has one problem: you can use only one mousewheel event in your form.
If you have more than one control to get mousewheel event the best way is This answer by "Shehan Silva - weltZ"