WPF: Button with custom template is not clickable as whole [duplicate] - c#

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..

Related

UWP: How do you make a Border a click through shield?

I have situation where I want an overlay control to block UI interactions on a Page for everything that is behind a border. I have tried setting Border.ManipulationMode to False. I have set IsTapEnabled, IsRightTapEnabled, IsDoubleTapEnabled, and IsHitTestVisible to False.
I also tried subscribing to the Tapped and PointerEntered events, and setting the args Handled property to true. After all of this I can still click on Buttons through the border, and invoke their commands. Below are a few screenshots for context:
Page with no overlay
Page now has what should be an overlay that blocks controls behind it
A button capturing PointerOver that shouldn't be
Here is the UserControl xaml that becomes the overaly on the Page:
<UserControl x:Class="PocMvvmToolkitApp.Dialogs.DialogShell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid x:Name="overlayGrid"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<!--dialogShield is the Border that I want to prevent click through on-->
<Border x:Name="dialogShield"
Background="#AAFFFFFF"
IsHitTestVisible="False"
ManipulationMode="None"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsDoubleTapEnabled="False"
IsHoldingEnabled="False"
IsRightTapEnabled="False"
IsTapEnabled="False"/>
<Border x:Name="dialogBorder"
BorderBrush="Black"
BorderThickness="1" />
</Grid>
Attempting to handle the events:
public DialogShell()
{
this.InitializeComponent();
this.allDialogs = new List<ExtendedContentDialog>();
this.visibleDialogs = new List<ExtendedContentDialog>();
////Doesn't work
this.dialogShield.PointerEntered += this.OnModalShieldPointerEntered;
this.dialogShield.Tapped += this.OnModalShieldTapped;
}
private void OnModalShieldTapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
////Doesn't block click through
e.Handled = true;
}
private void OnModalShieldPointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
e.Handled = true;
}
On the Page.xaml.cs here is where I add or remove the DialogShell control to the parent Grid on the page:
private void OnDialogStackChanged(Args.DialogStackChangedEventArgs args)
{
switch (args.Context)
{
case Args.DialogStackChangedContext.Showing:
if (this.dialogShell == null)
{
this.dialogShell = new DialogShell();
this.dialogShell.ShowDialog(args.Dialog);
this.rootGrid.Children.Add(this.dialogShell);
Grid.SetColumnSpan(this.dialogShell, 2);
}
break;
case Args.DialogStackChangedContext.Closing:
if (this.dialogShell != null)
{
this.dialogShell.RemoveDialog(args.Dialog);
if (this.dialogShell.AllDialogs.Count == 0)
{
this.rootGrid.Children.Remove(this.dialogShell);
this.dialogShell = null;
}
}
break;
}
}
Any help with this Border situation would be appreciated. Before someone recommends using ContentDialog, please don't, I have my reasons for this setup. Thanks!

Commandbar not getting focus -- gotta use keyboard

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;
}
}
}

Disable touch event handling of an InkCanvas in UWP

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;
}
}

Viewport3D Mouse Event doesn't fire when hitting background

I set up a Viewport3D with a MouseEventHandler
[...]
Main3DWindow.MouseUp += new MouseButtonEventHandler(mainViewport_MouseUp);
[...]
void mainViewport_MouseUp (object sender, MouseButtonEventArgs e) {
Point location = e.GetPosition(Main3DWindow);
ModelVisual3D result = GetHitTestResult(location);
if (result == null) {
_CurrentData.Unselect();
return;
}
_CurrentData.SelectItemFromObjectList(result);
}
And it works pretty fine when an object is clicked.
My expectation was: If no object is clicked (because the user clicked at the background) the result is null. But in fact the mainViewport_MouseUp-method is not even called.
My question: how can i detect clicks on the background of the Viewport3D?
It is as you wrote, it wont be fired.
I solved that by defining events on border and put viewport into border. Sample is from XAML:
<Border
MouseWheel="mainViewport_MouseWheel"
MouseMove="mainViewport_MouseMove"
MouseLeftButtonDown="mainViewport_MouseLeftButtonDown"
Background="Black">
<Viewport3D
Name="mainViewport"
ClipToBounds="True"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="0,0,0,0">
.....
</Viewport3D>
</Border>
And in the code:
private void mainViewport_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point location = e.GetPosition(mainViewport);
try
{
ModelVisual3D result = (ModelVisual3D)GetHitTestResult(location);
//some code.......
}
catch
{
//some code .......
}
}

Why does the MouseMove event not work if mouse loses focus on object?

I basically have a simple problem in my program that I just want to make sure goes right. It should on the click of the mouse button add the MouseEventHandler and then move the circle along with the mouse until the event handler gets removed. I simplified the code to the very basics:
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 Name="grid1" Background="White" MouseLeftButtonUp="grid_MouseUp">
<Ellipse Height="50" HorizontalAlignment="Left" Margin="12,12,0,0" Name="ellipse1" Stroke="{x:Null}" VerticalAlignment="Top" Width="50" Fill="Black" MouseLeftButtonDown="ellipse1_MouseDown" />
</Grid>
</Window>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private static Point _oldPoint = new Point(), _newPoint = new Point();
private void ellipse1_MouseDown(object sender, MouseButtonEventArgs e)
{
_oldPoint = e.GetPosition(grid1);
grid1.MouseMove += new MouseEventHandler(grid_MouseMove);
}
private void grid_MouseUp(object sender, MouseButtonEventArgs e)
{
grid1.MouseMove -= new MouseEventHandler(grid_MouseMove);
}
private void grid_MouseMove(object sender, MouseEventArgs e)
{
_newPoint = e.GetPosition(grid1);
ellipse1.Margin = new Thickness(ellipse1.Margin.Left - _oldPoint.X + _newPoint.X, ellipse1.Margin.Top - _oldPoint.Y + _newPoint.Y, 0, 0);
_oldPoint = _newPoint;
}
}
Now in general this code works fine and I think is quite neat as it doesn't check the movement of the mouse until one actually presses the button. However, my question is as follows:
I had to add the MouseMove event to the grid rather than to the circle, because once the mouse pointer loses focus of the circle (by moving the mouse too fast) it doesn't trigger the MouseMove event anymore. But why exactly does that happen? At the beginning of the event the mouse was definitely above the circle and then it moved. Yes, it moved away from the circle but shouldn't that still trigger the event?
You can capture the mouse and handle all events in your ellipse.
<Grid Name="grid1" Background="White">
<Ellipse Height="50" HorizontalAlignment="Left" Margin="12,12,0,0" Name="ellipse1" Stroke="{x:Null}" VerticalAlignment="Top" Width="50" Fill="Black"
MouseLeftButtonDown="ellipse1_MouseDown" MouseLeftButtonUp="ellipse1_MouseUp" />
</Grid>
with this code behind
private void ellipse1_MouseDown(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(ellipse1);
_oldPoint = e.GetPosition(grid1);
ellipse1.MouseMove += new MouseEventHandler(ellipse1_MouseMove);
}
private void ellipse1_MouseUp(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(null);
ellipse1.MouseMove -= new MouseEventHandler(ellipse1_MouseMove);
}
I've moved and renamed grid_MouseMove to ellipse1_MouseMove.
Adding to what Peter said, if you use the Grid.MouseDown event and checked if the oldPoint is within Ellipse and have then handled the MouseMove event, this odd behavior wont be seen.
I also suggest exploring drag events.
A control only gets the mouse-events as long as the mouse is hovering over that particularly control.
If moving to a new control, the mouse is getting unhooked from the old control and hooked to the new control.
There are ways where you can create a global hook attached to the entire process, but I guess this is not what we are talking about.

Categories

Resources