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");
}
}
}
}
}
Related
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!
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 have the following XAML
<Window x:Class="SimpleAttahEvent.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<Grid>
<StackPanel Margin="5" Name="stackButton" ButtonBase.Click="DoSomething">
<Button Name="cmd1" Tag="The first button"> Command 1</Button>
<Button Name="cmd2" Tag="The second button"> Command 2</Button>
<Button Name="cmd3" Tag="The third button"> Command 3</Button>
</StackPanel>
</Grid>
</Window>
...With the following code to handle the attached events.
namespace SimpleAttahEvent
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
stackButton.AddHandler(Button.ClickEvent, new RoutedEventHandler(DoSomething));
}
private void DoSomething(object sender, RoutedEventArgs e)
{
Console.WriteLine(Button.ClickEvent.RoutingStrategy);
Console.WriteLine(TextBox.PreviewKeyDownEvent.RoutingStrategy);
if (e.Source == cmd1)
{
MessageBox.Show("First button is clicked");
}
if (e.Source == cmd2)
{
MessageBox.Show("Second button is clicked");
}
if (e.Source == cmd3)
{
MessageBox.Show("Third button is clicked");
}
}
}
}
These produce a dialog box with 3 buttons stacked vertically. When I click one of the button, a messagebox comes up with an OK button. However, the OK button on the dialogue box won't close unless I clicked it twice. Did I do this implicitly from the code given above?
Edit - Additional Info:
When I do this instead
private void DoSomething(object sender, RoutedEventArgs e)
{
object tag = ((FrameworkElement)sender).Tag;
MessageBox.Show((string)tag);
}
..it still require 2 clicks to close the message box.
Your problem is that you are doubling your handler. You do not have to click twice on the same OK; you are clicking on OK, which closes the first message. Then, the event is handled again and you get another exact same message that you have to click OK on. If you add + DateTime.Now to your messages you will see that this is indeed a second message
I missed this line on my first glance:
stackButton.AddHandler(Button.ClickEvent, new RoutedEventHandler(DoSomething));
Which is the same as the ButtonBase.Click from this line
<StackPanel Margin="5" Name="stackButton" ButtonBase.Click="DoSomething">
Choose one way to attach to event handlers and stick to it. Mixing them up is just going to cause confusion.
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.
I have a popup with StaysOpen=False so I want to close it by clicking anywhere outside of popup. Inside a popup I have a DataGrid. If I open popup and then click somewhere else the popup will be closed. But it won't happen if before clicking outside of popup I will click on column header in DataGrid. Test XAML:
<Window x:Class="Test.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" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="Black">
<Grid>
<ToggleButton x:Name="btn" VerticalAlignment="Top">Open</ToggleButton>
<Popup StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=btn}" >
<DataGrid Width="150" Height="150">
<DataGrid.Columns>
<DataGridTextColumn Header="Column" />
</DataGrid.Columns>
</DataGrid>
</Popup>
</Grid>
</Window>
I think that it happens because column header captures the mouse on click and popup doesn't receive mouse events anymore. I've tried to add a handler on LostMouseCapture event in order to capture mouse back by popup but it doesn't seem to work that easy. Any ideas?
Maybe it will help.
Attached behavior:
public class DataGridColumnHeaderReleaseMouseCaptureBehavior {
public static DataGrid GetReleaseDGCHeaderBehavior(DependencyObject obj) {
return (DataGrid)obj.GetValue(ReleaseDGCHeaderBehaviorProperty);
}
public static void SetReleaseDGCHeaderBehavior(DependencyObject obj, Boolean value) {
obj.SetValue(ReleaseDGCHeaderBehaviorProperty, value);
}
public static readonly DependencyProperty ReleaseDGCHeaderBehaviorProperty =
DependencyProperty.RegisterAttached("ReleaseDGCHeaderBehavior",
typeof(DataGrid),
typeof(DataGridColumnHeaderReleaseMouseCaptureBehavior),
new UIPropertyMetadata(default(DataGrid), OnReleaseDGCHeaderBehaviorPropertyChanged));
private static Popup _popup;
private static void OnReleaseDGCHeaderBehaviorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var oldGrid = (DataGrid)e.OldValue;
if (oldGrid != null)
oldGrid.MouseLeave -= OnMouseLeave;
var refSender = d as Popup;
_popup = refSender;
if (refSender != null) {
var refGrid = e.NewValue as DataGrid;
if (refGrid != null) {
refGrid.MouseLeave += OnMouseLeave;
}
}
}
static void OnMouseLeave(object sender, MouseEventArgs args) {
if (_popup != null)
typeof(Popup).GetMethod("EstablishPopupCapture", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(_popup, null);
}
}
XAML:
<Popup x:Name="popup"
bhvrs:DataGridColumnHeaderReleaseMouseCaptureBehavior.ReleaseDGCHeaderBehavior="{Binding ElementName=dataGrid}">
<DataGrid x:Name="dataGrid"/>
</Popup>
I think you've stumbled onto just a plain old bug. I've reproduced this and could not find a reasonable way to get it working. I think you should file a bug with Microsoft. It seems like a component that captures the mouse and the uncaptures it doesn't restore the capture to the originally capturing component.
I had a similar problem recently altough not exactly the same, and it was in Silverlight. I hacked my way through it by searching the required control (in your case the popup I guess) with the GetTemplatedParent function, in the required event handler of the 'misbehaving' control, and programatically do what I wanted to do on it.
This is not a nice solution, and doesn't solve all the problems, but you can give it a try. Be sure you comment what have you done, because it can turn into a mess.
i had the same problem,and did something like this:
private void YourDataGrid_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
YourDataGrid.CaptureMouse();
YourDataGrid.ReleaseMouseCapture();
}
but i'm looking for something better yet...