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

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!

Related

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

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

Hit-Testing does not work in WPF Border with lower Z-index

I have a piece of code with two Border elements, but the hit-testing only works for the topmost Border (Border2) in the code below. This means that when I right-click, I see the message box, but when I left-click, nothing happens. Is there a way to fix this so that I can capture different mouse events with sibling controls that have different Z-index values? Here is my code:
<Window x:Class="HiTest.MainWindow"
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"
xmlns:local="clr-namespace:HiTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:Border1 Background="Transparent"/>
<local:Border2 Background="Transparent"/>
</Grid>
</Window>
public class Border1 : Border
{
public Border1()
{
MouseLeftButtonDown += Border1_MouseLeftButtonDown;
}
private void Border1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Left");
}
}
public class Border2 : Border
{
public Border2()
{
MouseRightButtonDown += Border2_MouseRightButtonDown;
}
private void Border2_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Right");
}
}
In summary, I want to be able to capture different mouse events with sibling controls that have different Z-index values. How can I achieve this?
it is not possible to capture events for sibling elements. Bubbling only works for elements with parent and child relation.
Something like this with parent child relation.
<Grid Name="Parent">
<Border Name="Border01" Background="Transparent" MouseDown="Border01_MouseDown">
<Border Name="Border02" Background="Transparent" MouseDown="Border01_MouseDown" />
</Border>
</Grid>
private void Border01_MouseDown(object sender, MouseButtonEventArgs e)
{
if (sender.GetType() == typeof(Grid)) { }
if (sender.GetType() == typeof(Border))
{
if (((Border)sender).Name == "Border01" & e.LeftButton == MouseButtonState.Pressed)
{
MessageBox.Show("Left & Button01");
}
if (((Border)sender).Name == "Border02" & e.RightButton == MouseButtonState.Pressed)
{
MessageBox.Show("Right & Button02");
}
}
}
If you have sibling elements you need to delegate the MouseDown from the top element to the lower element by yourself.

Button sending same object in both mouse down and mouse up event

I created 2 button in WPF window and also added mouse down and mouse up event for both buttons. I did mouse down on one button and mouse up on second. but i am getting same first button object to event handler in both events. My question is why i am not getting the second button object in mouse up event.
This is my XAML
<Window x:Class="MouseDownUpSample.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>
<Button x:Name="but1" Content="source" HorizontalAlignment="Left" Margin="86,68,0,0" VerticalAlignment="Top" Width="75" PreviewMouseLeftButtonDown="Button_MouseDown" PreviewMouseLeftButtonUp="Button_MouseUp" />
<Button x:Name="but2" Content="destination" HorizontalAlignment="Left" Margin="406,164,0,0" VerticalAlignment="Top" Width="75" PreviewMouseLeftButtonDown="Button_MouseDown" PreviewMouseLeftButtonUp="Button_MouseUp"/>
</Grid>
Code
public partial class MainWindow : Window
{
string source = null;
string destination = null;
public MainWindow()
{
InitializeComponent();
}
private void Button_MouseDown(object sender, MouseButtonEventArgs e)
{
Button src=sender as Button;
source = src.Content as string;
}
private void Button_MouseUp(object sender, MouseButtonEventArgs e)
{
Button src = sender as Button;
destination = src.Content as string;
if(destination.Equals(source))
{
}
}
I am trying to transfer data from one object to another through drag & drop
My question is why i am not getting the second button object in mouse up event.
Because this is how buttons work.
Taken from MSDN:
If a mouse button is pressed while the pointer is over a form or control, that object "captures" the mouse and receives all mouse events up to and including the last MouseUp event.
This might also interest you:
If mouse buttons are pressed in succession, the object that captures the mouse after the first press receives all mouse events until all buttons are released.
Mouse.MouseDown occurs when any mouse button is pressed, where as Mouse.MouseUp occurs when any mouse button is released. So when you click a button MouseUp event is always followed by MouseDown event since they are sequential events. Hence your if() condition is always true in this case.
if(destination.Equals(source))
{
//always executed;
}
I achieved my goal through WPF drag & drop using DragDropEffects.Copy
XAML
<Window x:Class="MouseDownUpSample.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>
<Button x:Name="but1" Content="source" HorizontalAlignment="Left" Margin="86,68,0,0" VerticalAlignment="Top"
Width="75" PreviewMouseMove="but_MouseMove" AllowDrop="True" PreviewDrop="but_Drop" />
<Button x:Name="but2" Content="destination" HorizontalAlignment="Left" Margin="406,164,0,0" VerticalAlignment="Top"
Width="75" PreviewMouseMove="but_MouseMove" AllowDrop="True" PreviewDrop="but_Drop"/>
</Grid>
CODE
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void but_MouseMove(object sender, MouseEventArgs e)
{
Button src=sender as Button;
if (src != null && e.LeftButton == MouseButtonState.Pressed)
{
DragDrop.DoDragDrop(src,
src.Content.ToString(),
DragDropEffects.Copy);
}
}
private void but_Drop(object sender, DragEventArgs e)
{
Button dest = sender as Button;
string destinationContent = null;
destinationContent = dest.Content as string;
if (dest != null)
{
if (e.Data.GetDataPresent(DataFormats.StringFormat))
{
string sourceContent = (string)e.Data.GetData(DataFormats.StringFormat);
if (destinationContent.Equals(sourceContent))
{
Console.WriteLine("equal");
}
}
}
}
}

WPF "Hidden" behind a Panel

I am encountering an issue in my project and i don't have much more time to do research.
The goal of this project is to allow attending to tests without being physically present.
You receive a file containing your tests, you attend to them (time limited) and send back the file containing your answers.
I got a TextBox in a StackPanel, itself contained in another StackPanel.
All the controls are created programatically.
The controls are added correctly but the TextBox don't react to mouse input.... (in fact only when the textbox is the ast item and even ther only the little last pixel)
UserControl XAML file :
<UserControl x:Class="DataLibrary.View.Questions.ListQuestionInterface"
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:localization ="clr-namespace:DataLibrary.Resources"
xmlns:convert ="clr-namespace:DataLibrary.View.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Loaded="ListQuestionInterface_OnLoaded">
<UserControl.Resources>
<localization:LocalizedStrings x:Key="LocalizedStrings"/>
<convert:getVisible x:Key="getVisible"/>
<convert:getText x:Key="getText"/>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" x:Name="body" Grid.Row="0" FocusManager.IsFocusScope="True"/>
<Label Grid.Row="0" Margin="0,10,0,0" x:Name="explanations"/>
<Button Content="{Binding Path=type, Converter={StaticResource getText}}"
HorizontalAlignment="Right"
Margin="0,0,10,10"
VerticalAlignment="Bottom"
Grid.Row="1"
Width="120"
Height="20"
Click="DisplayAnswerButton_Click"
Visibility="{Binding Path=type, Converter={StaticResource getVisible}}"/>
</Grid>
</UserControl>
Code Behind:
public partial class ListQuestionInterface : UserControl
{
private UIElement _firstElement;
ListQuestion q;
private bool isTest;
public questionType type
{
get
{
return q.Type;
}
set
{
Console.WriteLine("Attempted to write questionType");
}
}
public ListQuestionInterface(ListQuestion question, bool isTest = true)
{
InitializeComponent();
this.explanations.Content = question.Explanation;
this.DataContext = this;
this.q = question;
this.isTest = isTest;
refreshStackPanel();
}
private void refreshStackPanel()
{
bool first = true;
this.body.Children.Clear();
var enumerators = new Hashtable();
foreach (Question subQuestion in this.q.SubQuestions)
{
enumerators.Add(subQuestion, subQuestion.interfaceEnumerator(isTest).GetEnumerator());
((IEnumerator)enumerators[subQuestion]).MoveNext();
}
//If the Alignemnt property has a value we'll want each pair of control to be aligned wit heach other
//if not, we just want them stacked to the left
if (q.Alignment.HasValue)
{
int maxCount = this.q.SubQuestions.Max(x => x.interfaceEnumerator(isTest).Count());
for (int i = 0; i < maxCount; i++)
{
var stack = new StackPanel
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Center
};
foreach (Question subQuestion in this.q.SubQuestions)
{
try
{
var enumerator = (IEnumerator)enumerators[subQuestion];
var control = enumerator.Current as Control;
((Panel)VisualTreeHelper.GetParent(control)).Children.Remove(control);
control.HorizontalAlignment = q.Alignment.Value;
Canvas canvas = null;
if (control.GetType() == typeof(Button) || control.GetType() == typeof(MaskedTextBox))
{
canvas = new Canvas();
if (control.GetType() == typeof(MaskedTextBox))
{
var thick = control.Margin;
thick.Left -= 5;
control.Margin = thick;
}
if (first)
{
this._firstElement = control;
first = false;
}
control.Focusable = true;
canvas.Children.Add(control);
}
if (canvas == null)
{
stack.Children.Add(control);
}
else
{
stack.Children.Add(canvas);
}
enumerator.MoveNext();
}
catch
{
var blank = new Label
{
Content = "BLANK",
Visibility = Visibility.Hidden
};
stack.Children.Add(blank);
Console.WriteLine("No more items to display");
}
}
this.body.Children.Add(stack);
}
}
else
{
var stack = new StackPanel
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Center,
};
foreach (var subQuestion in q.SubQuestions)
{
var subStack = new StackPanel
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Left,
Orientation = Orientation.Horizontal
};
var enumerator = subQuestion.interfaceEnumerator(isTest).GetEnumerator();
while (enumerator.MoveNext())
{
var control = enumerator.Current as Control;
control.HorizontalAlignment = HorizontalAlignment.Left;
if (control.GetType() == typeof(Button) || control.GetType() == typeof(MaskedTextBox))
{
if (first)
{
this._firstElement = control;
first = false;
}
control.Focusable = true;
}
((Panel)VisualTreeHelper.GetParent(control)).Children.Remove(control);
subStack.Children.Add(control);
}
stack.Children.Add(subStack);
}
this.body.Children.Add(stack);
}
}
private void DisplayAnswerButton_Click(object sender, RoutedEventArgs e)
{
foreach (Question question in q.SubQuestions)
{
question.DisplayAnswers();
}
refreshStackPanel();
}
private void ListQuestionInterface_OnLoaded(object sender, RoutedEventArgs e)
{
this._firstElement.Focus();
}
}
}
I don't think the button have somethng to do with the problem so i'll leave the converters code away.
I've checked some things already :
IsHitTestVisible is never set to false
Defining one of the stackpanels as the FocusScope don't change anything
If i place all my controls into canvas and the canvas into the stackpanel i can freely click on my controls but their placement is completely broken.
The Actual Width/height of the control is sufficient to interact with them(60x20)
The problem seems to have appeared just after using the following trick to set a first focused element on other UserControls (which are not in the current VisualTree anymore)
I really do need help since i can't seem to find someone with a similar problem.
And here are two screenshots to illustrate the problem :
The black arrows shows where my i clicked before taking the screenshot (btw if you know of any software that can do a screenshot WITH the mouse i'm taking it :) )
Ok, my fault here -_-'
I was so tired that i didnt't see that my stackpanel was in fact really BEHIND a Label
In my code i only had 2 row definitions, in the first i put the stackpanel AND a Label (who took the entire space).
And because it was declared later, the label was above the stackpanel thus preventing any mouse interaction with it's content.
Here is the corrected XAML :
<UserControl x:Class="DataLibrary.View.Questions.ListQuestionInterface"
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:localization ="clr-namespace:DataLibrary.Resources"
xmlns:convert ="clr-namespace:DataLibrary.View.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Loaded="ListQuestionInterface_OnLoaded">
<UserControl.Resources>
<localization:LocalizedStrings x:Key="LocalizedStrings"/>
<convert:getVisible x:Key="getVisible"/>
<convert:getText x:Key="getText"/>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" x:Name="body" Grid.Row="0" FocusManager.IsFocusScope="True"/>
<Label Grid.Row="1" Margin="0,10,0,0" x:Name="explanations"/>
<Button Content="{Binding Path=type, Converter={StaticResource getText}}"
HorizontalAlignment="Right"
Margin="0,0,10,10"
VerticalAlignment="Bottom"
Grid.Row="2"
Width="120"
Height="20"
Click="DisplayAnswerButton_Click"
Visibility="{Binding Path=type, Converter={StaticResource getVisible}}"/>
</Grid>
</UserControl>
So in fact, the control was really hidden behind another one -_-'
I now know that this isn't the WPF way of doing things, but i do not know yet how to DataBind to a Template properly
I'm still taking any advices on a good tutorial/starting point for binding to a DataTemplate in XAML, tho only DataBindings i could find were for binding single values to control properties

How to display a pop-up?

I created a pop-up and want to show it when a button has been clicked. But it doesn't work because it waits for finishing button's duty.
I don't want to use "timer, thread and background worker" because I already can do it with that. I am looking for an another solution…
Here is my code:
void btnSearch_Click(object sender, RoutedEventArgs e)
{
LoadingOpen();
if (cmbvideoserver.Text == "Youtube")
SearchInYoutube();
else if (cmbvideoserver.Text == "Vimeo")
SearchInVimeo();
LoadingClose();
}
public void LoadingOpen()
{
myPopup.IsOpen = true;
Common.Popup = true;
window.Opacity = 0.3;
}
public void LoadingClose()
{
myPopup.IsOpen = false;
Common.Popup = false;
window.Opacity = 1;
}
and the XAML:
<Window Name="window" x:Class="youtube.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" Loaded="window_Loaded">
<Canvas Name="main">
<Popup Name="myPopup" IsOpen="False" PopupAnimation="Slide"
Margin="100,10,20,0" Placement="Center" >
<Label Content="Loading..."></Label>
</Popup>
</Canvas>
</Window>
How can I show popup on my main window ? (without any thread,timer and background controls/classes)
You can show Windows as dialogs by useing the ShowDialog() method.

Categories

Resources