I have an InkCanvas over the front of my application.
I want it to only interact with Stylus/Pen events. All other events should be passed through to the various controls underneath the canvas.
The intention is that I detect gestures on the InkCanvas with a pen, while other manipulation events are handled by the controls below the InkCanvas (such as touch and inertial manipulation).
Currently I've tried disabling manipulation events, capturing them, setting handled = false. So far I can't find the right solution. Any ideas?
You can detect the input mode (PointerDeviceType) in the Pointer events of the InkCanvas, for example:
<ScrollViewer x:Name="scrollViewer" Width="400" Height="400" Background="LightBlue" VerticalAlignment="Center" HorizontalAlignment="Center"
PointerPressed="scrollViewer_PointerPressed">
<StackPanel>
<Rectangle Height="300" Width="300" Fill="Red"/>
<Rectangle Height="300" Width="300" Fill="Black"/>
</StackPanel>
</ScrollViewer>
<InkCanvas x:Name="inkCanvas" Width="400" Height="400" GotFocus="inkCanvas_GotFocus" VerticalAlignment="Center" HorizontalAlignment="Center"
Tapped="inkCanvas_Tapped" PointerPressed="inkCanvas_PointerPressed"/>
code behind:
private void inkCanvas_PointerPressed(object sender, PointerRoutedEventArgs e)
{
// Accept input only from a pen or mouse with the left button pressed.
PointerDeviceType pointerDevType = e.Pointer.PointerDeviceType;
if (pointerDevType == PointerDeviceType.Pen)
{
//TODO:
}
else
{
// Process touch or mouse input
inkCanvas.Visibility = Visibility.Collapsed;
}
}
private void scrollViewer_PointerPressed(object sender, PointerRoutedEventArgs e)
{
PointerDeviceType pointerDevType = e.Pointer.PointerDeviceType;
if (pointerDevType == PointerDeviceType.Pen)
{
inkCanvas.Visibility = Visibility.Visible;
}
else
{
// Process touch or mouse input
inkCanvas.Visibility = Visibility.Collapsed;
}
}
Related
How do I make the cursor in a TextBox stop blinking in WPF?
I have already tried to a MouseDown event on the parent, with one action, this.Focus();, which does not work.
You could have achieved this with HideCaret Method. However, since the WPF application cannot provide you handle for the TextBox Control (it only provides handle for Window), we cannot use HideCaret.
However, we could emulate the effect using WPF. The first step to achieve this would be disable the actual Caret. This can be done by changing the Color to transparent.
<TextBox x:Name="txtName" CaretBrush="Transparent" />
The Next step involve using a Canvas and Border to emulate the Cursor.
<Grid>
<TextBox x:Name="txtName" CaretBrush="Transparent" />
<Canvas>
<Border x:Name="Caret" Visibility="Collapsed" Canvas.Left="0" Canvas.Top="0" Width="1" Height="15" Background="Black"/>
</Canvas>
</Grid>
You need to ensure the Canvas overlap the Textbox
And in code-behind
txtName.SelectionChanged += (sender, e) => MoveCustomCaret();
txtName.LostFocus += (sender, e) => Caret.Visibility = Visibility.Collapsed;
txtName.GotFocus += (sender, e) => Caret.Visibility = Visibility.Visible;
Where MoveCustomCaret is defined as
private void MoveCustomCaret()
{
var caretLocation = txtName.GetRectFromCharacterIndex(txtName.CaretIndex).Location;
if (!double.IsInfinity(caretLocation.X))
{
Canvas.SetLeft(Caret, caretLocation.X);
}
if (!double.IsInfinity(caretLocation.Y))
{
Canvas.SetTop(Caret, caretLocation.Y);
}
}
Sample
My little project is progressing well, however I'm stumbling over something which is probably stupid...
Somehow, when I open the application, nothing gets focus, I have to use the "tab" key to be able to move the focus to the commandbar and to be able to use the keyboard shortcuts.
And then....
WHen I use the Scrollviewer to move the image or zoom, I cannot use the keyboard shortcuts again, until I use the "tab" to move it to the commandbar.
I've tried
cmdbar.Focus(FocusState.Programmatic);
a bit everywhere in the app where I think that it may be useful, to no avail. I've also tried to use the Keyboard Accelerators, but it does not help. Any tips?
Here is my XAML code:
<Page.Resources>
<DataTemplate x:Key="myResourceTemplate">
<TextBlock Text="{Binding}" MaxHeight="10" FontSize="8" HorizontalAlignment="Left" VerticalAlignment="Top" FontWeight="Bold" LineHeight="9" Height="Auto" />
</DataTemplate>
</Page.Resources>
<Page.BottomAppBar>
<CommandBar x:Name="cmdbar" ClosedDisplayMode="Compact" HorizontalAlignment="Left" HorizontalContentAlignment="Left" VerticalAlignment="Center" KeyUp="kb_openkey" Opacity="1" Visibility="Visible" Background="#260000FF">
<CommandBar.Content>
<Grid/>
</CommandBar.Content>
<AppBarButton Icon="ZoomIn" Label="AppBarButton" Tapped="Zoomin_Click"/>
<AppBarButton Icon="ZoomOut" Label="AppBarButton" Tapped="Zoomout_Click"/>
<AppBarToggleButton x:Name="randomstatus" Icon="Shuffle" Label="Random" Tapped="Togglerandom"/>
<... a bunch of other buttons >
</CommandBar>
</Page.BottomAppBar>
<Grid x:Name="imggrid" Background="Black" BorderBrush="Black" KeyUp="kb_openkey">
<ScrollViewer x:Name="imageView_scroller" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled" ZoomMode="Enabled" RequestedTheme="Dark" KeyUp="kb_openkey">
<Image x:Name = "ctlImage" Grid.Column ="0" VerticalAlignment = "Stretch" HorizontalAlignment = "Stretch" Stretch = "Uniform"
PointerWheelChanged="ctlImage_PointerWheelChanged"
ManipulationMode = "TranslateX, TranslateY, Scale"
ManipulationStarted = "ctlImage_ManipulationStarted"
ManipulationDelta = "ctlImage_ManipulationDelta"
ManipulationCompleted = "ctlImage_ManipulationCompleted"
KeyUp="kb_openkey"
>
<Image.RenderTransform>
<CompositeTransform x:Name="image_Transform" ></CompositeTransform >
</Image.RenderTransform >
</Image>
</ScrollViewer>
</Grid>
And here is how I handle keyboard input:
void kb_openkey(object sender, KeyRoutedEventArgs e)
{
if ((int)e.Key >= 1 && (int)e.Key <= 255)
{
switch ((int)e.Key)
{
case 70: //A
....dothis....;
break;
case 65: //A
.... dothat....;
break;
}
}
}
you do not need to set focus in order to use KeyboardAccelrators as shortcuts. Hence you do not need the keyup or keydown events on your image or command bar, unless they have some other task irrelevant of setting focus.
you should use KeyBoardAccelrators in your command bar with AccessKey and any options modifiers like Ctrl or Shift
an example of AccessKey on AppBarButton
<AppBarButton
Icon="Copy"
Label="Copy"
ToolTipService.ToolTip="Copy (Ctrl+C)"
Click="OnCopy"
AccessKey="C">
<AppBarButton.KeyboardAccelerators>
<KeyboardAccelerator
Modifiers="Control"
Key="C" />
</AppBarButton.KeyboardAccelerators>
</AppBarButton>
you can find more details in the link of docs I provided above.
Update :
when you tap on another UI element the focus from previous element is removed automatically, you do not need a KeyUp event on your image as well as commandbar, you just just use a global CoreWindow.KeyDown which can help you in any key related commands you want to accomplish
#touseefbsb, very useful!!! Thanks! This handles the key no matter what has got focus and is being clicked on.
So my code is, for reference:
In XAML, in the page section, add:
Loaded="initkbd"
Unloaded="unloadkbd"
And in the C# portion, add:
//Add the key handler method to the KeyDown handlers
private void initkbd(object sender, RoutedEventArgs e)
{
Window.Current.CoreWindow.KeyDown += kb_openkey;
cmdbar.Content = "Added to keys";
}
//Remove the keyhandler method from the list
private void unloadkbd(object sender, RoutedEventArgs e)
{
Window.Current.CoreWindow.KeyDown -=kb_openkey;
}
and then, the key handler looks like this:
private void kb_openkey(CoreWindow sender, KeyEventArgs e)
{
//Mark the event as handled
e.Handled = true;
int keypressed = (int) e.VirtualKey;
//Than handle the key, based on its keycode
if ((int)keypressed >= 1 && (int)keypressed <= 255)
{
switch (keypressed)
{
case 70: //F
//do something when F is presed
break;
case 76: //L dialog to show items
//Do something when L is pressed
break;
}
}
}
I am trying to practice c# by reproduce an App that is in the Apple AppStore.
In the app, there is a rectangle with the text: "Touch me". When you touch it, the rectangle repositions itself.
After you do this a few times, the text changes to "Do not Touch me". In that case you have to Touch outside of the rectangle.
It all went well, up to the point where you have to touch outside the rectangle.
Here is my event handler:
private void Canvas_MouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
{
if (click == 0)
{
if (rectangle1.IsMouseOver || textBlock1.IsMouseOver)
{
// reposition and stuff
if (clicks == 10)
{
// Change the value of the variable click to 1
click = 1;
textBlock1.Text = "Do Not Click me";
Canvas.SetLeft(textBlock1, 200);
}
}
}
else
{
if (rectangle1.IsMouseOver || textBlock1.IsMouseOver)
{
// Game Over
this.Close();
} else
{
// reposition and stuff
click = 0;
textBlock1.Text = "Click me";
Canvas.SetLeft(textBlock1, 225);
}
}
}
The program works perfectly up to the point where you have to click outside the rectangle.
The program closes when you click on the rectangle but when you click outside it, nothing happens.
Is there any event-handler that can do the task i want?
Here is my xaml
<Window x:Class="ClickMe.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="510" Width="525" ResizeMode="NoResize">
<Canvas Name="canvas" MouseLeftButtonDown="Canvas_MouseLeftButtonDown_1">
<Rectangle Fill="#FFF4F4F5" Name="rectangle1" HorizontalAlignment="Left" Height="38" Stroke="Black" VerticalAlignment="Top" Width="509" Canvas.Left="0" Canvas.Top="63"/>
<Label Name="label1" Content="0" Canvas.Left="57" Canvas.Top="446"/>
<Label Content="Klicks:" Canvas.Left="10" Canvas.Top="446"/>
<TextBlock Name="textBlock1" Canvas.Left="225" TextWrapping="Wrap" Text="Click Me" Canvas.Top="74" Margin="10,0,0,0"/>
</Canvas>
Canvas is a UIElement. This allows the use of the PointerPressed event.
private void Target_PointerMoved(object sender, PointerRoutedEventArgs e)
{
Windows.UI.Xaml.Input.Pointer ptr = e.Pointer;
if (ptr.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
{
Windows.UI.Input.PointerPoint ptrPt = e.GetCurrentPoint(Target);
if (ptrPt.Properties.IsLeftButtonPressed)
{
//do yo thang
}
}
}
You really just need to set the Background of the Canvas, as it only gets mouse input where it has "rendered content". The background could even be transparent:
<Canvas Name="canvas" Background="Transparent"
MouseLeftButtonDown="Canvas_MouseLeftButtonDown_1">
...
</Canvas>
use this instead of Canvas_MouseLeftButtonDown_1 event:
protected override OnMouseDown(MouseButtonEventArgs e)
{
if(e.Changed == MouseButton.Left)
{
// Your logic on mouse down will go here
}
base.OnMouseDown(e);
}
with this you can click anywhere on the canvas and get the event to fire. I hope this helps..
I can't get why my canvas doesn't get pointer events. Below my code and a part of the XAML layout:
XAML
<Canvas
x:Name="cPad"
Canvas.ZIndex="99"
Grid.Column="0"
PointerPressed="Pad_PointerPressed">
<Rectangle
x:Name="rPicker"
Width="24"
Height="24"
Stroke="Black"
Fill="White"
StrokeThickness="3"/>
</Canvas>
C#
private void Pad_PointerPressed(object sender, PointerRoutedEventArgs e)
{
cPad.CapturePointer(e.Pointer);
var point = e.GetCurrentPoint(cPad).Position;
Canvas.SetLeft(rPicker, point.X);
Canvas.SetTop(rPicker, point.Y);
e.Handled = true;
}
The problem that rPicker moves only if I click on rPicker... But I need to Pad_PointerPressed executes for any point on canvas. How can I make it?
Set Canvas.Background property to Transparent to enable PointerPressed events:
<Canvas
...
Background="Transparent"
>
...
</Canvas>
Basically, I'm trying to make the canvas listen for a touch input (tap) and will increment the number of taps on screen. It isn't working when I touch the screen on my device. I debugged my code and nothing seems out of the ordinary except that the touch is not detected. I checked ZIndex and the canvas is in front of the screen to be touchable. How do I make it work?
XAML:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" Margin="188,10,187,397"/>
<Button Content="Reset" HorizontalAlignment="Stretch" Margin="-18,535,-18,0" VerticalAlignment="Top" Click="Button_Click"/>
<Canvas ZIndex="0" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeave="Canvas_MouseLeave"/>
</Grid>
C#:
int taps = 0; // create var to detect number of times, user touches the screen
// Constructor
public MainPage()
{
InitializeComponent();
}
// method to register the touch as the finger is placed on the screen
private void Canvas_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//Canvas c = sender as Canvas;
counter.Text = "TOUCHED!";
}
//method register the touch as the finger is lifting up from the screen
private void Canvas_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//Canvas c = sender as Canvas;
taps++;
counter.Text = taps.ToString(); //convert var from int to string
}
//method register the touch as the finger leaves the area of the screen
private void Canvas_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
//Canvas c = sender as Canvas;
MessageBox.Show("You left the screen without lifting your finger. That does not count as a tap!", "Caution!", MessageBoxButton.OK);
}
// method to reset the counter to zero when button is pressed and released
private void Button_Click(object sender, RoutedEventArgs e)
{
taps = 0; // reset the count
counter.Text = taps.ToString(); // convert var from int to string
}
I don't know why you want to do it with Canvas - it won't work as you have nothing in this Canvas, so it can't register your click/tap, Canvas is also hard to adjust to screen. I think it can be done simpler way if you want to do it with MouseUp/Down - subscribe directly to Grid containing your elements instead of filling this Grid with additional Canvas:
In XAML:
<Grid x:Name="ContentPanel" Margin="12,0,12,0" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="7*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" VerticalAlignment="Center" Grid.Row="0"/>
<Button Content="Reset" HorizontalAlignment="Stretch" VerticalAlignment="Center" Click="Button_Click" Grid.Row="1"/>
<TextBlock Name="Touched" FontSize="50" HorizontalAlignment="Center" TextWrapping="Wrap" Text="Touched" VerticalAlignment="Center" Visibility="Collapsed" Grid.Row="2"/>
</Grid>
In code behind:
private int taps = 0;
public MainPage()
{
InitializeComponent();
ContentPanel.MouseLeftButtonDown += ContentPanel_MouseLeftButtonDown;
ContentPanel.MouseLeftButtonUp += ContentPanel_MouseLeftButtonUp;
}
private void ContentPanel_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
taps++;
counter.Text = taps.ToString(); //convert var from int to string
Touched.Visibility = Visibility.Collapsed;
}
private void ContentPanel_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Touched.Visibility = Visibility.Visible;
}
// method to reset the counter to zero when button is pressed and released
private void Button_Click(object sender, RoutedEventArgs e)
{
taps = 0; // reset the count
counter.Text = taps.ToString(); // convert var from int to string
}
As you can see I've subscribed to Grid events (which covers whole screen) - but to make it work I had to set its Background Brush to Transparent, otherwise it will work only if you touch text.
There are many other ways to make your App work, but I hope this will help.
Is there a reason why you don't use the touch-events?
Instead of using MouseLeftButtonDown and MouseLeftButtonUp you should use TouchDown and TouchUp.
Only when you don't handle the touch events or the manipulation events they will be mapped to mouse events. In my experience with touch a single tap also not always gets mapped to MouseLeftButtonDown. As far as I know you could also with mouse events only recoginse one finger. When you want to count more fingers it's necessary to use the TouchDown/TouchUp events.
The problem lies in the overlapping style of the grid
so either make grid rows or define a stackpanel inside the grid, something like this.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<stackpanel>
<TextBlock Name="counter" FontSize="150" HorizontalAlignment="Center" TextWrapping="Wrap" Text="0" Margin="0,0,0,0"/>
<Canvas ZIndex="0" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeave="Canvas_MouseLeave"/>
<Button Content="Reset" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</stackpanel>
</Grid>
Try and check now.
You should set your Background property. If you don't want any background set it to Transparent:
<Canvas ZIndex="99" Background="Transparent" Name="Canvas" HorizontalAlignment="Center" Height="535" VerticalAlignment="Top" Width="446" Tapped="Canvas_CountMyTaps"/>
(If you want the canvas to be on Top be sure to make it have a greater ZIndex than the other elements that it overlaps)
If not set (the default value is null) the element won't capture any taps/click etc, it will be as if they "fall through".
Also, consider using the Tapped event which is a "higher level" event that will respond to clicks, taps with the finger, stylus, etc.