Here's how my element tree is composed (irrelevant informations removed).
<TabItem Style="Click_PutNormalCursorBack">
<ScrollViewer>
<ItemsControl Style="ContainsMuchLabels_n_CollapsibleGroupeHeaders">
<Label Style="Click_ChangeCursor" />
</ItemsControl>
<ScrollViewer>
</TabItem>
I am simulating Drag n Drop. When you click on the clickable label, it transform's your cursor to guive you the impression your dragging it. What i want is that clicking on the TabItem, it put back your normal cursor.
I cant use PreviewMouseDown event because it would never allow the label to be clicked.
One solution i found was adding a big panel to my element tree between my ScrollViewer and my ItemsControl with a transparent background to recieve clicks, putting an event handler on my ItemsControl's CollapsibleGroupHeader and ItemsPanel but its not really an elegant solution.
I tough RoutedEvents in WPF where ther to resolve that kind of problems...
Note : my handlers are not my my styles, styles where just ther for guiving more information
Is ther a better solution than mine?
Use the MouseDown event for both. Just don't mark the event handled (e.Handled = true) if the event doesn't apply. For example, in your clickable tab, if it's clicked when the cursor isn't in the drag state, don't mark handled. Once the cursor IS in the drag state, if the user clicks again on the clickable label, don't mark handled (unless you want that event to reset the cursor). You could also use the PreviewMouseDown event on the tab, but again don't mark handled unless that click is from a cursor that originates from the label.
If you don't mark an event handled it continues to propagate so multiple controls can handle it. Even if something does mark an event handled and you still want to handle it, you can make an event fire even when marked handled: see "Adding Instance Handlers That Are Raised Even When Events Are Marked Handled" in this article
Related
I'm trying to build a UWP-based WebView application, and I have this basic design:
<Page>
<Grid>
<WebView Name="_webhost" DragEnter="_webhost_OnDragEnter"/>
<StackPanel AllowDrop="True" Drop="_webhost_OnDrop" DragOver="UIElement_OnDragOver" Background="Transparent" PointerEntered="UIElement_OnPointerEntered"/>
</Grid>
</Page>
So the StackPanel is on top of the Webview, both of them are filling the whole screen while the StackPanel is actually transparent by default.
When I set IsHitTestVisible=false in the StackPanel, then all mouse-related input events are fired in the WebView's contents - but the thing is, I want some (specific) events to fire in the StackPanel. When not setting that property in the StackPanel, it catches all of the input events, thus none of them are propagated into the WebView.
Note that setting event handlers for the WebView usually doesn't do anything - since official docs says state that it doesn't support most of the user input events inherited from UIElement.
So bottom line - I want specific events to fire in StackPanel, but all other events to fire in the other element (WebView or at least its contents). How can this be achieved?
In general, the input event will bubble from the children element to parent element, and the bottom same level control will not hold the event. And I tested your code the WebView DragEnter never trigger until set IsHitTestVisible=false for StackPanel.
I want specific events to fire in StackPanel
If you want specific the event for StackPanel, you could add e.Handled = true; in the event handler. But it only works for event that bubble from the children element to parent element
The title says it all: "How do I prevent TextBox or RichEditBox from losing focus after clicking on a disabled button?"
I tried setting AllowFocusOnInteraction to false, but that only works when the button is enabled. That property works well if, say, I have a button for setting text to bold or italic. The editor will not lose focus and everything works superb. But if I were to disable that button and then click on it, the editor loses focus. This causes several new issues for me. Would love some help with this. Thanks.
Also note that I have a UWP app. Not sure if it matters, though.
Okay, so I figured out the CORRECT way to fix the issue.
So the bold, italic, and underline format buttons are all within my custom toolbar. What I had to remember is that most xaml elements can be clicked on and therefore trigger a PointerPressed event so long as the element (a Grid element for example) has a background for the click to intercept. If the background is transparent the PointerPressed event will not fire.
Sooo, I made sure my toolbar had a solid background set and then I set a PointerPressed event on it. So whenever you click on the toolbar that event will fire and I simply set the e.Handled property to true;
So now because the button within the toolbar is disabled the pointer click will go to the next clickable element within the visual tree that is underneath the button that you clicked, which is my toolbar (which now has a background). When the toolbar is reached, the e.Handled event being set to true within the event handler will tell the system to do nothing further and so the RichEditBox retains its focus.
I know my writing here is very sloppy but I am sort of in a rush right now and so I will most likely come back and clean my answer up. Hope this helps someone.
wrap the disabled button in a Border or a Grid.
<Grid Tapped="DisabledButtonTapped">
<Button IsEnabled="false"/>
</Grid>
now with help of tapped method on this grid you can set focus back to your RichEditBox.
private void DisabledButtonTapped(object sender, object args)
{
MyRichEditBox.Focus(FocusState.Programmatic);.//use the x:Name of your richeditbox in place of "MyRichEditBox".
}
I have a custom UserControl. I want to use it in a few different products, so I want something that can be implemented inside of the UserControl itself. I want to know when the user has clicked outside of the bounds of the UserControl so that I can hide it, similar to a ComboBox. How can I do that?
I tried handling the click event, but it only seems to fire if the click occured within the bounds of the control.
That's what the Capture property is designed to do. Set it to true and all mouse messages are routed to your control, even if it moves out of the window bounds. Check the e.Location property in the MouseDown event.
Hm, you may be able to accomplish what you want by listening to the GotFocus/LostFocus events. ComboBoxes give the drop downs focus when they open and close them when they lose focus.
do this
Select all controls on your form including form
In Property Window select MouseClick event
Now enter below Code in Common_MouseClick
Code:
if (!sender.Equals(yourControl))
{
yourControl.Visible=false;
}
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).
In the MSDN article Understanding Routed Events and Commands In WPF, it states
an event will bubble (propagate) up the visual tree from the source element until either it has been handled or it reaches the root element.
However, in this example, when you click the button, it doesn't "bubble up the visual tree" to get handled by the parent StackPanel event, i.e. clicking on the button fires no event.
Why not? What do they mean then by "bubbling up" if not this?
XAML:
<Window x:Class="TestClickEvents456.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel x:Name="TheStackPanel"
Background="Yellow"
MouseDown="TheStackPanel_MouseDown">
<Button x:Name="TheButton"
Margin="10"
Content="Click This"/>
<TextBlock x:Name="TheMessage"
Text="Click the button or the yellow area"/>
</StackPanel>
</Window>
code-behind:
using System.Windows;
using System.Windows.Input;
namespace TestClickEvents456
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void TheStackPanel_MouseDown(object sender, MouseButtonEventArgs e)
{
TheMessage.Text = "StackPanel was clicked.";
}
}
}
The event bubbles up, until it gets handled...
Since the Button does something with your mouse clicks, it absorbs your mouse event and turns it into a ClickEvent.
If you use the PreviewMouseDown, you see that the StackPanel first receives the event before the button does.. Preview events use the Tunnel down approach..
As others have said, it's because the MouseDown event gets handled by the Button before it can be bubbled further. You can see this in Reflector, in ButtonBase.OnMouseLeftButtonDown:
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (this.ClickMode != ClickMode.Hover)
{
e.Handled = true;
// SNIP...
}
base.OnMouseLeftButtonDown(e);
}
One solution is to listen for a MouseDown event, and indicate that you don't care if the event is handled. You can do this with the AddHandler method. It has a boolean overload that lets you listen for events which are already handled.
If you do this somewhere instead of setting the MouseDown handler in XAML:
TheStackPanel.AddHandler(MouseDownEvent, new MouseButtonEventHandler(TheStackPanel_MouseDown), true);
You'll receive all MouseDown events on TheStackPanel, regardless of whether they've been handled.
In addition, if you want the stackpanel to receive the event, change the stackpanel xaml to:
<StackPanel x:Name="TheStackPanel"
Background="Yellow"
Button.Click="TheStackPanel_MouseDown" />
and the event signature to:
private void TheStackPanel_MouseDown(object sender, RoutedEventArgs e)
In this case, the stackpanel will recieve the button's click event. However, clicking on the stackpanel itself won't fire any event, since it listens specifically to a button click.
It is because all the messages are being captured handled by Button and messages stop the message stops bubbling there. The answer is right in your question's text:
An event will bubble (propagate) up the visual tree from the source element until either it has been handled or it reaches the root element.
EDIT:
Edward Tanguay (OP) commented on this answer and I am copying his comment here because it is very relevant:
"I don't see that the button IS handling the event, i.e. I have no click handler on the button, I DO have a click handler (MouseDown) on the StackPanel and hence I would think it would bubble up PAST the button since the button doesn't handle it and get handled by the stackpanel which does, right?"
You are right. Button is not handling the MouseDown event because no handler ha been specified for it at that control.
But, then, MouseDown is particular in some way. At least in Windows Forms it is used to initiate actions as drawing and dragging so, when a control gets the event, it proceeds to trap all the subsequent mouse messages even if you have not defined handlers for it. This trap is done when the control sets Capture property to True and this effectively stop subsequent events from being bubbled up. Capture property is set back to False by Windows Forms when it gets a MouseUp event.
I repeat, this is the way it works in Windows Forms, you may want to double-check this but, IMHO there is no reason why this should be different for WPF.
For reference: Se the section "Windows Forms processing" at http://blogs.msdn.com/jfoscoding/archive/2005/07/28/444647.aspx (scroll slightly down from the middle of the page).
Note: See my comment to Arcturu's answer for a reference on bubble and tunneling events raise sequences.
button event Suppress the mousedown and mouseup because button event is high level event and have bit of code that give flag handle true this cause Suppressed for mousdown to resolve this problem you can add this code in constructor of window
TheButton.AddHandler(
UIElement.MouseDownEvent,
new MouseButtonEventHandler(TheStackPanel_MouseDown),
true);