By Using reflector you can see that WPF UserControl is overriding AdjustBranchSource.
internal override void AdjustBranchSource(RoutedEventArgs e)
{
e.Source = this;
}
My very own problem regards inconsistency caused by that issue.
When an element is based inside a user control or outside. The Source parameter behaves differently. Which surprises me the source should always be the element in target by the RoutedEvent.
The question is why was it implemented like that?
This kinda makes sense. If you treat the UserControl as a black box then you shouldn't know what controls are on it, and thus the source of an event.
If you need to distinguish between different buttons on the UserControl then the UserControl should have it's own events which the buttons trigger. That way from the outside it looks like the right event and the user of the UserControl doesn't need to know which button did which event.
To give an example, on a listbox, do you need to know that the down-scroll button was the button that sent the original event? Or do you just need to know that a scroll-down event was triggered.
The source of a routed event can change throughout the routing of the event. I'm not entirely sure why UserControl changes it, but can you not just use the OriginalSource property on RoutedEventArgs instead?
Related
I have a little problem with winforms and mousewheel events.
I have a custom user control representing a slider. Now, I have a couple groups of sliders in which each group is wrapped inside a panel. All the groups are then wrapped in another panel (which has AutoScroll set to true) and this is wrapped in a form. The slider logic is implemented such that the mousewheel can be used to change its value. For this, the slider user control gets focus when the mouse is over the slider. However, when I scroll, also the AutoScroll parent panel scrolls with it.
I've already lost a lot of time on this issue. Anybody knows what is happening here and how I can solve it? I thought the event was bubbling to the parent panel but I don't find a Handled property on the event when handling it in the Slider control (as is possible with WPF).
many thanks
We implemented the Slider as a complete custom user control (inheriting the UserControl class) with own look-and-feel.
You might have noticed that a UserControl doesn't show the MouseWheel event in the Properties window. Hint of trouble there. The WM_MOUSEWHEEL message bubbles. If the control that has the focus doesn't handle it then Windows passes it on to its Parent. Repeatedly, until it finds a parent window that wants to handle it. The Panel in your case.
You'll need to invoke a bit of black magic in your slider control. The actual event args object that get passed to the MouseWheel event is not of the MouseEventArgs type as the event signature suggests, it is HandledMouseEventArgs. Which lets you stop the bubbling. Like this:
protected override void OnMouseWheel(MouseEventArgs e) {
base.OnMouseWheel(e);
// do the slider scrolling
//..
((HandledMouseEventArgs)e).Handled = true;
}
If you are creating event dynamically like
object.event += new EventHandler<EventArgs>(eventfunction);
try un-registering the event after the eventfunction is called like this
object.event -= new EventHandler<EventArgs>(eventfunction);
I have a ScrollViewer that contains a large image. I want the user to be able to hold the mouse down and drag the image to move it side to side, and I'm trying to implement this using a Behavior. The problem is that the mouse down event doesn't seem to be able to fire on the ScrollViewer when a user presses down on the mouse button. With a code behind, I would handle that event on the image, but with a behavior that is only attached to one control I don't know how to approach this issue.
What approach should I use to create a System.Windows.Interactivity.Behavior that attaches to both objects?
The MouseLeftButtonDown event is raised just fine on the ScrollViewer. The problem is that the ScrollViewer is handling the event itself (e.Handled = true). And since it is already handled your behavior do not receive it.
Depending on what you are doing you might be able to just use the PreviewMouseLeftButtonDown event instead. This way it doesn't matter if the ScrollViewer will handle it since the Behavior is receiving it first.
You could also try to use the Drag&Drop events directly. But I'm not sure if that will work.
You don't need to attach to an element to be able to add an event handler to it. If your AssociatedObject is a ScrollViewer then you might be able to get a reference to the image like this:
var image = AssociatedObject.Content as Image;
and then you can add a mouse event handler:
image.MouseLeftButtonDown += (s, e) => Debug.WriteLine("Clicked!");
So the object you are attached to, your associated object, is your "home base", but you can work with any object you can get your hands on up to and including using VisualTreeHelper to walk the visual tree.
I have a MyButton class that inherits from Button. On this class I have placed several other controls (Labels, Progessbar). The problem with this is that the controls on the Button make it impossible to fire the Button.Click or Button.MouseHover event. How can I achieve it that the controls on the Button are only displayed but are "event transparent": A click/hover on the label and progessbar is the same as if I clicked/hover directly on the Button (including sender and everything). Something like "inheriting the events from the parent".
class MyButton : Button
{
Label foo = new Label();
ProgressBar bar = new ProgessBar();
}
You should derive from UserControl then have the button as a child control, and bubble up the button child's on click event.
This link is probably more than what you need, but it's a good starting point.
UPDATE
As pointed out, you may not be using ASP.NET. So here is another post that talks about different custom user controls, specifically what you're after is a Composite Control. This is for Windows Forms.
Write Click event handlers for the label and PB, have them call PerformClick().
Making controls transparent to the mouse is possible but is ugly to do. You'd have to override WndProc() to catch WM_NCHITTEST and return HTTRANSPARENT. The all-around better solution is to not use controls. A Label is just TextRenderer.DrawText() in the button's Paint event. ProgressBar isn't hard either, e.Graphics.FillRectangle().
Having the child controls be real controls in front of the button (either in a class inheriting from Button or from UserControl) may make it hard to get button-specific events working properly, as you have found. (Edit: Although it's hard, it's not impossible -- see Hans Passant's answer)
As a workaround, instead of using child controls, you could custom-paint them onto the button surface, since you don't need most of the functionality of the controls (events, focusing, etc.), just their display.
You can do the additional painting in the OnPaint method of your class. Something like:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawString("My fake label", Font,
SystemBrushes.ControlText, new Point(10, 10))
// draw progressbar
}
To update the display, you would need to trigger a repaint of the Button using Invalidate().
Take a look at Custom Bitmap Button Using C# on CodeProject for a more complete example.
This answer was here a moment ago, but got deleted:
Can you inherit from UserControl instead? This will allow you to place other controls on the control surface, including your button and subscribe to their events.
If you're using WPF, I guess what you're looking for would be something called Bubbled Events. It's a feature in WPF by which events are bubbled from a control to its parent (in your case, it would be from your ProgressBar and Label to your button). I found this article on the matter which I think would be of help to you.
Hope this helps :)
I am creating a custom control. Let's say I'm reinventing the wheel and creating a custom Button control that derives from the UserControl class(only for example)
Well, it of course has a Click event handler. Now my problem is, when do I call this event handler? I know it happens sometime between the Pages OnLoad and OnLoadComplete, but I'm not quite sure what event I can hookup to so that the Click event is raised at the same time as other control events.
When are you suppose to call custom control events?
In general you should raise the event as soon as you know that the underlying event has happened.
For example, how does the ASP.NET Button control know that the it was clicked by the use? It implements IPostBackEventHandler and the RaisePostBackEvent() method. That method will get called if there is postback event data associated with the control. I believe the association is determined by the "name" attribute that it renders. In RaisePostBackEvent() it then raises the Click event.
The question you need to answer is: How does your control know that it should raise its event? Once you find that our the rest is easy.
If your control has similarities to existing ASP.NET controls I would recommend stepping through the ASP.NET source code and seeing how those controls work.
If you implement IPostbackEventHandler, you can do something like this, taken from decompiling System.Web.Ui.WebControls.Button
protected virtual void RaisePostBackEvent(string eventArgument)
{
base.ValidateEvent(this.UniqueID, eventArgument);
if (this.CausesValidation)
{
this.Page.Validate(this.ValidationGroup);
}
this.OnClick(EventArgs.Empty);
this.OnCommand(new CommandEventArgs(this.CommandName, this.CommandArgument));
}
Is it possible to set up a Grid or other container control to be sort of an event dam? So that when any events, specifically a left click, that start within it are working their way up that they stop at that control and go no further?
PreviewMouseDown is your friend...
Add this event to your control, and set the Handled property on true...
All events tunnel first from root to leaves in the preview fase, then they are handled from leaves to root in the actual event case...
So PreviewMouseDown handles the Grid before the Button, while the MouseDown event handles the Button before the Grid...
hope this helps...
You should be able to extend whatever control you want (assuming it is not sealed). In your extended class you can override the click event and swallow it (do not pass it to the base class).