WPF ListView and ScrollViewer hide MouseLeftButtonDown - wpf

To demostrate the problem I have this Xaml:
<DockPanel MouseLeftButtonDown="DockPanel_MouseLeftButtonDown" MouseLeftButtonUp="DockPanel_MouseLeftButtonUp">
<ListView>
<ListViewItem>ListViewItem</ListViewItem>
</ListView>
<TextBlock>TextBlock</TextBlock>
</DockPanel>
and the event handlers are :
private void DockPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("DockPanel_MouseLeftButtonDown");
}
private void DockPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("DockPanel_MouseLeftButtonUp");
}
When you run the app and click on the words TextBlock you get MouseDown fired followed by MouseUp. So far so good. But when you click on the words ListViewItem only MouseUp is fired. Same problem for ScrollViewer (List view includes it so I am guessing it's the same problem).
Does anybody know why and if this can be fixed.
By fixed I mean get it to fire not try to use another event or another mechanism all together.

First the problem:
As suspected the problem is in ScrollViewer: http://referencesource.microsoft.com/#PresentationFramework/Framework/System/Windows/Controls/ScrollViewer.cs,488ab4a977a015eb
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (Focus())
e.Handled = true;
base.OnMouseLeftButtonDown(e);
}
As you can see it sets MouseButtonEventArgs.Handled to true which stops the bubbling of the event.
Now the solution - it is in the way you add the handler:
MyListView.AddHandler(
ListView.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(ListView_MouseLeftButtonDown),
true);
Note the last parameter (true) it causes the handler to be invoked even if the EventArgs.Hanlded was set to true.
Then you can reset it:
private void ListView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = false;
}

I had somewhat similar situation when ScrollViewer was blocking my MouseLeftButtonDown event. I had a content control wrapped into ScrollViewer:
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ContentControl x:Name="Details" />
</ScrollViewer>
and this was inside of Popup which had a drag/drop behavior. So, because my behavior was not receiving this event, it did not work. When I added IsHitTestVisible="True" to ScrollViewer, my behavior started to work, but of course my ContentControl was not responding to any clicks. Then I saw this:
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (Focus())
e.Handled = true;
base.OnMouseLeftButtonDown(e);
}
and tried to add Focusable="False" to exclude ScrollViewer from my click - it works. My behavior works and controls inside of ContentControl are getting all mouse events.
<ScrollViewer VerticalScrollBarVisibility="Auto" Focusable="False">
<ContentControl x:Name="Details" />
</ScrollViewer>
Hope it will help somebody.

Related

Why does the Handled property in PreviewMouseLeftButtonDownEvent affect a ClickEvent?

Consider you add a ClickEvent- and PreviewMouseLeftButtonDown-Handler for a Button
<Button x:Name="button"
Click="Button_Click"
PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown">
</Button>
When clicking the Button, first PreviewMouseLeftButtonDown is fired, then the Click-Event.
If you set e.Handled = true in the Preview...-Event, the Click-Event is not handled any more.
However, now let's think of the MouseLeftButtonDownEvent.
First, this event's routing strategy is direct. That is, it is re-raised for every control. In contrast, the Preview...-Event is tunneling, the Click-Event is bubbling.
Second, adding a MouseLeftButtonDownEventHandler is only successful when registering the handler such that it is even invoked for already handled events, as shown in the following code excerpt.
button.AddHandler(MouseLeftButtonDownEvent,
new MouseButtonEventHandler(Button_MouseLeftButtonDown),
true);
I've written a test application, having a button, and added a handler for each of the Events. When an event handler is invoked, it writes some information into a text block.
When I click the button, all three event handlers are invoked.
When I add e.Handled = true to the Preview...-EventHandler, only this event handler is invoked. Even the Mouse...-EventHandler is not raised, although I've set UIElement.AddHandler handledEventsToo to true.
When I add e.Handled = true to the Mouse...-EventHandler, all three event handlers are invoked.
That does not make any sense to me. Mouse...-EventHandlers do not affect the Click-EventHandlers, but Preview...-EventHandlers affect both Mouse...- and Click-EventHandlers.
And even 'forcing' to handle an event failed for the Mouse...-EventHandler.
Actually, I've never thought that event handlers of different types could affect each others. What I understood is that if I've got a Preview...-Event and a Click-Event, that these are independent.
So, what am I missing?
Here's the pretty simple sample code:
XAML:
<DockPanel>
<Border x:Name="border" DockPanel.Dock="Top" Height="50"
BorderBrush="Gray" BorderThickness="1">
<StackPanel x:Name="stackpanel" Background="LightGray"
Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="button" Width="Auto"
PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown">
Click Me
</Button>
</StackPanel>
</Border>
<Border DockPanel.Dock="Bottom" BorderBrush="Gray" BorderThickness="1">
<ScrollViewer>
<TextBlock x:Name="textBlock" TextWrapping="Wrap"/>
</ScrollViewer>
</Border>
</DockPanel>
Code-Behind:
public MainWindow()
{
InitializeComponent();
button.AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(Button_MouseLeftButtonDown), true);
button.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click), true);
stackpanel.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click), true /*false*/ );
}
private void Output(object sender, RoutedEventArgs e)
{
textBlock.Text += "RoutedEvent: " + e.RoutedEvent + "\n";
textBlock.Text += "Sender: " + sender + "\n";
textBlock.Text += "Source: " + e.Source + "\n";
textBlock.Text += "OriginalSource: " + e.OriginalSource + "\n" + "\n";
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// e.Handled = true;
Output(sender, e);
}
private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// e.Handled = true;
Output(sender, e);
}
private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Output(sender, e);
}
I've never thought that event handlers of different types could affect each other
You are mostly correct, as this is quite rare, but you can find your answers in the Preview Events page on MSDN. From the linked page:
For instance, a Windows Presentation Foundation (WPF) Button suppresses MouseLeftButtonDown and MouseLeftButtonDown bubbling events raised by the Button or its composite elements in favor of capturing the mouse and raising a Click event that is always raised by the Button itself. The event and its data still continue along the route, but because the Button marks the event data as Handled, only handlers for the event that specifically indicated they should act in the handledEventsToo case are invoked.
Furthermore, you said this:
When I add e.Handled = true to the Mouse...-EventHandler, all three event handlers are invoked
That is expected, as setting e.Handled in a bubbling event handler will do nothing... there is nothing that will read that value after the event has left the handler code. e.Handled is predominantly used in tunnelling event handlers to stop the events from any further routing. Again, from the linked page:
For input events specifically, Preview events also share event data instances with the equivalent bubbling event. If you use a Preview event class handler to mark the input event handled, the bubbling input event class handler will not be invoked. Or, if you use a Preview event instance handler to mark the event handled, handlers for the bubbling event will not typically be invoked.

Prevent hyperlink Click event from bubbling up

I'm designing a windows Phone app. I have a Hyperlink object in a RichTextBox, in a Grid. The Grid had a Tap event, and the Hyperlink has a Click event.
Clicking the Hyperlink also raises the parent Grid's Tap event. How can I prevent this?
I would use e.Handled in the Click handler, but RoutedEventArgs do not have a Handled property in Silverlight for Windows Phone... I also tried walking the logical tree to look for the original source, but the click appears to originate from a MS.Internal.RichTextBoxView control (e.OriginalSource)...
I don't think there is any good way from within the Click handler itself. The following state management can work though:
XAML:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" Tap="ContentPanel_Tap_1">
<RichTextBox Tap="RichTextBox_Tap_1">
<Paragraph>
fdsfdfdf
<Hyperlink Click="Hyperlink_Click_1">fdsfdsfsaf</Hyperlink>
fsdfsdfa
</Paragraph>
</RichTextBox>
</Grid>
and the code:
bool RtbTapHandled = false;
private void Hyperlink_Click_1(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Hyperlink");
RtbTapHandled = true;
}
private void RichTextBox_Tap_1(object sender, System.Windows.Input.GestureEventArgs e)
{
if (RtbTapHandled)
{
e.Handled = true;
}
RtbTapHandled = false;
System.Diagnostics.Debug.WriteLine("RTB_Tap");
}
private void ContentPanel_Tap_1(object sender, System.Windows.Input.GestureEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Content_Tap");
}
In this case if you click on the RichTextBox you'll get callbacks from both RichTextBox_Tap_1 and ContentPanel_Tap_1, but if you click on the Hyperlink you'll get Hyperlink_Click_1 and RichTextBox_Tap_1 though it'll be handled at that level and stopped.

Set the focus on a textbox in xaml wpf

Despite some posts on this forum and others i cannot find something that tells me how to set the focus on a TextBox.
I have a userControl with many labels and textBoxes. When the form is loaded I want the a particular textBox to have the focus.
I have set the tabIndex but that didn't seem to work.
Any suggestions?
You can use the FocusManager.FocusedElement attached property for this purpose. Here's a piece of code that set the focus to TxtB by default.
<StackPanel Orientation="Vertical" FocusManager.FocusedElement="{Binding ElementName=TxtB}">
<TextBox x:Name="TxtA" Text="A" />
<TextBox x:Name="TxtB" Text="B" />
</StackPanel>
You can also use TxtB.Focus() in your code-behind if you don't want to do this in XAML.
You can apply this property directly on the TextBox :
<TextBox Text="{Binding MyText}" FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"/>
I am new to using WPF and reading through the above examples I had a similar experience trying set the focus to a textbox using the xaml code examples given, i.e. all the examples above didn't work.
What I found was I had to place the FocusManager.FocusElement in the page element. I assume this would probably work as well if you used a Window as the parent element. Anyway, here is the code that worked for me.
<Page x:Class="NameOfYourClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="Title"
Height="720"
Width="915"
Background="white"
Loaded="pgLoaded"
FocusManager.FocusedElement="{Binding ElementName=NameOfYourTextBox}">
<!-- Create child elements here. -->
</Page>
I have a TextBox inside a Grid inside a DataTemplate which I want to have keyboard focus when it becomes visible. I also found that
<DataTemplate x:Key="DistanceView" DataType="{x:Type vm:ROI}">
<Grid FocusManager.FocusedElement="{Binding ElementName=tbDistance}">
<TextBox x:Name="tbDistance" Grid.Column="1" Grid.Row="1" VerticalAlignment="Bottom"/>
</Grid>
</DataTemplate>
did not work for me.
However when I call Focus() in the parent ContentControl
private void ContentControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((sender as ContentControl).IsVisible)
{
(sender as ContentControl).Focus();
}
}
it starts to work and the caret is visible in the TextBox. I think the FocusScope has to be given focus for the FocusManager.FocusedElement property to have any effect.
Jerry
From experimenting around, the xaml solution
FocusManager.FocusedElement="{Binding ElementName=yourElement}"
seems to work best when you place it in the highest element in the window hierarchy (usually Window, or the Grid you place everything else in)
Usage:
local:FocusManager.FocusOnLoad="True"
public class FocusManager
{
public static readonly DependencyProperty FocusOnLoad = DependencyProperty.RegisterAttached(
"FocusOnLoad",
typeof(bool),
typeof(FocusManager),
new UIPropertyMetadata(false, new PropertyChangedCallback(OnValueChanged))
);
private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (!(sender is Control control))
return;
if ((bool) e.NewValue == false)
return;
control.Loaded += (s, e) => control.Focus();
}
public static bool GetFocusOnLoad(DependencyObject d) => (bool) d.GetValue(FocusOnLoad);
public static void SetFocusOnLoad(DependencyObject d, bool value) => d.SetValue(FocusOnLoad, value);
}
FocusManager was not in intellisense and this confused me a bit. I just typed the entire attribute and it worked.
FocusManager.FocusedElement="{Binding ElementName=MyTextBox}"
Microsoft Visual Studio Enterprise 2015 version 14.0.23107.0/C#/WPF
For completeness, there is also a way to handle this from code behind (e.g. in the case of controls that, for whatever reason, are created dynamically and don't exist in XAML). Attach a handler to the window's Loaded event and then use the ".Focus()" method of the control you want. Bare-bones example below.
public class MyWindow
{
private VisualCollection controls;
private TextBox textBox;
// constructor
public MyWindow()
{
controls = new VisualCollection(this);
textBox = new TextBox();
controls.Add(textBox);
Loaded += window_Loaded;
}
private void window_Loaded(object sender, RoutedEventArgs e)
{
textBox.Focus();
}
}
bind the element you want to point the focus in as
FocusManager.FocusedElement= "{Binding ElementName= Comobox1}"
in grid or groupbox etc
Further to my comment on Feb 04 '22, I solved it this way:
In the UserControl definitionin the XAML add a Loaded event handler. (pressing tab after Loaded= will automatically add an event handler to the code behind)
Then edit the event handler in the code behind:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
expressionTextBox.Focus();
}
I'm hoping that WPF is clever enough to handle th unhooking of the evnt at some point, allowing the class to be garbage collected and not give rise to memory leaks, but I don't know. I'd be interested in any comments on that.

WPF- Is it possible to add an OnVerticalOffsetChanged event to a custom textbox?

Is there any way for me to do this?
You can tell when the VerticalOffset changes by adding a handler to the ScrollViewer.ScrollChanged event to your TextBox. Something like this:
<TextBox AcceptsReturn="True" ScrollViewer.ScrollChanged="TextBox_ScrollChanged" />
The TextBox uses a ScrollViewer internally, so it's ScrollChanged event will bubble up to the TextBox (where you can handle it). The event arguments include information about what changed, such as the VerticalChange (the amount that the control has scrolled vertically).
private void TextBox_ScrollChanged(object sender, ScrollChangedEventArgs e) {
System.Diagnostics.Debug.WriteLine(string.Format("************ {0}", e.VerticalChange));
}

How to set focus on TextBox in Silverlight 4 out-of-browser popup

I have a simple ChildWindow popup in Silverlight 4 (beta).
Important: This is an out-of-browser application.
i want to auto set focus on a TextBox control when the window opens.
I've tried a couple things :
The following code doesn't seem to do anything. I don't think the control is ready to be focussed after 'Loading'.
private void ChildWindow_Loaded(object sender, RoutedEventArgs e)
{
textBox1.Focus();
}
This works, but its klunky.
private void ChildWindow_GotFocus(object sender, RoutedEventArgs e)
{
if (_firstTime == true) {
textBox1.Focus();
_firstTime = false;
}
}
Isn't there a better way? I always had to do horrible things like this in WinForms but was hoping not to have to anymore.
Note: This similar question is for in browser only. It suggests calling System.Windows.Browser.HtmlPage.Plugin.Focus(); which doesn't work and in fact gives an error when running on Silverlight 4 beta out-of-browser.
I was having the same problem in SilverLight 4 (OOB) and I noticed that the tab sequence would set focus to a control that i could not see. What appears to be happening is the focus is being set to your control (first one in the tab sequence) and then for some reason the focus moves to the ContentControl (name ="content"), which (i think) is the parent of the child window.
ContentControl by default has IsTabStop=true.
see....
Why would I want IsTabStop set to true on a ContentControl?
To set the ContentControl.IsTabStop = false for all ContentControls in your app, add this to your styles.xaml.
<Style TargetType="ContentControl" >
<Setter Property="IsTabStop" Value="false"/>
</Style>
The same issue happens with the tab sequence on the MainPage. This style will also fix this.
You are on the right track. You need to handle for two test cases:
1. Setting the focus in the browser.
2. Setting the focus out of the browser.
Your code you that you showed in the Loaded event will work perfectly fine out of the browser. All that is necessary is to refactor it to handle both cases:
private void ChildWindow_Loaded(object sender, RoutedEventArgs e)
{
if (App.current.IsRunningOutOfBrowser)
{
textBox1.Focus();
}
else
{
System.Windows.Browser.HtmlPage.Plugin.Focus();
textBox1.Focus();
}
}
That should do the trick for you.
Thanks for all the posts but after doing a little research the below thing work for me
in Xamal:
<TextBox VerticalAlignment="Center" HorizontalAlignment="Left" FontFamily="Arial" FontSize="12" Height="25" Width="200" Margin="38,50,0,0" Name="txtUserName" Text="{Binding LoginInfo.UserName,Mode=TwoWay, NotifyOnValidationError=True}" IsTabStop="True" TabIndex="1" ></TextBox>
// Initialiazing Main Part View Model
/// </summary>
/// <param name="mainPartViewModel"></param>
public ChildWindowLoginControl(MainPartViewModel mainPartViewModel)
{
InitializeComponent();
this.DataContext = mainPartViewModel;
System.Windows.Browser.HtmlPage.Plugin.Focus();
this.GotFocus += (s, e) => { txtUserName.Focus(); };
}
I had to use your GotFocus way for Silverlight 3 application written in IronPython when I wanted to set focus in ChildWindow.
I use:
protected override void OnOpened()
{
base.OnOpened();
textBox1.Focus();
}
Thanks for all the post, but i have find the work done through following.
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (App.Current.IsRunningOutOfBrowser)
{
txtSalesOrderNo.Focus();
}
else
{
System.Windows.Browser.HtmlPage.Plugin.Focus();
txtSalesOrderNo.Focus();
}
}

Resources