I have a slider that represents position in an audio file. The user requirement is to automatically play when the user double clicks on the slider.
When I use this:
<Slider.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding PlayCommand}" />
</Slider.InputBindings>
The Play Command is not called. However, if I use this:
<Slider.InputBindings>
<MouseBinding Gesture="RightClick" Command="{Binding PlayCommand}" />
</Slider.InputBindings>
or this
<Slider HorizontalAlignment="Stretch" (...) MouseDoubleClick="slider1_MouseDoubleClick" >
</Slider>
The play command is called.
What prevents the use of the gesture with the double click, yet allows the right click gesture or the double click event to be called?
OK, I think I've figured out what's happening here:
The RepeatButton in the Track is intercepting the MouseLeftButtonDown event and setting Handled = true. Note that the MouseLeftButtonDown event is an off-shoot of MouseDown, so if you set Handled for one, you're really setting it for both. In this case, the mouse-down events are being handled when ClickCount = 1, and the act of marking the events as Handled prevents those events' handlers from being invoked on the second click (i.e., when ClickCount = 2). Input gestures are evaluated on the MouseDown event, so suppressing the MouseDown handlers for the second click means the Slider won't get probed for input bindings, and commands with double-click gestures will never get a chance to execute.
So, if the mouse-down handlers don't get invoked during the second click, why do the MouseDoubleClick handlers get invoked? Well, it turns out that MouseDoubleClick originates from a class-level handler in Control, and that handler gets invoked even when the original mouse-down event(s) were marked as Handled. Note the last argument to RegisterClassHandler below:
EventManager.RegisterClassHandler(
typeof(Control),
UIElement.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(HandleDoubleClick),
/* handledEventsToo: */ true);
And thus the MouseDoubleClick event gets raised even though the LeftDoubleClick input binding never got a chance to execute. This is perhaps not the most intuitive behavior, and I'm unsure whether it was a conscious design decision or a simple oversight.
Related
I have realized that the following XAML no longer works.
I have even attempted to swap the command property for a Tapped property.
Yet, the method handler for the tapped event still does not get called.
It is when I move the appbarbutton outside of the commandbar scope, that the Tapped event gets triggered and handled in the code-behind.
<Page.BottomAppBar>
<CommandBar>
<CommandBar.PrimaryCommands>
<AppBarButton Icon="Phone" Label="Call" Command="{Binding CallCommand, Mode=TwoWay}" />
</CommandBar.PrimaryCommands>
</CommandBar>
</Page.BottomAppBar>
However, if I place the appbarcommand outside of the commandbar and replace the command binding with a Tapped event method handler in the code-behind then the event gets triggered.
How does one ignore changes to a control when databinding occurs? I tried hooking various events like gotfocus,textchanged,and leavefocus, but if the control already has focus and the user "cancels" their changes, when I reload the record and data binding takes over, textchanged thinks the user still made the change since the focus is on that control. The call stack is empty. Are there any global data binding events like databinding starting and databinding ending? I see where I fire my OnProperyChanged but within that call, databinding does not occur. Looks like it's getting "queued" up and runs at some other point.
At one point, I was going to hook the property change events in our view model , but this means I won't detect and can't VISUALLY display the form is modified till the user leaves the control. I know, I know, I can change all my bindings so that binding occurs immediately on every character change but then this messes with some validation cases as the user hasn't finished typing in their value.
I'd really love some kind of event like TextChangedByUser that would fire whether the user used a key, clipboard, mouse clipboard, anything triggered by the user.
I just can't figure out how to distinguish between user changes and databinding changes.
I'd really love some kind of event like TextChangedByUser that would
fire whether the user used a key, clipboard, mouse clipboard, anything
triggered by the user.
I just can't figure out how to distinguish between user changes and
databinding changes.
Don't use the Text.TextChanged event to detect user input,
use the Binding.SourceUpdated event instead.
Or more general:
Don't use the DPs of your visual elements to detect user updates, use the Binding.SourceUpdated event instead.
This is a RoutedEvent.
At your binding, you have to set NotifyOnSourceUpdated = true. With help of UpdateSourceTrigger you are even able to finetune when you want to be informed.
Your xaml could be sth like this:
<Grid x:Name="LayoutRoot" Binding.SourceUpdated="LayoutRoot_SourceUpdated">
...
<TextBox>
<TextBox.Text>
<Binding NotifyOnSourceUpdated="True" Path="path" UpdateSourceTrigger="PropertyChanged" >
</Binding>
</TextBox.Text>
</Grid>
Your event could be like this:
private void LayoutRoot_SourceUpdated(object sender, DataTransferEventArgs e)
{
// called every time s.th. changed by user
}
(edited due to comment)
Why is this a valid way to detect if an input is triggered in any way by the user?
In the given example, the TextBox's DataContext 'path' property is the source, while the 'TextBox.Text' property is the target.
[Data Binding Overview]http://msdn.microsoft.com/en-us/library/ms752347.aspx
The TextBox.Text property is changed for the first time when the binding initializes and the source-value is written to the 'TextBox.Text' property. Because you do not know when the binding takes place exactly you cannot use the TextBox.Text property or any of its events (e.g. TextChanged) to detect a user input. Hence:
Don't use the Text.TextChanged event to detect user input!!! more general: Don't use the DPs of your visual elements to detect user updates!!!
If the user changes the content of the visual text field by which means whatsoever, the 'TextBox.Text' property changes (your target).After that, the binding updates the source at a time defined by UpdateSourceTrigger.That's when the SourceUpdated event is fired.
I admit not to know the effect of changes to the binding source from outside the binding.
But I have a complete Editor-like Desktop-Application detecting changes by the user that way and it is working very nicely.
You should update your Binding Code to set the following
{Binding Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
EDIT: Sorry, I have overseen the fact that you already know this...In that case, I can't help :(
You can use the UIElement.TextInput event to detect user input.
Note that the event is probably already handled by the input control itself so you might have to use the UIElement.PreviewTextInput event.
I realised that when using an InvokeCommandAcction associated to an EventTrigger, the original event was still routing up to the parent elements until it is handled. Well, I guess it is an expected behavior. But my question is how I can mark the event as Handled so it does not propagate up through the whole UI tree?
Actually, as you handle this event in a command, everything will be handled in this command, therefore it does not need to propagate. And in one corner case I found, it causes some unwanted behavior. For example, I open a new window when a user double click an element (MouseDoubleClick event). The problem is that the new windows opens and then the main window come back in front of the new one because the MouseDoubleClick event just reached the top element in the UI tree. The wanted behavior would be to keep the new window in front, but as the InvokeCommandAction lets the event propagate up, the main window takes back the focus...
What I could do is to use the CallMethodAction asset instead but as I am in a MVVM scenario, I don't want UI event arguments in my code. Even if this would let me implicitely mark the event as handled and fix the issue.
<UserControl x:Class="..."
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding Path=DisplayReportCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
...
</UserControl>
You could implement your own EventTrigger that marks events as handled.
public class HandlingEventTrigger : System.Windows.Interactivity.EventTrigger
{
protected override void OnEvent(System.EventArgs eventArgs)
{
var routedEventArgs = eventArgs as RoutedEventArgs;
if (routedEventArgs != null)
routedEventArgs.Handled = true;
base.OnEvent(eventArgs);
}
}
Then replace <i:EventTrigger EventName="MouseDoubleClick"> with <local:HandlingEventTrigger EventName="MouseDoubleClick"> and add
xmlns:local="clr-namespace:HandlingEventTrigger's namespace here"
to your usercontrol's atributes.
Add attached event to user control
CommandManager.PreviewCanExecute="PreviewCanExecute"
and in event handler
e.ContinueRouting = false;
Hope this will help!
MouseDoubleClick Event is actually not a bubbling routed event but a direct routed event.
However, this event is raised along the element tree, which can be checked with Snoop tool. Moreover, even if Handled for MouseDoubleClick is set to true, this event will occur along the element tree.
Although this routed event(MouseDoubleClick 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. (From MSDN)
As above, your problem may be not caused by the propagating as you mentioned. There is Window.ShowActivated property, which determines whether a window is activated when first shown. You can set the property in a sub window(xaml) as below but please note that though ShowActivated can give the focus to the main window, it cannot let the main window visually keep in front of the sub window. I have tried to find the solution but have no idea until now.
<Window ShowActivated="False" ....>
....
</Window>
I am using the method described here to attach a ViewModel ICommand to the LostFocus event of a Combobox, by setting CommandBehavior.RoutedEventName="LostFocus". I expected the event to fire at the same time the binding for UpdateSourceTrigger=LostFocus fired, but this turns out not to be the case.
The selecteditem Binding UpdateSourceTrigger=LostFocus fires whenever the keyboard tabs away, or after the user actually selects an item from the dropdown by clicking (not sure why this causes lostfocus, but at least it fires AFTER a selection is made).
The attached behavior event fires anytime the user clicks on the Combobox. Immediately. If using the keyboard it behaves normally, firing when you tab away from it. However, when using the mouse, the event fires when the control GAINS focus, before the user has even made a selection. Is there any way to make this behave like lostfocus does for the selecteditem?
Edit: I am curious if another answer exists, but I found a way around this problem, by setting up an additional binding. SelectedItem updates by defualt, handling the normal property change notifications, and selectedvalue updates on lostfocus, handling only the command I was trying to run. Binding looks like this:
SelectedItem="{Binding Path=SelectedCustomer, Mode=TwoWay}"
SelectedValuePath="CM_CUSTOMER_ID"
SelectedValue="{Binding Path=CustomerLostFocus, UpdateSourceTrigger=LostFocus}"
You would need to check the OriginalSource of the event arguments for the LostFocus event:
The LostFocus event is a bubbling event. This means that if multiple
LostFocus event handlers are registered for a sequence of objects
connected by parent-child relationships in the object tree, the event
is received by each object in that relationship. The bubbling metaphor
indicates that the event starts at the object that directly receives
the input condition, and works its way up the object tree. For a
bubbling event, the sender available to the event handler identifies
the object where the event is handled, not necessarily the object that
actually received the input condition that initiated the event. To get
the object that initiated the event, use the OriginalSource value of
the event's RoutedEventArgs event data.
So for the ComboBox, you may receive events for the various focusable elements inside the ComboBox.
I'm trying to write an event handler that fires every time a node in a TreeView gets the focus. The problem I'm running into is that the event handler fires on the TreeViewItem (node) that I click on with the mouse, and then it continues to bubble up the control tree, even though I've set e.Handled = true on the RoutedEventArgs provided to the handler. Does anybody have an idea what the problem could be ? I've double checked my code and I can see no reason why this should be happening.
Are you using TreeView.GotFocus when you really want TreeViewItem.Selected?
<TreeView TreeViewItem.Selected="treeView1_Selected" />
If you really want focus, use TreeViewItem.Focus instead so that items are targeted instead of the whole tree.
<TreeView TreeViewItem.GotFocus="treeView1_GotFocus"/>