I have a Button
<Button>
<Button.Template>
<ControlTemplate>
<StackPanel>
<Image Source="share.png"
MouseLeftButtonUp="Image_MouseLeftButtonUp"
MouseLeftButtonDown="Image_MouseLeftButtonDown" />
</StackPanel>
</ControlTemplate>
</Button.Template>
</Button>
But the problem is that, unlike MouseLeftButtonDown, the event MouseLeftButtonUp does not fire. Why? How can I solve it? Thanks.
Probably because the Button is handling the event already, so it doesn't filter down to the Image control.
Why are you handling the MoustUp and MouseDown events on the Image? Why not handle them on the Button?
EDIT
After a quick look at the documentation for this event, I see that the routing strategy for MouseLeftButtonUp is Direct, but the actual underlying event is the MouseUp event which has a Bubbling strategy. So, in effect, MouseLeftButtonUp should have a de-facto bubbling strategy, meaning the Image should have the opportunity to handle the event before the Button.
Sounds like something else might be going on. If you set the Image to nothing on the Image control, and the background is null (different from Color.Transparent), then WPF will treat the control as not "existing" as far as mouse events go.
EDIT 2
Hmm... maybe my original guess was correct afterall. According to this thread:
http://social.msdn.microsoft.com/forums/en/wpf/thread/e231919d-9fef-4aa5-9dcb-2c1eb68df25b/#306db880-ed50-45d2-819f-88d76f7148c1
Button is handling the MouseUp event. Try using PreviewMouseUp and then checking to see which button was clicked.
EDIT 3
Ok... I think I figured it out. I'm betting that the Button is "capturing" the Mouse on the MouseDown event. When this occurs, other controls will not receive MouseUp or MouseDown events (include the Preview versions) until the Mouse has been released.
See: http://books.google.com/books?id=nYl7J7z3KssC&pg=PA146#v=twopage&q&f=true
The Click event is handling the LeftMouseUp event as Cyborgx37 said.
You can use the PreviewMouseLeftButtonUp event instead of MouseLeftButtonUp, on Button as you can see below:
<Button PreviewMouseLeftButtonUp="Button_PreviewMouseLeftButtonUp">
<Button.Template>
<ControlTemplate>
<StackPanel>
<Image Source="..." />
</StackPanel>
</ControlTemplate>
</Button.Template>
</Button>
Your Button template only has an Image, I guess you don't need this Button, you can use as above, so you can capture the MouseLeftUp event.
<Image MouseLeftButtonUp="Image_MouseLeftButtonUp" Source="..." />
This way is so much easier, but it depends on what you need.
Btw, sorry for my crap english, I hope it could help you.
Try using the event PreviewMouseLeftButtonUp.
Related
I'm not using any external libraries here, just plain WPF.
I have a DataGrid with a custom DataGridColumnHeader. This column header contains a ToggleButton to toggle a Popup. Inside the popup there is a TextBox. The problem I'm having is that doubleclicking inside the TextBox raises the MouseDoubleClick event on the DataGrid. Here's a simplified version containing numbered comments I will refer to afterwards
<Window x:Class="PopupsAreWeird.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PopupsAreWeird" xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<!-- 1) This eventhandler is never called -->
<Grid Control.MouseDoubleClick="Grid_MouseDoubleClick_1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" />
<ToggleButton x:Name="openToggle" Grid.Column="1" Content="Open" />
<Popup IsOpen="{Binding ElementName=openToggle, Path=IsChecked}" StaysOpen="True">
<!-- 2) This eventhandler is always called, and the problem I am having is there regardless of whether I set e.Handled = true in this handler or not -->
<TextBox Width="200" MouseDoubleClick="TextBox_MouseDoubleClick" />
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical">
<!-- 3) This eventhandler is always called, but never should be -->
<DataGrid MouseDoubleClick="DataGrid_MouseDoubleClick">
<DataGrid.Columns>
<DataGridTextColumn Header="Header 1" Width="200" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
1) I do not understand why this handler is never called. This was meant to set e.Handled to true to stop bubbling upwards to the DataGrid. While Grid does not have a definition for MouseDoubleClick, it is my understanding that I can attach eventhandlers for any event to any element (like attaching ButtonBase.Click to a Panel element). Is this not true, or is there any special case here I am missing?
3) I want to avoid the double click event bubbling to here, but even if I set e.Handled to true in eventhandler 2, this eventhandler is called, and in that handler, e.Handled is false. I assume the reason is that the Popup is defined inside of DataGridColumnHeader, and for some weird reason 2 events are being raised, one for the tree of the popup, and one for the tree that contains the Popup element, but that seems a bit nonsensical.
I know Popup is a sort of weird thing in WPF, but this seems like I'm missing something obvious. Is there any way to achieve what I want, i.e. not have events (or at least the MouseDoubleClick event) bubble up to the DataGrid?
Thanks in advance,
David
1) You are right, You can attach a handler for a routed event to any UIElement. But indeed the Popup is a special case. The Popup is a special control that behaves like Window. It can popup everywhere on the screen, always rendered top-most and is not necessarily bound to the application itself. That's why its visual tree is detached from the application's visual tree. Popup.Child will be a separate isolated visual tree. Microsoft Docs: Popup and the Visual Tree.
Since routed events traverse the visual tree to be handled by any node, it makes sense that bubbling/tunneling routed events inside the Popup will stop/start at the root of this isolated tree. So routed events that are originated in the Popup cannot be handled outside the Popup.
3) Short version: Control.MouseDoubleClick (and the preview version) is a special event that behaves different. This event is raised on each UIElement on the route when the event traverses the visual tree. So setting Handled to true has no effect.
To solve your problem, you should either handle UIElement.PreviewMousLeftButtonDown and check if the MouseButtonEventArgs.ClickCount equals 2 to detect a double click and then set Handled = true
or check if the type of the sender or RoutedEventArgs.Source is not TextBox before handling it (explicit event filtering).
"Although this routed event seems to follow a bubbling route through
an element tree, it actually is a direct routed event that is raised
along the element tree by each UIElement. If you set the Handled
property to true in a MouseDoubleClick event handler, subsequent
MouseDoubleClick events along the route will occur with Handled set to
false. This is a higher-level event for control consumers who want to
be notified when the user double-clicks the control and to handle the
event in an application.
Control authors who want to handle mouse double clicks should use the
MouseLeftButtonDown event when ClickCount is equal to two. This will
cause the state of Handled to propagate appropriately in the case
where another element in the element tree handles the event.
The Control class defines the PreviewMouseDoubleClick and
MouseDoubleClick events, but not corresponding single-click events. To
see if the user has clicked the control once, handle the MouseDown
event (or one of its counterparts) and check whether the ClickCount
property value is 1."
Microsoft Docs: Control.MouseDoubleClick
I have a simple button, and I would like it to have the behavior of a "sticky" button, such as it visually has two states (much like a switch). In other words, I would like it to maintain its pressed style even after I release the mouse button, but it would have to be clickable.
Is there a way to easily do it in WPF? I'm using Fluent but I could change it if needed.
<Fluent:Button Header="myButton" Command="{Binding ToggleCommand}" SizeDefinition="Small">
<Fluent:Button.Icon>
<Grid Width="16" Height="16">
<Image Source="../Images/16x16/icon.png"/>
</Grid>
</Fluent:Button.Icon>
</Fluent:Button>
Something like this:
1-
2-
PS:
I'm using MVVM, and in the Command, I'm checking and changing a bool flag to know what state the button currently is. I would be able to bind it to a button property, if it helps in any way.
Turns out there is a component ToggleButton that does exactly what I was looking for. Silly me for not knowing it existed in the first place.
I understand how bubbling and tunneling works. However, i'm confused about using them.
Here is why:
I want to handle a mouse click event. To bubble it, there is MouseDown and, to tunnel it, there is PreviewMouseDown. However, MouseDown doesn't necessarily mean the user clicked the control. May be the user pressed the button and moved away from it to cancel the click.
I wouldn't want to change anything if the button is not being clicked.
So my question is, how are the Bubbling/Tunneling strategies useful?
If the event is listed RoutedEventArgs, then it's routed event. Routed events support a RoutingStrategy of Bubble, Tunnel, or Direct. Let's take a look at the event handler of Button.Click:
private void Grid_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button Test clicked!");
}
There specified RoutedEventArgs, so it's routed event. Because the preview were not specified in the name, therefore this Bubble event. This can be demonstrated in the following way:
<Grid ButtonBase.Click="Grid_Click">
<Button Name="TestButton" Width="100" Height="30" Content="Test" />
</Grid>
When you click on the TestButton, the event is to rise above the Grid, and displays a message:
Button Test clicked!
Usefulness of Bubbling/Tunneling strategies
Tunneling
Many of the standard controls listen to events, such as KeyDown, MouseDown, etc. For example -DataGrid control. I want by pressing the enter key the function was called adding a record. But DataGrid already has KeyDown event, so the event is not raised. So you have to do your logic in the Tunnel event - PreviewKeyDown, it will work before the KeyDown event. The same applies to RichTextBoxControl.
Bubbling
Sometimes, you need a global handler for a specific event, so it worked for all controls in VisualTree. Naturally, the a direct event you can not do it. Hence on the stage comes Bubbling event.
Another reason is the ideology of the WPF. This Button can contain anything: Image, another Button, etc:
The user can click on the TextBlock/Image in the Button. How do we know that the click was in Button? That's right, with the help of Bubbling event.
For more information, please see:
Understanding Routed Events and Commands In WPF
Edit
I changed little bit a Click handler:
private void Grid_Click(object sender, RoutedEventArgs e)
{
String message = "#" + eventCounter.ToString() + ":\r\n" +
" Sender: " + sender.ToString() + ":\r\n" +
" Source: " + e.Source + ":\r\n" +
" Original Source: " + e.OriginalSource;
lstEvents.Items.Add(message);
}
Result of click on the Button:
Hi though you can get some good articles regading this on net but still I will try to answer this.
Suppose you give a button a very plain appearance consisting of a single Rectangle, and provide a simple piece of text as the content Even with such basic visuals, there are still two elements present: the text and the rectangle.The button should respond to a mouse click whether the mouse is over the text or the rectangle. In the standard .NET event handling model, this would mean registering a MouseLeftButtonUp event handler for both elements.
This problem would get much worse when taking advantage of WPF’s content
model. A Button is not restricted to having plain text as a caption—it can contain any
object as content. The xaml below is not especially ambitious, but even
this has six visible elements: the yellow outlined circle, the two dots for the eyes, the
curve for the mouth, the text, and the button background itself. Attaching event
handlers for every single element would be tedious and inefficient. To work MouseDown We would have to add 8 MouseDownEvents to this bit of code.
<Button PreviewMouseDown="PreviewMouseDownButton" MouseDown="MouseDownButton">
<Grid PreviewMouseDown="PreviewMouseDownGrid" MouseDown="MouseDownGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Canvas PreviewMouseDown="PreviewMouseDownCanvas" MouseDown="MouseDownCanvas" Width="20" Height="18" VerticalAlignment="Center">
<Ellipse PreviewMouseDown="PreviewMouseDownEllipse" MouseDown="MouseDownEllipse" x:Name="myEllipse" Canvas.Left="1" Canvas.Top="1" Width="16" Height="16" Fill="Yellow" Stroke="Black" />
<Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" MouseDown="MouseDownEllipse" Height="3" Fill="Black" />
<Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" MouseDown="MouseDownEllipse" Height="3" Fill="Black" />
<Path Data="M 5,10 A 3,3 0 0 0 13,10" Stroke="Black" MouseDown="Path_MouseDown_1"/>
</Canvas>
<TextBlock Grid.Column="1" MouseDown="TextBlock_MouseDown_1">Click!</TextBlock>
</Grid>
</Button>
WPF uses RoutedEvents Bubble/Tunnel /Normal, which are rather more thorough than normal events. Instead
of just calling handlers attached to the element that raised the event, WPF walks the
tree of user interface elements, calling all handlers for the routed event attached to
any node from the originating element right up to the root of the user interface tree.
As you said you know the concept of bubbling/tunneling so not going into that. But this is what these events are intended for i.e they let you know if the Mouse button was Down or Up on the control or its children.Events are working fine.For your scenario you should use the Click event of your button which tell if the mouse was down and up on the button itself.
Thanks
I have a class derived from ItemsControl in which I implement my own selection-algorithm which uses the MouseLeftButtonDown to change the selection.
Now I needed a specific control to handle Mouseclicks in the ViewModel, so I wrote the following:
<controls:DraggableItemsContainer bla="blub">
<controls:DraggableItemsContainer.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding DeselectSubGroupsCommand}" />
</controls:DraggableItemsContainer.InputBindings>
</controls:DraggableItemsContainer>
What happens now is, that I don't get the MouseLeftButtonDown-event anymore - which is comprehensable because the command "e.handles" the click.
But in this case, that's not what I want. Is there a way to fire the event anyway?
PS: Yes I need to do the selection in the MouseLeftButtonDown-event and not in any Preview-event
Two options:
You can have your custom ItemsControl hook PreviewMouseDown instead of MouseDown.
You can continue to hook MouseDown, but do it by calling AddHandler, and pass true for the handledEventsToo parameter.
If it's important that you get notified after the MouseBinding has done its work, then you need to use AddHandler. If you're okay with getting the message first, PreviewMouseDown is simpler.
I think your issue is that you try to catch the same event, on the same control, using two different approaches.
Try this:
<Grid>
<Grid.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding DeselectSubGroupsCommand}" />
</Grid.InputBindings>
<controls:DraggableItemsContainer bla="blub">
.....
</controls:DraggableItemsContainer>
</Grid>
Make sure that in your control you have e.Handled = false.
This should allow for your internal logic to run, and then execute the command. If you need it in the opposite order..... I don't know.
I have a WPF application that I am working on where there is a button that is obscured by a partially opaque rectangle overlay. The button is still visible, but it can not receive any events because they are all caught by the rectangle which is on top of it.
Is there any way to set a pass-through so that the event is received by the next visual item underneath? If not is there some other workaround that could be used in this situation?
Set IsHitTestVisible="false" on the opaque overlay.
You need to set IsHitTestVisible="False" for the control over your button.
This example shows that a button is covered by a border, but the border doesn't get any event since because of the IsHitTestVisible="False" condition of border:
<Grid Background="Yellow">
<Button Click="Button_Click" Width="100" Height="25"/>
<Border Background="Cyan" Opacity="0.4" Width="200" Height="200" IsHitTestVisible="False" />
</Grid>
C# code,
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("iiiiiii");
}
You could add the your own event to the rectangle event (or in the click event of the rectangle it self) and check there if it's with in buttons area
Rectangle.Click += your_click_event;
private void your_click_event(object sender, RoutedEventArgs e)
{
//check if it's coordinates are within the underlining button.
//fire button click event
}
But it would be more convient to set
IsHitTestVisible="False"
Like mentioned in other posts. UIElement.IsHitTestVisible Property
I would recommend to look in to routed events in WPF. Routed events get routed based primarily on the visual tree. Routed events support a RoutingStrategy of Bubble, Tunnel, or Direct.
Understanding Routed Events and Commands In WPF