WPF touch event in screen not working with UserControl - wpf

We are developing a WPF application. We use .net4.7 but we have tested it with .net5 and our problem persists.
We have some different elements (buttons, input text...) and sometimes they have to show a "popup" (as a messagebox).
So, our problem happens when we have a list item (listview from windows control) and we want to show a MessageBox; if we do the same thing with a button instead of a list item, it works.
If we manage the MessageBox with the mouse, everything works perfectly, but it we manage with our touch screen, we need to click 10 times to get it working.
This is our list in xml (it comes from a template).
<lists:VehicleList Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3"
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:StepSelect}}}"/>
This is what we show through the ViewModel with result yes/no and which buttons we must click 10 times with the touch screen:
MessageBox.ShowDialog(InfoMessages.driverContinue, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { NavigateForward(this, res);
Any idea?
Thanks in advance :)
PS: We use Windows 10.
Edited:
Found that the problem occurs when I do MessageBox.ShowDialog(whatever); but it does not happen when I do MessageBox.Show(whatever);
Any idea?

The problem was caused because we missed in the list the following code:
private void ListView_PreviewTouchDown(object sender, System.Windows.Input.TouchEventArgs e)
{
e.Handled = true;
}

Related

WPF take screenshot of application including hidden content

Under the category "limitations of the technology":
I have received the requirement to have a screenshot button in my application that will take a screenshot and launch a printer dialog. Fair enough. My code achieves that. I simply take my window, and use a RenderTargetBitmap to render the window.
However, the requirement now states that it should include all content that is hidden behind scrollbars. Meaning, that in the screenshot the application should look "stretched" in order to eliminate scrollbars, and show all data. For instance in case there is a large list or datagrid, all the data should be visible.
Keeping in mind that WPF might be virtualizing and not rendering things that are not in view, is there any way I can achieve this requirement? Is there a possibility of rendering the visual tree to a seperate infinite space and taking a screenshot there? Something else?
In response to comments:
The screenshot button is on an outer shell that only holds the menu. Inside this shell any of 800+ views can be hosted. These views could contain datagrids, lists, large forms consisting of textboxes... anything. There is no way to tell what is 'inside' without walking the visual tree.
The functionality requested is similar to printing a webpage in your browser to PDF. It will also give you the entire DOM instead of just what you see in the limited view of the browser.
XAML:
<Grid>
<Button
x:Name="btnPrint"
Width="50"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Click="BtnPrint_Click"
Content="Print" />
<ScrollViewer Height="500" HorizontalAlignment="Center">
<Grid x:Name="toPrint">
<!--your code goes here-->
</Grid>
</ScrollViewer>
</Grid>
C#:
private void BtnPrint_Click(object sender, RoutedEventArgs e)
{
var pdialog = new PrintDialog();
if (pdialog.ShowDialog() == true)
{
System.Windows.Size pageSize = new System.Windows.Size { Height = pdialog.PrintableAreaHeight, Width = pdialog.PrintableAreaWidth };
toPrint.Measure(pageSize);
toPrint.UpdateLayout();
pdialog.PrintVisual(toPrint, "Print");
}
}

WebBrowser control keyboard and focus behavior

Apparently, there are some serious keyboard and focus issues with WPF WebBrowser control. I've put together a trivial WPF app, just a WebBrowser and two buttons. The app loads a very basic editable HTML markup (<body contentEditable='true'>some text</body>) and demonstrates the following:
Tabbing is misbehaving. User needs to hit Tab twice to see the caret (text cursor) inside WebBrowser and be able to type.
When user switches away from the app (e.g., with Alt-Tab), then goes back, the caret is gone and she is unable to type at all. A physical mouse click into the WebBrowser's window client area is required to get back the caret and keystrokes.
Inconsistently, a dotted focus rectangle shows up around WebBrowser (when tabbing, but not when clicking). I could not find a way to get rid of it (FocusVisualStyle="{x:Null}" does not help).
Internally, WebBrowser never receives the focus. That's true for both logical focus (FocusManager) and input focus (Keyboard). The Keyboard.GotKeyboardFocusEvent and FocusManager.GotFocusEvent events never get fired for WebBrowser (although they both do for buttons in the same focus scope). Even when the caret is inside WebBrowser, FocusManager.GetFocusedElement(mainWindow) points to a previously focused element (a button) and Keyboard.FocusedElement is null. At the same time, ((IKeyboardInputSink)this.webBrowser).HasFocusWithin() returns true.
I'd say, such behaviour is almost too dysfunctional to be true, but that's how it works. I could probably come up with some hacks to fix it and bring it in row with native WPF controls like TextBox. Still I hope, maybe I'm missing something obscure yet simple here. Has anyone dealt with a similar problem? Any suggestions on how to fix this would be greatly appreciated.
At this point, I'm inclined to develop an in-house WPF wrapper for WebBrowser ActiveX Control, based upon HwndHost. We are also considering other alternatives to WebBrowser, such as Chromium Embedded Framework (CEF).
The VS2012 project can be downloaded from here in case someone wants to play with it.
This is XAML:
<Window x:Class="WpfWebBrowserTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="640" Height="480" Background="LightGray">
<StackPanel Margin="20,20,20,20">
<ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
<WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="300"/>
<Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>
</Window>
This is C# code, it has a bunch of diagnostic traces to show how focus/keyboard events are routed and where the focus is:
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Navigation;
namespace WpfWebBrowserTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// watch these events for diagnostics
EventManager.RegisterClassHandler(typeof(UIElement), Keyboard.PreviewKeyDownEvent, new KeyEventHandler(MainWindow_PreviewKeyDown));
EventManager.RegisterClassHandler(typeof(UIElement), Keyboard.GotKeyboardFocusEvent, new KeyboardFocusChangedEventHandler(MainWindow_GotKeyboardFocus));
EventManager.RegisterClassHandler(typeof(UIElement), FocusManager.GotFocusEvent, new RoutedEventHandler(MainWindow_GotFocus));
}
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
// load the browser
this.webBrowser.NavigateToString("<body contentEditable='true' onload='focus()'>Line 1<br>Line 3<br>Line 3<br></body>");
this.btnLoad.IsChecked = true;
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
// close the form
if (MessageBox.Show("Close it?", this.Title, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
this.Close();
}
// Diagnostic events
void MainWindow_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Debug.Print("{0}, source: {1}, {2}", FormatMethodName(), FormatType(e.Source), FormatFocused());
}
void MainWindow_GotFocus(object sender, RoutedEventArgs e)
{
Debug.Print("{0}, source: {1}, {2}", FormatMethodName(), FormatType(e.Source), FormatFocused());
}
void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
Debug.Print("{0}, key: {1}, source: {2}, {3}", FormatMethodName(), e.Key.ToString(), FormatType(e.Source), FormatFocused());
}
// Debug output formatting helpers
string FormatFocused()
{
// show current focus and keyboard focus
return String.Format("Focus: {0}, Keyboard focus: {1}, webBrowser.HasFocusWithin: {2}",
FormatType(FocusManager.GetFocusedElement(this)),
FormatType(Keyboard.FocusedElement),
((System.Windows.Interop.IKeyboardInputSink)this.webBrowser).HasFocusWithin());
}
string FormatType(object p)
{
string result = p != null ? String.Concat('*', p.GetType().Name, '*') : "null";
if (p == this.webBrowser )
result += "!!";
return result;
}
static string FormatMethodName()
{
return new StackTrace(true).GetFrame(1).GetMethod().Name;
}
}
}
[UPDATE] The situation doesn't get better if I host WinForms WebBrowser (in place of, or side-by-side with WPF WebBrowser):
<StackPanel Margin="20,20,20,20">
<ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
<WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
<WindowsFormsHost Name="wfHost" Focusable="True" Height="150" Margin="10,10,10,10">
<wf:WebBrowser x:Name="wfWebBrowser" />
</WindowsFormsHost>
<Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>
The only improvement is that I do see focus events on WindowsFormsHost.
[UPDATE] An extreme case: two WebBrowser controls with two carets showing at the same time:
<StackPanel Margin="20,20,20,20">
<ToggleButton Name="btnLoad" Focusable="True" IsTabStop="True" Content="Load" Click="btnLoad_Click" Width="100"/>
<WebBrowser Name="webBrowser" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
<WebBrowser Name="webBrowser2" Focusable="True" KeyboardNavigation.IsTabStop="True" FocusVisualStyle="{x:Null}" Height="150" Margin="10,10,10,10"/>
<Button Name="btnClose" Focusable="True" IsTabStop="True" Content="Close" Click="btnClose_Click" Width="100"/>
</StackPanel>
this.webBrowser.NavigateToString("<body onload='text.focus()'><textarea id='text' style='width: 100%; height: 100%'>text</textarea></body>");
this.webBrowser2.NavigateToString("<body onload='text.focus()'><textarea id='text' style='width: 100%; height: 100%'>text2</textarea></body>");
This also illustrates that the focus handling issue is not specific to contentEditable=true content.
For anyone else stumbling upon this post and needing to set keyboard focus to the browser control (not a particular element within the control, necessarily), this bit of code worked for me.
First, add a project reference (under Extensions in VS) for Microsoft.mshtml.
Next, whenever you'd like to focus the browser control (say for example, when the Window loads), simply "focus" the HTML document:
// Constructor
public MyWindow()
{
Loaded += (_, __) =>
{
((HTMLDocument) Browser.Document).focus();
};
}
This will place keyboard focus inside the web browser control, and inside the "invisible" ActiveX window, allowing keys like PgUp / PgDown to work on the HTML page.
If you want to, you might be able to use DOM selection to find a particular element on the page, and try to focus() that particular element. I have not tried this myself.
The reason it behaves this way is related to the fact that it's an ActiveX control which itself is a fully windows class (it handles mouse and keyboard interaction). In fact much of the time you see the component used you'll find it is the main component taking up a full window because of this. It doesn't have to be done that way but it presents issues.
Here's a forum discussing the exact same issue and it's causes can be clarified by reading the last commentators article links:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/1b50fec6-6596-4c0a-9191-32cd059f18f7/focus-issues-with-systemwindowscontrolswebbrowser
To outline the issues you're having
Tabbing is misbehaving. User needs to hit Tab twice to see the caret (text cursor) inside WebBrowser and be able to type.
that's because the browser control itself is a window which can be tabbed to. It doesn't "forward" the tab to it's child elements immediately.
One way to change this would be to handle the WM message for the component itself but keep in mind that doing so gets tricky when you want the "child" document inside of it to be able to handle messages.
See: Prevent WebBrowser control from stealing focus? specifically the "answer". Although their answer doesn't account that you can control whether the component interacts through dialogs with the user by setting the Silent property (may or may not exist in the WPF control... not sure)
When user switches away from the app (e.g., with Alt-Tab), then goes back, the caret is gone and she is unable to type at all. A physical mouse click into the WebBrowser's window client area is required to get back the caret and keystrokes.
This is because the control itself has received the focus. Another consideration is to add code to handle the GotFocus event and to then "change" where the focus goes. Tricky part is figuring out if this was "from" the document -> browser control or your app -> browser control. I can think of a few hacky ways to do this (variable reference based on losing focus event checked on gotfocus for example) but nothing that screams elegant.
Inconsistently, a dotted focus rectangle shows up around WebBrowser (when tabbing, but not when clicking). I could not find a way to get rid of it (FocusVisualStyle="{x:Null}" does not help).
I wonder if changing Focusable would help or hinder. Never tried it but I'm going to venture a guess that if it did work it would stop it from being keyboard navigable at all.
Internally, WebBrowser never receives the focus. That's true for both logical focus (FocusManager) and input focus (Keyboard). The Keyboard.GotKeyboardFocusEvent and FocusManager.GotFocusEvent events never get fired for WebBrowser (although they both do for buttons in the same focus scope). Even when the caret is inside WebBrowser, FocusManager.GetFocusedElement(mainWindow) points to a previously focused element (a button) and Keyboard.FocusedElement is null. At the same time, ((IKeyboardInputSink)this.webBrowser).HasFocusWithin() returns true.
People have hit issues with where 2 browser controls both show the focus(well... the caret) or even had a hidden control take the focus.
All in all it's pretty awesome what you can do with the component but it's just the right mix of letting you control/change the behavior along with predefined sets of behavior to be maddening.
My suggestion would be to try to subclass the messages so you can direct the focus control directly through code and bypass it's window from trying to do so.

Embedded ComboBox in Button, but Button steals click event when activating drop down

I have an application with limited screen space, so I'm trying to conserve some real estate by putting a combo box within a button. Its pretty nifty and it looks exactly like what I wanted it to look like. The problem is, every time I click the down arrow on the combo box, the Button also receives a click event. The drop down menu still works properly, but my button has already fired before I've had a chance to actually select what I wanted.
Here is my WPF code that describes my Button and its contents.
<Button FontSize="14" Height="32" HorizontalAlignment="Left" Click="DisableCopierButton_Click">
<StackPanel Orientation="Horizontal">
<Label Content="Disable Copier:" />
<ComboBox Name="DisableCopierComboBox">
<ComboBoxItem Content="1"/>
<ComboBoxItem Content="2"/>
</ComboBox>
</StackPanel>
</Button>
My question is, when I'm clicking on the ComboBox and only the ComboBox, how do I prevent the click event from passing through the button that is underneath it?
I finally found it. I went with the solution provided by Rachel here, even though it was not selected as the answer.
private void EnableCopierButton_Click(object sender, RoutedEventArgs e)
{
if (!(e.OriginalSource is Button))
{
// combo box was clicked, not the button, so get out of here.
return;
}
}
This seems like a SplitButton, yeah. Don't reinvent the wheel. Plenty of implementations on the web.
I recommend WPF Toolkit Extended's.
Rather than making the combo box be part of the button's Content, you may want to try creating a ContentTemplate for the button that includes a combo box.

AutoCompleteBox with TextChanged event not selecting properly

Hi I'm using an AutoCompleteBox like this
<!-- XAML Code -->
<sdk:AutoCompleteBox Grid.Row="2"
FilterMode="None"
ItemsSource="{Binding Customers}"
SelectedItem="{Binding Path=SelectedCustomer, Mode=TwoWay}"
Text="{Binding CustomerSearchString, Mode=TwoWay}"
ValueMemberBinding="{Binding Path=FullName}"
ValueMemberPath="FullName"
TextChanged="{ext:Invoke MethodName=Search, Source={Binding}}"/>
C# part:
// Search Method in the viewmodel
public void Search()
{
var customerOperation = _context.Load(_context.GetCustomerByNameQuery(CustomerSearchString));
customerOperation.Completed += (s, e) => Customers = new List<Customer>(customerOperation.Entities);
}
In my app to quick-search for customers for an fast and uncomplicated method to search. I get it to display everything correctly in the dropdown, and when I select with the mouse it works perfectly.
But when I press ArrowDown, you see the text come up for a split-second but then it reverts and puts the cursor back in the textbox instead of selecting the first entry down. I tried using the TextInput event, but that one won't fire.
How can I avoid this behaviour?
SOLUTION:
The problem was, that the TextChanged event got fired when the user selected an entry, creating some sort of race condition like behaviour where the Text got reset. The solution was to use the KeyUp event (don't use KeyDown, because the Text property won't be updated yet). This event doesn't get triggered when the user selects something, solving the problem.
Final code (ViewModel unchanged):
<!-- XAML Code -->
<sdk:AutoCompleteBox Grid.Row="2"
FilterMode="None"
ItemsSource="{Binding Customers}"
SelectedItem="{Binding Path=SelectedCustomer, Mode=TwoWay}"
Text="{Binding CustomerSearchString, Mode=TwoWay}"
ValueMemberBinding="{Binding Path=FullName}"
ValueMemberPath="FullName"
KeyUp="{ext:Invoke MethodName=Search, Source={Binding}}"/>
Thanks everyone!
Add a handler like this in code:
KeyEventHandler eventHandler = MyAutoCompleteBox_KeyDown;
MyAutoCompleteBox.AddHandler(KeyDownEvent, eventHandler, true);
I'm not understanding why you are using a TextChanged event...? What is that for? If you take that out, does it work? I use an autocomplete box in my project and I don't need a search method... all I do is just supply a list of objects to the autocompletebox and the it searches that list when the user types. I can select either by mouse or by up/down arrows. The only thing that I can think of is that each time you try to use the up/down arrow the text changes and fires off the search function and closes the selection option drop down...

WPF Combo box + MouseLeftButtonDown

Hopefully someone can help because I haven't been able to figure this out. Here's my xaml code for the popup/combo box, please not there is other code before and after this for the rest of the layout.
<Popup x:Name="popupMethods" Height="400" Width="150"
StaysOpen="False" Placement="Bottom" IsOpen="false"
HorizontalAlignment="Left">
<ComboBox x:Name="combo" MouseLeftButtonDown="combo_MouseDown">
<TextBlock>Hello</TextBlock>
<TextBlock>World</TextBlock>
<TextBlock>This</TextBlock>
<TextBlock>is</TextBlock>
<TextBlock>Autocomplete</TextBlock>
<TextBlock>Textbox</TextBlock>
</ComboBox>
</Popup>
Have it set up to popup on the screen whenever the user starts typing, which works. The problem is I want the user to be able to click one of the words in the combo box and that gets inserted into the text box. This parts not working as the MouseLeftButtonDown is never being fired. I've tried a couple of different methods including the one from this site
http://www.designerwpf.com/2008/12/03/getting-a-mouseleftbuttondown-or-mouseleftbuttonup-
event-from-your-textbox/
as well as one I saw somewhere else that was combo.MouseLeftButtonDown += delegate { };
Thanks for any help.
Instead of MouseLeftButtonDown event handler, use PreviewMouseLeftButtonDown you can achieve the same.
You probably want to look at the SelectionChanged event. It fires whenever an item in the ComboBox's drop down is selected.
I think that the ComboBox internally handles the MouseLeftButtonDown event, and that is causing it not to be passed on to your code.

Resources