WPF could not get touch position from MouseDown event - c#

I have a WPF project written before touch support was added to .NET (v 4.0), so only the mouse events were handled. I run into this problem when testing the project on a touch screen with fingers.
The problem is, the position (X, Y) is correctly retrieved in the first touch, but the (X, Y) values stay the same in subsequent touches, no matter where I touch, and even if I touch out of the Image, the MouseDown event is fired, which makes it more weird.
It can be reproduced with .NET 3.0/3.5/4.0, tested on Win7/Win8, both are 64 bits. And it seems it is MouseDown event that is misbehaving, MouseUp works fine.
Update:
This is a bug with long history and MS has not yet fixed it (even in 4.5), so you have to update the code if you encounter the same symptom - get the touch position from Touch event, not Mouse event. Luckily this bug is not subtle so it only takes a little while to be located and fixed.
Code to reproduce the issue:
XAML:
<Window x:Class="WpfApplication1.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>
<Image Height="60" Width="80" x:Name="Image" MouseDown="Image_MouseDown"
Source="/WpfApplication1;component/Images/Desert.jpg" />
</Grid>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Image_MouseDown(object sender, MouseButtonEventArgs e)
{
Point p = e.GetPosition(Image);
MessageBox.Show(p.X.ToString() + " " + p.Y.ToString());
}
}
}

By default in WPF, if a Touch event is not handled by a control it will be promoted to a Mouse event. Touch events are routed events, so when the Mouse event will be triggered it will go up and down the visual tree (hence your event handler being executed even when you touch outside of the Image).
If the MouseDown event comes from a promoted touch event, you could probably get the correct position using the StylusDevice:
if (e.StylusDevice != null)
point = e.StylusDevice.GetPosition(sender as Image);
Or as an alternative, you could add a handler for the TouchDown event for the controls where you need the position:
<Image TouchDown="UIElement_OnTouchDown"/>
private void UIElement_OnTouchDown(object sender, TouchEventArgs e)
{
var touchPoint = e.GetTouchPoint(sender as Image);
// more processing using touchPoint.Position
}

If you run in .NET 4.5.0, you will not see this issue. It is introduced at 4.5.1. I am trying to see if I can get somebody at MS to pay attention and fix this without you and me hacking up our code.

Related

Draw on a PDF into a WPF applicartion using Microsoft Webview2 control

I am working on a WPF application in which I want to open and draw into a pdf.
I am able to open the pdf in the webview2 control, but I am not able to "draw into the pdf"
(If i am open this pdf via Microsoft Edge it is possible)
Has anyone an idea/hint/suggestion why the drawing function is disabled/not active when I compile the code?
Desired Output
Output
Used resources:
Get started with WebView2 in WPF
Using the WebView2 control in a .Net application
**Installed versions:**
[Introduction to Microsoft Edge WebView2][1]
[Using the WebView2 control in a .Net application][2]
Installed version of Microsoft.Web.WebView2: 1.0.902.49
Installed version of Microsoft Edge: 92.0.902.67
Installed version of Microsoft Edge WebView2-Runtime: 92.0.902.67
Target Framework: .NET Framework 4.7.2
Visual Studio 2019
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Web.WebView2.Core;
namespace SwissSonic.Pages
{
/// <summary>
/// Interaction logic for Change_drawing.xaml
/// </summary>
public partial class Change_drawing : Page
{
private MainWindow mainWindow;
public Change_drawing(MainWindow main)
{
InitializeComponent();
InitializeAsync();
mainWindow = main;
mainWindow.Button_Click_Zeichnung.Click += Button_Click_Zeichnung;
}
async void InitializeAsync()
{
await webView1.EnsureCoreWebView2Async(null);
}
private void Button_Click_Zeichnung(object sender, RoutedEventArgs e)
{
webView1.CoreWebView2.Settings.IsStatusBarEnabled = true;
webView1.CoreWebView2.Settings.AreDefaultContextMenusEnabled = true;
webView1.CoreWebView2.Settings.IsScriptEnabled = true;
webView1.CoreWebView2.Settings.IsStatusBarEnabled = true;
string test = #"file:\\\C:\\TestPDF\\mcdonalds_in_india.pdf";
//string test = #"https://www.stadlerrail.com/media/pdf/web_stadler_rail_gb20_de.pdf";
// webView1.Source = new Uri(test);
webView1.CoreWebView2.Navigate(test);
}
}
}
<Page x:Class="SwissSonic.Pages.Change_drawing"
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"
xmlns:local="clr-namespace:SwissSonic.Pages"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="Change_drawing">
<Grid Background="Red">
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<wv2:WebView2 Name="webView1" Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="0" Grid.RowSpan="3"/>
</Grid>
</Page>
I can reproduce the issue with the latest version of WebView2. I think the screenshot in the resource link you provide is from a previous version of WebView2. In present version of WebView2, the Draw, Highlight and Erase seems to be disabled.
And the only one related API I can find is HiddenPdfToolbarItems which is added in the latest WebView2 prerelease. But this API can only hide/show these buttons in pdf toolbar.
I think we can't make the Draw, Highlight and Erase show using code because there isn't related API. You can provide feedback about this issue here. I think Edge WebView team will check it and make a reply.

Closing a page in WPF

I am new to C#, I just created a new page and want to close it in the XAML side I have:
<Grid>
<Button Content="Back" Click="Button_Click_Exit"
HorizontalAlignment="Left" Margin="220,263,0,0"
VerticalAlignment="Top" Width="75"/>
</Grid>
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace UI
{
/// <summary>
/// Interaction logic for calibrationPage.xaml
/// </summary>
public partial class calibrationPage : Page
{
public calibrationPage()
{
InitializeComponent();
}
private void Button_Click_Exit(object sender,
RoutedEventArgs e)
{
Close();
}
}
}
I think this supposed to be really simple, somehow I get this error, when I try to build it:
error CS1061: 'UI.calibrationPage' does not contain a definition for 'Close'
and no extension method 'Close' accepting a first argument
of type 'UI.calibrationPage' could be found
Edit 1: I understand that close() does not exit, then rephrasing my question, how can I simply close the page using a button click?
Edit 2: For the benefit of others: I ended up using PageNavigator.NavigateTo function to navigate back and forth between pages, I do not think that there is a concept of closing one page in WPF. Thanks for everyone's participation.
You're not telling an object to close.
If you're just wanting a button to act on the event, I'd recommend click event or OnClick. If you are in VS you can just double click the button in the designer, it will auto populate everything for you.
If you want a form to close you can use similar methods from your WPF window.
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
On the other hand, if you wanted to create your own method (like in this case) you can do so like:
private void closeMethod()
{
// your code here
}
Then you call it with:
closeMethod();
Depending on your method you can tell it to do what you want on the page. Such as close the page or window.
You can also pass references in your methods
Refer to: Microsoft for methods.
Not sure if it fits here, but you may want to use Application.Exit(); in some cases, if so refer to: MSDN to find the difference.
You would think closing a page would be a trivial task, but there's no page.Close property so what is one to do? I don't think pages were designed to be opened and closed like Windows, but here's a solution that should solve the problem for most.
To start, create a new WPF application and add a page to it called Page1.
In MainWindow.xaml, create a grid with a button and frame to display your page. (The frame is a key element because you can close the page by setting the frame.content = Nothing)
<Window x:Class="MainWindow"
...
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Top">
<Button Content="Page1" Click="Page1_Click" Height="40"/>
</StackPanel>
<Frame x:Name="mainframe" Margin="0,50,0,0" NavigationUIVisibility="Hidden"/>
</Grid>
</Window>
In MainWindow.xaml.vb, add this code to display the page within the frame...
Class MainWindow
Private Sub Page1_Click(sender As Object, e As RoutedEventArgs)
Dim page1 As New Page1(mainframe)
mainframe.Content = page1
End Sub
End Class
In Page1.xaml, create a grid with a button to close the page...
<Page x:Class="Page1">
...
<Grid>
<Button Click="Page1Button_Click" Content="Close Page 1" Height="140" Width="140"/>
</Grid>
</Page>
And in your Page1.xaml.vb, close the page like this...
Class Page1
Private mainwin_frame As Frame
Public Sub New(localframe As Frame)
InitializeComponent()
mainwin_frame = localframe
End Sub
Private Sub Page1Button_Click(sender As Object, e As RoutedEventArgs)
mainwin_frame.Content = Nothing
End Sub
End Class
The key to all of this working is passing the mainframe object to the Page1 constructor so the page can access it. I'm sure there are other ways to do this but this is a common sense approach imo.

Click button through only part of a transparent image in wpf program

I am trying to implement the ability to click through only part of a transparent background. The background image is a solid gray and has 3 circles, a rectangle, and an oval shape. The image is transparent. Button 1 appears inside of the rectangle and button 2 appears inside of the oval. When the program runs both buttons are visible but currently not clickable because the image is in a grid and added after the buttons. I would like to make only the rectangle click through so button 1 can be clicked, but still keep the image ahead of button 2 so it is not clickable.
I've searched around quite a bit for a solution but have not found what I'm looking for. This example is a dumbed down version of what I'm currently working on - but the functionality is essentially the same. Any help would be appreciated.
Xaml:
<Window x:Class="TransparentClickThrough.MainWindow"
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"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="ButtonOne" Click="ButtonOne_Click" Content="Button1" Width="50" Height="25" ></Button>
<Button x:Name="ButtonTwo" Content="Button2" Width="50" Height="25" Margin="379,100,59,148" Click="ButtonTwo_Click"></Button>
<Image Source="/TransparentClickThrough;component/background.png" />
</Grid>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TransparentClickThrough
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonOne_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("You clicked button 1!");
}
private void ButtonTwo_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("You clicked button 2!");
}
}
}
background - 3 circles, rectangle, and oval are all set to transparent
This is what the program looks like when running.
I was able to just add a Rectangle with transparent fill to the grid. Then I added an event "MouseDown" Interaction trigger to toggle the Visibility through a DataTrigger which in turn changed the background color.
Similar to this link link

Data binding TextBox to string in code behind fails

I'm attempting to bind a string property to a TextBox's Text field in XAML. I am doing this in a UserControl. I've searched StackOverflow and the internet in general and found various related topics, some examples:
Binding objects defined in code-behind
WPF: simple TextBox data binding
I've followed the code in these examples as best as I can but the Input property does not appear to be binding to the TextBox. I've tried various different methods of setting up the DataContext, including from the code behind and it's still not working.
What is it that I'm missing, is this an issue because it's a UserControl?
The code in the SearchInputTextBox_TextChanged event is called but always outputs an empty string. If I put a Debug.WriteLine call in the Input set section nothing happens.
The XAML file:
<UserControl x:Class="DatabaseViewerApp.View.SearchBox"
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"
d:DesignHeight="70" d:DesignWidth="240"
x:Name="Control">
<Border Padding="5" Background="#303030">
<StackPanel>
<TextBlock Text="Search" Margin="0,0,0,0" FontSize="20px" Foreground="White"></TextBlock>
<TextBox Name="SearchInputTextBox" Text="{Binding ElementName=Control, Path=Input}" Margin="0,5" FontSize="15px" TextChanged="SearchInputTextBox_TextChanged"></TextBox>
</StackPanel>
</Border>
</UserControl>
The C# file:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DatabaseViewerApp.View
{
/// <summary>
/// Interaction logic for SearchBox.xaml
/// </summary>
public partial class SearchBox : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string input;
public string Input
{
get { return input; }
set
{
if (value != input)
{
input = value;
NotifyPropertyChanged("Input");
}
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public SearchBox()
{
InitializeComponent();
}
private void SearchInputTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
Debug.WriteLine(Input);
}
}
}
I think your problem is not that it isn't working... The issue is that you may not be testing it properly. Your code works for me in a new WPF solution. What you may be missing is that the Text binding gets updated when you leave the focus. In contrast, the TextChanged event gets fired as soon as the text is input into the text box.
So, whats happening is that when the TextChanged event is fired, the binding hasn't yet been updated. If you try putting a breakpoint in your property setter, then change some text in the text box, and move focus out of that control, you should see it get hit.
As an aside, there isn't much sense in binding your Text to a property in the code behind because the UI element can be accessed directly from there.
Try this:
<TextBox Name="SearchInputTextBox"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl},Path=Input}"
Margin="0,5" FontSize="15px" TextChanged="SearchInputTextBox_TextChanged">
</TextBox>

Routed Events problem - hitting the root UI element before the child element

My routed events are hitting the root UI element before the child element. Is this expected? How can I have the routed events hit the child element first?
Objective: If text is typed anywhere other than "custom textbox", put text in "default textbox"
Result: Window_PreviewTextInput is being hit before custom_PreviewTextInput, even if my cursor focus is on "Custom Textbox"
What should I do differently?
XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" SizeToContent="WidthAndHeight"
PreviewTextInput="Window_PreviewTextInput"
>
<Grid Margin="100,100,100,100">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="default" Width="100"/>
<TextBox x:Name="defaultTB" Width="300" Height="50"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="custom" Width="100"/>
<TextBox x:Name="custom" PreviewTextInput="custom_PreviewTextInput" Width="300" Height="50"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//goal: if text is typed anywhere except custom textbox, put text in default textbox
private void Window_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Keyboard.Focus(defaultTB);
}
//goal: if text is typed in custom TB, put text there, and end the event routing
private void custom_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = true;
}
}
}
Routed Event could be a bubbling or tunneling. You've a tunneling event behaviour.
From MSDN, UIElement.PreviewTextInput Event:
Routing strategy - Tunneling
The corresponding bubbling event is TextInput.
Routed Events Overview - Routing Strategies:
Bubbling: Event handlers on the event source are invoked. The routed
event then routes to successive parent elements until reaching the
element tree root. Most routed events use the bubbling routing
strategy. Bubbling routed events are generally used to report input or
state changes from distinct controls or other UI elements
Direct: Only the source element itself is given the opportunity to
invoke handlers in response. This is analogous to the "routing" that
Windows Forms uses for events. However, unlike a standard CLR event,
direct routed events support class handling (class handling is
explained in an upcoming section) and can be used by EventSetter and
EventTrigger.
Tunneling: Initially, event handlers at the element tree root are
invoked. The routed event then travels a route through successive
child elements along the route, towards the node element that is the
routed event source (the element that raised the routed event).
Tunneling routed events are often used or handled as part of the
compositing for a control, such that events from composite parts can
be deliberately suppressed or replaced by events that are specific to
the complete control. Input events provided in WPF often come
implemented as a tunneling/bubbling pair. Tunneling events are also
sometimes referred to as Preview events, because of a naming
convention that is used for the pairs.

Categories