I am trying to make a method where i can get the element that was clicked. In App.xaml.cs i have method OnPreviewMouseDown that is activated for each click in application.
Now i need some help with getting element name from sender (if this is even possible)
static void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.RightButton == MouseButtonState.Pressed)
{
Control control = (Control)sender; // Sender gives you which control is clicked.
string name = control.Name.ToString(); //returns main window name, not element....
string typee = sender.GetType().ToString(); //returns PPPMain.Views.MainWindow
}
}
I tried this and some other suggestions from internet but didn't find any solutions...
Thanks in advance!
Use the OriginalSource property of the MouseButtonEventArgs:
var element = e.OriginalSource as FrameworkElement;
var name = element?.Name;
You could try using this code inside your event:
VisualTreeHelper.HitTest(this, e.GetPosition(this));
you can find more in this other topic: WPF Get Element(s) under mouse
Related
I am trying to handle some drag and drop functionality for values in my TreeView.
I am specifically targeting the Drop method. I want to be able to access the Parent object of the element being dragged.
The
TreeView.SelectedItem
is the property of the TreeView that I suspect I will need. However, this property is of type string, so accessing the Parent object is proposing a challenge.
I tried using the
VisualTreeHelper.GetParent(DependencyObject)
method but there doesn't seem to exist a conversion from string to DependencyObject and vice versa.
I am able to access the ParentControl of the target, but not the source and I can't understand why.
This is my Drop Method, as well as the MouseMove. Any help would be appreciated.
private void TreeViewItem_Drop(object sender, DragEventArgs e)
{
if (sender is TreeViewItem target)
{
string source = e.Data.GetData(typeof(string)) as string;
string dropTarget = target.DataContext as string;
var parentControl = VisualTreeHelper.GetParent((TreeViewItem)(sender));
var parentElement = ((VirtualizingStackPanel)(parentControl)).DataContext;
if (parentElement is GroupDecision decision)
{
if (decision.Decisions.Contains(source) && (decision.Decisions.Contains(dropTarget)))
{
int sourceIndex = decision.Decisions.IndexOf(source);
int targetIndex = decision.Decisions.IndexOf(dropTarget);
SwapElements(sourceIndex, targetIndex, decision.Decisions);
}
else
{
if (!decision.Decisions.Contains(source))
{
decision.Decisions.Add(source);
// Remove from old
}
}
}
}
private void TreeViewItem_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (TreeView.SelectedItem != null)
{
DragDrop.DoDragDrop(this, TreeView.SelectedItem, DragDropEffects.Move);
}
}
}
The treeview.selecteditem in your dodragdrop there is what ends up in your data when you do your e.Data.GetData. Since you cast it to a string, it's just a string floating about in memory. Adrift on an ocean of not-in-the-visual-tree. It has no parent control, because it's a string.
The sender is whatever control you dropped it on, so that's not the thing you dragged from. It's what you're dragging to.
But... what you pass as data can be whatever you give it.
That can be a simple string or a complicated business object with many properties on it.
Whatever you want when you drop, is best stashed away there in that data.
In the answer here:
WPF drag and drop and data types
That's a viewmodel.
I don't really follow what you want out of this but instead of
DragDrop.DoDragDrop(this, TreeView.SelectedItem, DragDropEffects.Move);
You should put whatever is required as that second parameter.
DragDrop.DoDragDrop(this, AmoreComplexObjectWithMoreData, DragDropEffects.Move);
Define a class which has properties matching whatever you need. New it up and set those as you start dragging. When you drop, you have everything you need then. Cast your drag data back to said class.
A string has no visual ancestor. Maybe you want to pass the TreeViewItem container of the selected item to the DoDragDrop method?:
private void TreeViewItem_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
if (TreeView.SelectedItem != null)
{
DragDrop.DoDragDrop(this,
TreeView.ItemContainerGenerator.ContainerFromItem(TreeView.SelectedItem) as TreeViewItem,
DragDropEffects.Move);
}
}
}
You could then get a reference to the visual container in the Drop event handler and traverse the visual tree as you like:
TreeViewItem source = e.Data.GetData(typeof(TreeViewItem)) as TreeViewItem;
string sourceObject = source.DataContext as string;
//...
I have the following dispatch routine in VS2013 C#:
private void B_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
string src = btn.Name.ToString();
string foo = "G" + src.Substring(1);
G0.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
It currently changes the visibility of G0. I want to change the code so that if Button B123 is pressed, then G123.Visibility is changed.
Thanks,
Dan
Note: This is a generic eventhandler for the buttons. There are 100's of buttons so an individual handler for each button is not practical. It could also be the handler from a dropdown or text box. G123 is a random control on the XAML page. The point is, given a string that contains the Name, how do I find the associated control so that I can modify its properties?
I'm not sure that I correctly understand your question, so I may be guessing that if button buttons B123 and G123 are related to each other by the number 123. In general, I suppose you want to change the visibility of button GX if button BX is changed.
In order to find all controls in the Window, have a look at the solution provided by Bryce Kahle, see Find all controls in WPF Window by type. In your buttenclick handle, do something like
private void B_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
string src = btn.Name.ToString();
string identifier= src.Substring(1);
foreach (var btn in FindVisualChildren<Button>(this).Where(b => b.Name.EndsWith(identifier)))
{
if(btn.Name.StartsWith("G"))
btn.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
Hope that helps.
In the comments, user Clemens gave the answer. (Since he didn't give it as an answer, I can't vote it up.)
Using FindName, I was able to get to the properties of the desired control:
private void B_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
string src = btn.Name.ToString();
string foo = "G" + src.Substring(1);
Windows.UI.Xaml.Shapes.Rectangle rect = (Windows.UI.Xaml.Shapes.Rectangle)this.FindName(foo);
rect.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
This has the flexibility so that I can change the fill, contents, text, foreground, style, etc. for a specified control. More control than if I had simply used XAML binding.
Thanks Clemens,
Dan
I have a tab control in a window. The tabs all have simple context menus which (are supposed to) allow the user to close them. However, when I click close, nothing happens.
Here is the event handler
void closeTab_Click(object sender, RoutedEventArgs e)
{
Tabs.Items.Remove((MenuItem)sender);
}
I've looked around about closing tabs, but none of the articles I found went into much detail about how to actually close the tab.
New problem:
void closeTab_Click(object sender, RoutedEventArgs e)
{
MenuItem close = (MenuItem)sender;
Tabs.Items.Remove(Convert.ToInt32(close.Name.Remove(0,3)));
}
The context menu item is named thusly:
Name = "Tab" + Tabs.Items.Count.ToString(),
It still does nothing
The menu item is not the tab. You cannot remove it from the TabControl. You need a reference to the tab to which the MenuItem belongs. This can be done in various ways.
I see you tried some rather hacky things there with names and string manipulation, here would be a more clean approach which does not require any of that:
var target = (FrameworkElement)sender;
while (target is ContextMenu == false)
target = (FrameworkElement)target.Parent;
var tabItem = (target as ContextMenu).PlacementTarget;
Tabs.Items.Remove(tabItem);
This gets the parent until it finds the ContextMenu and gets the TabItem from the PlacementTarget.
I have a context menu that looks like this
A
|--1
|--2
|--3
I need to access the object that the context menu is called from, after selecting 1 2 or 3
meaning if this is a context menu of a textbox1 then I need to access that object, how do I do that?
Forgot to mention, this is a WPF application. so Im using the System.Windows.Controls
and the ContextMenu is created programmatically
You can walk up the tree and get the control from the ContextMenu.PlacementTarget, e.g.
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
var item = sender as MenuItem;
while (item.Parent is MenuItem)
{
item = (MenuItem)item.Parent;
}
var menu = item.Parent as ContextMenu;
if (menu != null)
{
var droidsYouAreLookingFor = menu.PlacementTarget as TextBox;
//...
}
}
You can look at the SourceControl property of the ContextMenuStrip that owns the context menu item that was clicked.
For example, in the Click handler for the menu item:
private void aToolStripMenuItem_Click(object sender, EventArgs e)
{
var control = ((sender as ToolStripMenuItem).Owner as ContextMenuStrip).SourceControl;
...
}
Of course if you only have one ContextMenuStrip on the form, you can just reference it directly
var control = myContextMenuStrip.SourceControl;
Slight tweak to HB's answer. HB deserves the credit. Helped me find a DataGrid.
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem item = sender as MenuItem;
ContextMenu cm = (ContextMenu)item.Parent;
Popup popup = (Popup)cm.Parent;
var finalGoal = popup.PlacementTarget as DataGrid;
}
use the
ContextMenu.SourceControl
that's the variable that calls the context menu. all you need to do is cast the control
found the answer from a similar question
Get owner of context menu in code
viky's code works, but I had to cast it twice.
I guess looping the casting of the Parent is possible for better flexibility
(more casts depends on how deep the clicked item is)
Ugly Solution
I am searching for a better way to do the same thing. For now, the code below works:
TextBlock tb = ((sender as MenuItem).Parent as ContextMenu).PlacementTarget as TextBlock;
Replace TextBlock with your control's type.
I am working on a node-graph-view similar to Maya's HyperGraph in which I can connect Nodes with drag and drop. Because the target-node can have several Inputs, I want to create a temporary ContextMenu to select the input as suggesting in the following mock-up:
http://www.pixtur.org/images/uploaded/0000/0696/large.jpg
I tried for quite a time to trigger the creation or opening of a context-menu. It looks like the Win32 TrackPopupMenu does roughly, what I'm looking for. Is there an WPF / C# equivalent?
Thanks
pixtur
I would suggest another solution:
In this example a button will raise a context menu with one entry ("Copy") on right click. If the "Copy" context menu item is clicked, a console output is generated.
[..]
var button = new Button();
button.Content = "SomeButtonName";
button.MouseUp += HandleMouseUp;
[..]
private void HandleMouseUp(object sender, MouseButtonEventArgs e)
{
var senderUIControl = sender as Control;
var contextMenu = new ContextMenu();
var item = new MenuItem();
item.Header = "Copy";
item.Click += (o, a) => {
Console.WriteLine("Copy item clicked");
};
contextMenu.Items.Add(item);
senderUIControl.ContextMenu = contextMenu;
}
I use the following code to attach a contextmenu to a listview gricolumn header:
<ListView ... MouseUp="ListView_MouseUp">
In the codebehind i set the ContextMenu property of the list on the mouse up event, in order to show the context menu:
private void ListView_MouseUp(object sender, MouseButtonEventArgs e)
{
DependencyObject depObj = e.OriginalSource as DependencyObject;
while (depObj != null && (!(depObj is GridViewColumnHeader)))
{
depObj = VisualTreeHelper.GetParent(depObj);
}
if (depObj is GridViewColumnHeader && e.ChangedButton == MouseButton.Left)
{
((GridViewColumnHeader)depObj).ContextMenu = ContextMenu;
}
}
The variable ContextMenu refers to a contextmenu instance that i created bfeorehand, you could also create the ContextMenu in the Mouse event handler.
I'm not sure if this helps as I dont know how you do the drag/drop, but it is worth a try