Bubbling Events not Occurring - c#

In the code below, I am seeing the tunneling events occurring but am not seeing the corresponding bubbling events occurring. Why might this be?
Thanks,
Dave
<Window x:Class="TestRoutedEvents.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"
PreviewMouseUp="Window_PreviewMouseUp"
MouseUp="Window_MouseUp">
<Grid Background="Brown" Margin="30"
PreviewMouseUp="Grid_PreviewMouseUp"
MouseUp="Grid_MouseUp">
<TextBlock Text="Press me" HorizontalAlignment="Center" VerticalAlignment="Center"
Background="LightGray"
Padding="3"
PreviewMouseUp="TextBlock_PreviewMouseUp"
MouseUp="TextBlock_MouseUp"/>
</Grid>
</Window>
namespace TestRoutedEvents
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void TextBlock_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("TextBlock_PreviewMouseUp");
}
private void Grid_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Grid_PreviewMouseUp");
}
private void Window_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Window_PreviewMouseUp");
}
private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("TextBlock_MouseUp");
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Grid_MouseUp");
}
private void Window_MouseUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Window_MouseUp");
}
}
}

Actually event is raising but you are not able to get that. The reason is that the messagbox takes the focus from the window when it pops up. Thus, the UI elements in the routed event chain won't receive the routed event any more.
you can have a control to your window and add event details into that to confirm about it.
e.g. Add ListBox in xaml and name it like listBox and then use this code in each of your handler
listBox.Items.Add(sender+"\n"+e.RoutedEvent.Name+"\n"+e.RoutingStrategy);

This is a quote from MSDN:
TextBox has built-in handling for the
bubbling MouseUp and events.
Consequently, custom event handlers
that listen for MouseUp or MouseDown
events from a TextBox will never be
called. If you need to respond to
these events, listen for the tunneling
PreviewMouseUp and PreviewMouseDown
events instead.
The article is about TextBox not TextBlock, but I tested this on other UIElement-derived controls, and they all seemed to behave the same way. I'm assuming this is at the base class level somewhere in the hierarchy.
How to: Handle MouseUp and MouseDown Events for a TextBox

I don't know what you are trying to do with this, but perhaps this post will help. It confirms what Anurag is saying, regarding the messagebox taking the focus from your window.
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/f4d609d4-ba2c-478e-aa53-9ee557ea5165

Related

Detect global click UWP

I have a visible grid that has to collapse when I click outside of it. I solved it for half.
MainPage.xaml code:
<StackPanel Width="400" VerticalAlignment="Center">
<Button x:Name="btnOne" Content="Button One" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="30"/>
<Button x:Name="btnShowGrid" Content="Show Grid" Click="btnShowGrid_Click" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="30"/>
<Grid x:Name="ControlGrid" PointerEntered="ControlGrid_PointerEntered" PointerExited="ControlGrid_PointerExited" HorizontalAlignment="Center" Visibility="Visible" Background="LightGreen" Height="300" VerticalAlignment="Top" Width="300" Margin="30"/>
</StackPanel>
MainPage.xaml.cs code:
bool PointerInGrid = false;
public MainPage()
{
this.InitializeComponent();
}
protected override void OnPointerPressed(PointerRoutedEventArgs e)
{
base.OnPointerPressed(e);
if (!PointerInGrid)
{
ControlGrid.Visibility = Visibility.Collapsed;
}
}
private void btnShowGrid_Click(object sender, RoutedEventArgs e)
{
ControlGrid.Visibility = Visibility.Visible;
}
private void ControlGrid_PointerEntered(object sender, PointerRoutedEventArgs e)
{
PointerInGrid = true;
}
private void ControlGrid_PointerExited(object sender, PointerRoutedEventArgs e)
{
PointerInGrid = false;
}
If I click into the Grid this remains visible, if I click out the grid goes to collapsed, so far everything is fine but, if I click the btnOne the grid remains visible.
So is possible detect a global click to collapse the grid?
As always thanks in advance.
Code that works in windows form:
const int WM_PARENTNOTIFY = 0x0210;
const int WM_LBUTTONDOWN = 0x0201;
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
if (!DesignMode)
{
if (m.Msg == WM_PARENTNOTIFY)
{
if (m.WParam.ToInt32() == WM_LBUTTONDOWN)
{
MessageBox.Show("Clicked!");
}
}
}
base.WndProc(ref m);
}
The PointerPressed event is swallowed by the button so it cannot reach the underlying parent, where the event handler OnPointerPressed is executed.
There is a technique to pass the event down to the parent, please refer to this answer.
You can do it like this, in the page's constructor
public MainPage()
{
this.InitializeComponent();
//...other code
//then add this line
this.btnOne.AddHandler(UIElement.PointerPressedEvent,
new PointerEventHandler((s, e) => { e.Handled = false; }), true);
}
A second thought: Maybe you can just handle btnOne’s PointerPressed event, and in the handler set its event argument’s Handled property as false. Don’t need to use AddHandler, I mean. Give it a try.
As far as I known, the UWP is sandboxed, we can't use WndProc method to receive Window event Messages.
Specific Windows Runtime controls may have class-based handling for the PointerPressed input event. If so, the control probably has an override for the method OnPointerPressed. Typically the event is marked handled by the class handler, and the PointerPressed event is not raised for handling by any user code handlers on that control. For example, ButtonBase has class handling that handles PointerPressed and instead fires Click.
For more info, please refer PointerPressed.
When we click the Button, the OnPointerPressed event will not be fired.
If you want to your Button can fire OnPointerPressed event, you can create a class that inherited the Button class. In the class, you can override the OnPointerPresse event.
For example:
class MyButton: Button
{
protected override void OnPointerPressed(PointerRoutedEventArgs e)
{
}
}
By the way, if we override OnPointerPressed event in class that inherited the Button class, the Click event will not be fired.

WPF Drag&Drop vs manipulation

I want to enable Drag'n'Drop of a child control inside a parent that has IsManipulationEnabled = true.
When manipulation is enabled, touch events don't get promoted to mouse events. In order to enable promotion, one should handle touch events before manipulation logic steps in (see example). I've tried that and it works... until I call DoDragDrop for the first time. Then I no longer receive mouse events. Why?
Here's a minimal code to reproduce the issue. All drag and drop handling was removed for the sake of readability.
XAML:
<Window x:Class="Test.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" IsManipulationEnabled="True">
<Grid>
<Border Background="Red"
x:Name="Border"
TouchDown="Border_OnTouchDown"
MouseDown="Border_OnMouseDown"
TouchUp="Border_OnTouchUp"
Width="100" Height="50" />
</Grid>
</Window>
C#:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Border_OnTouchDown(object sender, TouchEventArgs e)
{
Debug.WriteLine("Border_OnTouchDown");
e.Handled = true;
e.TouchDevice.Capture((FrameworkElement)sender);
}
private void Border_OnMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("Border_OnMouseDown!");
DragDrop.DoDragDrop((DependencyObject)sender, "", DragDropEffects.All);
}
protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
{
Debug.WriteLine("OnManipulationStarted");
base.OnManipulationStarted(e);
}
private void Border_OnTouchUp(object sender, TouchEventArgs e)
{
((FrameworkElement)sender).ReleaseTouchCapture(e.TouchDevice);
e.Handled = true;
}
}
Output:
Border_OnTouchDown
Border_OnMouseDown! <- works first time
Border_OnTouchDown
Border_OnTouchDown <- no longer works, no matter how many times I tap
Border_OnTouchDown
Border_OnTouchDown
Border_OnTouchDown
...
If I don't call DoDragDrop in MouseDown - events get promoted as they should.
Looks like this is a bug in .NET. I had v4.5.2 installed. Now I've installed v4.6 and the problem is no longer reproducible.
I didn't even have to retarget the project to v4.6 or recompile it: just installing the new runtime fixed everything.
This solution works for any framework version:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Border_OnTouchDown(object sender, TouchEventArgs e)
{
Debug.WriteLine("Border_OnTouchDown");
IsManipulationEnabled = false;
e.TouchDevice.Capture(Border);
}
private void Border_OnMouseDown(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("Border_OnMouseDown!");
DragDrop.DoDragDrop((DependencyObject)sender, "", DragDropEffects.All);
}
protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
{
Debug.WriteLine("OnManipulationStarted");
base.OnManipulationStarted(e);
}
private void Border_OnTouchUp(object sender, TouchEventArgs e)
{
Border.ReleaseTouchCapture(e.TouchDevice);
IsManipulationEnabled = true;
}
}
Here we are basically disabling manipulation if the user touched the Border. Since drag&drop operation could (and probably would) end outside of the Border, we also need to capture the touch input to be sure to receive the TouchUp event to re-enable manipulation.

How to allow key entry in a textbox while capturing it in a window?

I have a WPF window which captures both the KeyDown and KeyUp events. However, there is a TextBox in this window, and if a user is typing in that textbox, I want the general capture to effectively be ignored. The trouble I'm running into is that my efforts to ignore the general capture when typing in that textbox result in the user being unable to type in that textbox at all.
MainWindow.xaml
<Window x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="600" Width="500"
KeyDown="MainWindow_OnKeyDown"
KeyUp="MainWindow_OnKeyUp">
<StackPanel>
<TextBox x:Name="TextBoxAllowed" KeyDown="TextBoxAllowed_OnKeyDown" KeyUp="TextBoxAllowed_OnKeyUp" />
</StackPanel>
</Window>
MainWindow.xaml.cs
namespace MyProject
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
{
// take action using the value of e.Key
// should not be triggered when TextBoxAllowed has focus
// but should be triggered otherwise
}
private void MainWindow_OnKeyUp(object sender, KeyEventArgs e)
{
// take action using the value of e.Key
// should not be triggered when TextBoxAllowed has focus
// but should be triggered otherwise
}
private void TextBoxAllowed_OnKeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
private void TextBoxAllowed_OnKeyUp(object sender, KeyEventArgs e)
{
e.Handled = true;
}
}
}
I'd like it so that when TextBoxAllowed has focus, the user can type in it normally, and that the MainWindow_OnKey* event handlers are not triggered. But when that field doesn't have focus, if any typing takes place, then I want those key handlers to kick in. How do I accomplish this?
Just use this:
private void TextBoxAllowed_OnKeyDown(object sender, KeyEventArgs e)
{
if (!yourTextBox.HasFocus())
e.Handled = true;
}

WPF, Some Event not fire up from Grid To UserControl

I have a UserControl where i override some event like this:
public class MyControl: UserControl
{
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
// handle mouse event up
}
}
I add this control to antother UserControl -> Grid, this Grid has MouseUp registered.
public class MyParent: UserControl
{
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
// handle grid mouse event
}
}
And in MyParent XAML i have simply:
<UserControl ... >
<Grid Name="Grid" MouseUp="Grid_MouseUp">
<my:MyControl Height="250" Width="310" Visibility="Visible" Opacity="1" IsEnabled="True" />
</Grid>
</UserControl>
What i notice is that when i release mouse over MyControl the event is captured by Grid and not routed to MyControl, why?
How can i receive MouseUp event inside MyControl class?
EDIT
With MouseDown all works as expected, only MouseUp not works... and both event are registered for parent Grid too, so what is the difference?
EDIT2
Ok, i think i found the problem, if i add MyControl to MyParent -> Grid directly in XAML, all works good, but if i add it programmatically "MyParentInstance.Grid.Children.Add(MyControlInstance)" then i have the problem above.
Is my code to add control correct?
Thanks.
RoutedEvents only work for the parents of the element, which is invoking the specific event. That means for you, that all parent controls of your grid will receive the event, but children will not. For more information about routed events see Routed Events Overview. To solve your problem I would suggest to register the MouseUp Event at MyControl:
<UserControl ... >
<Grid Name="Grid">
<my:MyControl MouseUp="Grid_MouseUp" Height="250" Width="310" Visibility="Visible" Opacity="1" IsEnabled="True" />
</Grid>
</UserControl>
I did not come across the answer to this question after an extensive search of forums so I'm providing my solution here although this post is dated. I made use of event handlers that I'd already built in the child control by making them public. When the ChildControl user control is added to the Window at the top level, these fire. When nested within parent, the event had to be handled at the parent level.
When programmatically adding a control, add the event handler in the Parent control, such as:
class Parent : UIElement
{
//...
void Parent_AddChildControl()
{
ChildControl child = new ChildControl();
child.MouseEnter += child_MouseEnter;
UiElementX.Children.Add(child);
}
void child_MouseEnter( object sender , MouseEventArgs e )
{
((ChildControl)sender).ChildControl_MouseEnter(sender, e);
}
}
class ChildControl : UIElement
{
//...
public void ChildControl_MouseEnter(object sender, MouseEventArgs e)
{
//event handling
}
}

Prevent KeyDown event bubbling from TextBox to MainWindow in WPF

I have a program that contains a textbox control. A user is able to enter text into this control. The user can also press certain keys to trigger other actions (this is handled on the MainWindow). I have sample XAML and C# code to demonstrate my setup.
XAML
<Window x:Class="RoutedEventBubbling.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" />
<TextBox x:Name="Output" Grid.Row="1" IsReadOnly="True" />
</Grid>
</Window>
C#
using System.Windows;
using System.Windows.Input;
namespace RoutedEventBubbling
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private int mHitCount = 0;
public MainWindow()
{
InitializeComponent();
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
this.Output.Text = string.Format("Hit Count: {0}", ++mHitCount);
}
}
}
As you may have realised, in the case of this program, if I start typing into the first TextBox the second will be updated with the hit count. This is an undesirable result as I might have certain actions that I wish to trigger when handling the MainWindow's OnKeyDown method.
As such, my question is this: is it possible to prevent the OnKeyDown method being called on the MainWindow, while still allowing text to be entered into the text box? I know of the e.Handled = true approach, but in this case doing that on the KeyDown event of the TextBox will prevent the text from being entered. If this is not possible, I will have to find some other way of handling it all.
Thanks in advance.
EDIT
I have just found a moderately hacky way around this issue. If I handle the MainWindow's OnTextInput method instead, then the result I desired will be achieved as the TextInput event of the TextBox will have been handled. Below is a sample of the code I used:
private Key mPressedKey;
protected override void OnKeyDown(KeyEventArgs e)
{
// Note: This method will be called first.
base.OnKeyDown(e);
// Store the pressed key
mPressedKey = e.Key;
}
protected override void OnTextInput(TextCompositionEventArgs e)
{
// Note: This method will be called second, and only if the textbox hasn't handled it.
base.OnTextInput(e);
this.Output.Text = string.Format("Hit Count: {0}", ++mHitCount);
// ... Handle pressed key ...
}
You could put a type-based guard on that statement:
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.OriginalSource is TextBoxBase == false)
{
mPressedKey = e.Key;
}
}
Quite possibly not the best solution either though.
I ended up working around this issue using the following setup:
private Key mPressedKey;
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
mPressedKey = e.Key;
}
protected override void OnTextInput(TextCompositionEventArgs e)
{
base.OnTextInput(e);
// ... Handle mPressedKey here ...
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
// Force focus back to main window
FocusManager.SetFocusedElement(this, this);
}
This was done in the MainWindow.xaml.cs. It's a bit of a hack, but it achieved the result I wanted.

Categories