I got a canvas with some children.
<Canvas Name="Canvas" MouseDown="getElements">
<Rectangle Height="200" Width="200" Name="Element1"/>
<Rectangle Height="200" Width="200" Name="Element2"/>
</Canvas>
and by clicking on a canvas element I want to get the name of the clicked element with something like this:
public void getElements(object Sender, EventArgs e)
{
DependencyObject dpobj = Sender as DependencyObject;
string name = dpobj.GetValue(FrameworkContentElement.NameProperty) as string;
Console.WriteLine("Element Clicked: " + name);
}
but I only get the name of the canvas. can anybody help me to get the names of the clicked elements?
thanks in advance!
Since MouseDown is routed event instead of EventArgs use RoutedEventArgs or for MouseDown you can use MouseButtonEventArgs. It will give you OriginalSource property and you can try getting Name of e.OriginalSource:
private void getElements(object sender, MouseButtonEventArgs e)
{
var elementName = (e.OriginalSource as FrameworkElement).Name;
}
The event is routed from the elements in the canvas. To get the original source you can use the RoutedEventArgs instead:
public void getElements(object Sender, RoutedEventArgs e)
{
DependencyObject dpobj = e.OriginalSource as DependencyObject;
string name = dpobj.GetValue(FrameworkContentElement.NameProperty) as string;
Console.WriteLine("Element Clicked: " + name);
}
You can create an Event Handler for the MouseLeftButtonUp event on your Canvas (as WPF uses Routed Events, the event will 'bubble' up to parents and will be raised there, too). The XAML code would look somewhat like this:
<Window x:Class="CanvasChildren.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">
<Canvas MouseLeftButtonUp="UIElement_OnMouseLeftButtonUp">
<Rectangle Canvas.Top="10" Canvas.Left="10" Height="200" Width="200" Name="Element1" Fill="LightBlue"/>
<Rectangle Canvas.Left="250" Canvas.Top="100" Height="200" Width="200" Name="Element2" Fill="DarkSalmon"/>
</Canvas>
</Window>
In your code behind file, you can retrieve the original object that was clicked by accessing the Source property of the event arguments, like shown in this code:
private void UIElement_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var rectangle = (Rectangle) e.Source;
var name = rectangle.Name;
}
You can download my full example here (it's a Dropbox link).
Related
I created a new WPF app in Visual Studio and I placed a button using the drag and drop editor but I can't access the button in my .cs file using
MainButton.Content = "Set output to red";
but I get an error
System.NullReferenceException: 'Object reference not set to an instance of an object.'
MainButton was null while running the application.
The drag and drop editor generated this xaml file
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Border HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="Black" BorderThickness="3">
<TextBlock x:Name="Output" Background="Transparent" TextAlignment="Center" TextWrapping="Wrap" Text="Output" Height="88" Width="264"/>
</Border>
<RadioButton x:Name="Option1" Content="Red Pill" HorizontalAlignment="Left" Margin="135,75,0,0" VerticalAlignment="Top" Checked="RadioButton_Checked" IsChecked="True"/>
<RadioButton x:Name="Option2" Content="Blue Pill" HorizontalAlignment="Left" Margin="536,72,0,0" VerticalAlignment="Top" Checked="RadioButton_Checked_1"/>
<Button x:Name="MainButton" Content="Set output to red" HorizontalAlignment="Left" Margin="279,100,0,0" VerticalAlignment="Top" Width="213" Height="41" Click="MainButton_Click"/>
</Grid>
</Window>
Here's the .cs file
using System.Windows;
using System.Windows.Media;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MainButton_Click(object sender, RoutedEventArgs e)
{
if ((bool)Option1.IsChecked)
{
Output.Background = Brushes.Crimson;
}
else
{
Option2.IsChecked = true;
Output.Background = Brushes.DodgerBlue;
}
}
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
MainButton.Content = "Set output to red";
}
private void RadioButton_Checked_1(object sender, RoutedEventArgs e)
{
MainButton.Content = "Set output to blue";
}
}
}
I can access other things in the UI just fine like radio buttons and text blocks but not the button. Why could this be happening?
During the initialization phase, some variables are going to be null since it hasn't been reached in the call order. RadioButton_Checked is called through event before the button is constructed since it contains the Checked property.
A quick and easy fix is as follows: Check for null in your event calls.
private void RadioButton_Checked (object sender, RoutedEventArgs e)
{
if(MainButton != null)
MainButton.Content = "Set output to red";
}
private void RadioButton_Checked_1 (object sender, RoutedEventArgs e)
{
if (MainButton != null)
MainButton.Content = "Set output to blue";
}
Of course, there are better ways to handle this. You could set checked on a separate event, Initialized, which will handle it much cleaner.
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.
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");
}
}
}
}
}
i have this code
<Grid Width="160" x:Name="grd">
<Grid.Background>
<ImageBrush x:Name="img" ImageSource="Assets/Icons/tab-inactive.png" />
</Grid.Background>
<TextBlock x:Name="txtacticve" Text="Rating" Tap="TextBlock_Tap_1" />
</Grid>
while tapping the textblock the tap event will be called.how can i get the complete path of the imagesource in this tap event in code behind? i am really at the lost here how to get to the imagesource in my codebehind event.
One solution could be the following one :
In your XAML code, you can bind your TextBlock.Tag property to the Grid.Background property you need. It could be useful to do it in XAML rather than code behind if your Grid is not the first parent (no need to find the required parent with recursive C# code) :
<Grid Width="160" x:Name="grd">
<Grid.Background>
<ImageBrush x:Name="img" ImageSource="Assets/Icons/tab-inactive.png" />
</Grid.Background>
<TextBlock x:Name="txtacticve" Text="Rating" Tap="TextBlock_Tap_1" Tag="{Binding ElementName=grd, Path=Background}" />
</Grid>
Then in your code behind, you just have to cast and use the TextBlock.Tag property that way :
private void TextBlock_Tap_1(object sender, System.Windows.Input.GestureEventArgs e)
{
var textBlock = sender as TextBlock;
if (textBlock != null)
{
var test = ((BitmapImage)((ImageBrush)textBlock.Tag).ImageSource).UriSource.OriginalString;
}
}
Try this.
private void TextBlock_Tap_1(object sender, System.Windows.Input.GestureEventArgs e)
{
var grid = (Grid)txtacticve.Parent;
var img = grid.Background;
var path = ((BitmapImage)(((ImageBrush)(img)).ImageSource)).UriSource.AbsolutePath;
}
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.