How to find clicked UIElement child - wpf

I would like to get the clicked UIElement's child element that is button. Maybe there is simple and short solution for this? I have searched for this answer awhile, but could't find solution that would be easy to understand and use. I will appreciate any kind of help related to this question.
Code that i have right now:
private new void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender == (sender as UIElement))
{
//TODO: getting controls name that is placed inside clicked UIElement
}
}
Edit:
Wanted to mention that UIElement is ContentControl that is using ResourceDictionary template.
My xaml code looks something like this
<ContentControl Style="{StaticResource DesignerItemStyle}">
<Button x:Name="btnAdd" Content="add function" IsHitTestVisible="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</ContentControl>

There are two properties in MouseButtonEventArgs which you can leverage for this purpose
Source
This property contains reference to the object that raised the event. example Button etc
OriginalSource
the original reporting source as determined by pure hit testing, before any possible Source adjustment by a parent class, which may have been done to flatten composited element trees. example a Rectangle, Border or any template element inside the Button.
you can retrieve the Name for the element by casting OriginalSource to FrameworkElement or more appropriate
if the OriginalSource is not the desired element then you can retrieve the Logical parent of OriginalSource and that is more likely to be the desired element and retrieving Name remain same as above.
retrieve logical parent example
LogicalTreeHelper.GetParent(e.OriginalSource as DependencyObject);

Related

Canvas in ScrollViewer (Preview)MouseButtonDown event order

If we have
<ScrollViewer Name="scroll_viewer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Canvas Name="canvas" Height="200" Width="200">
<Rectangle Fill="AliceBlue" Width="100" Height="100"/>
</Canvas>
</ScrollViewer>
with handlers for:
scroll_viewer.PreviewMouseLeftButtonDown
scroll_viewer.MouseLeftButtonDown
canvas.PreviewMouseLeftButtonDown
Then if we click in the Rectangle we get scroll_viewer_PreviewMouseLeftButtonDown called first then canvas_PreviewMouseLeftButtonDown but scroll_viewer_MouseLeftButtonDown is not called.
I want to handle the click event first in the canvas - if an object is clicked I want to handled the event (for object drag). If no canvas object is clicked I want to handle event in scroll_viewer (to manage scrollview panning with the mouse).
How to manage this given that the call order is the oposite of what i want and that the non perview version scroll_viewer.MouseLeftButtonDown is not called?
UPDATE:
From this post: Silverlight forums
((FrameworkElement)scroll_viewer.GetValue(ScrollViewer.ContentProperty)).MouseLeftButtonDown += scroll_viewer_MouseLeftButtonDown;
DOES work ie does get called after the preview events - can some explain why this less than obvious syntax is required?
The problem is that the ScrollViewer already handles the MouseLeftButtonDown event internally, like so:
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) {
if (base.Focus())
e.Handled = true;
base.OnMouseLeftButtonDown(e);
}
You can "fix" this using a custom class, like so:
public class MyScrollViewer : ScrollViewer {
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) {
base.OnMouseLeftButtonDown(e);
e.Handled = false;
}
}
SIDE NOTE: You should use x:Name in XAML, not Name. Otherwise you may run into compilation errors using the above class.
Alternatively, you could attach your handler for all MouseLeftButtonDown events, including handled ones. So instead of:
this.scroll_viewer.MouseLeftButtonDown += new MouseButtonEventHandler(scroll_viewer_MouseLeftButtonDown);
You'd use:
this.scroll_viewer.AddHandler(ScrollViewer.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.scroll_viewer_MouseLeftButtonDown), true);
The Preview events follow a routing strategy similar to the Tunneling strategy, meaning that the event starts at the top of the element tree, and travels down it. So it would hit your ScrollViewer first, then your Canvas.
The non-Preview events follow a routing strategy similar to the Bubbling strategy, meaning that events start on the object they occurred on, and travel up the element tree. In this case, the Canvas would get hit first, then the ScrollViewer.
You can read more about the Routing strategies here
As a side note, for Canvas objects to be visible for HitTest events, they need to have a non-transparent background. So if you have a Canvas with no background color specified, it will default to Transparent and not be visible for HitTests.

SL4: need to register for a move (or redraw) event on an Item in an ItemsControl

Not finding a move event or redraw event in the FrameworkElement class. And Google not helping either. So...
I have a custom ItemsControl populated by an observable collection in the VM. The ItemsControl itself leverages the
<i:Interaction.Behaviors>
<ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</i:Interaction.Behaviors>
behavior so the user can drag around the whole assembly.
When the user moves the assembly, I want to be notified by each item as the item is repositioned as a result of the assembly moving. So far I have tried registering for
this.myItem.LayoutUpdated += this.OnSomethingNeedsToUpdate;
but it doesn't seem to fire as I drag the assembly around.
Also
this.myItem.MouseMove += this.OnSomethingNeedsToUpdate;
only works if I mouse into the item which is not good enough. Because I am moving the ItemsControl and then have to go mouse into the item to get the event to fire.
Any ideas? Can I look to some ancestor in the visual tree for help in the form of a OneOfMyDecendantsWasRedrawn event or similar? Again I am trying to be notified when an item moves not be notified when the assembly moves.
I would say your best bet would be to add the MouseDragElementBehavior to your custom ItemsControl in code instead of in the Xaml. Here is how this might look (using a Grid since that is easier to demo):
public class DraggableGrid : Grid
{
public DraggableGrid()
{
Loaded += new RoutedEventHandler(DraggableGrid_Loaded);
}
void DraggableGrid_Loaded(object sender, RoutedEventArgs e)
{
MouseDragElementBehavior dragable = new MouseDragElementBehavior();
Interaction.GetBehaviors(this).Add(dragable);
dragable.Dragging += new MouseEventHandler(dragable_Dragging);
}
void dragable_Dragging(object sender, MouseEventArgs e)
{
// Custom Code Here
}
}
In the section that says Custom Code Here you would loop through you Items and notify them that they are being dragged.
I ended up writting another behavior for the individual items I care about and then wrote a LINQ query to search up the visual tree looking for ancestors with the MouseDragElementBehavior attached to them. That query found the ItemsControl since it was an eventual parent of the Item. I was then able to register for the Dragging event as desried.
Thanks again to Bryant for providing the solution over here.

How to bubble events in Silverlight usercontrols?

I am trying to bubble an event from a child usercontrol to its parent.
The child usercontrol is a button inside a grid:
<UserControl>
<Grid>
<Button Click="Button_Click" />
</Grid>
</UserControl>
The parent usercontrol is composed by many instances of the child control:
<UserControl>
<StackPanel>
<customs:myButton CustomClick="something" />
<customs:myButton CustomClick="something" />
<customs:myButton CustomClick="something" />
etc.
</StackPanel>
</UserControl>
In the child usercontrol I have defined:
public delegate void CustomClickHandler(object sender, EventArgs e);
public event CustomClickHandler CustomClick;
and the "inner" button handles the click event in this way:
private void Button_Click(object sender, EventArgs e)
{
if (CustomClick != null)
CustomClick (sender, e);
}
I've tried to check what it is going on, and I can see the Button_Click is invoked, CustomClick is not null and it gets executed. However nothing seems to happen, the code attached to that even in the parent usercontrol is not called.
Any suggestion?
Thanks in advance,
Cheers,
Gianluca.
What you are seeking is called routed events. You can write your own custom ones or take a look at this library.
Ok, I have sorted this out, there were few problems though.
The first issue was caused by IsHitTestVisible. In some online articles it was said to set that property to false, in order to resolve some mouse-event related issues. I did so, but that was wrong because the element with that property set to false was "catching" the mouse events, and they were not arriving anymore to the inner usercontrols.
Secondly, in the most internal button (see my post above to understand the scenario), in order to get this to work, I've had to set the ClickMode="Hover" and to handle the MouseLeftButtonUp event. I'll try to use the standard click but I read somewhere that only certain event bubble up...
e.Handled did not need any change: I've checked and it was already false.
Neither I had to use any RoutedEvent library...
I cannot think of anything else I did to resolve the issue.
I guess this is all.
As always, if you have any suggestion, please feel free to add them here.
Cheers,
Gianluca.

WPF expand TreeView on single mouse click

I have a WPF TreeView with a HierarchicalDataTemplate.
Currently I have to double click an item to expand/collapse it.
I would like to change this behaviour to a single click, without loosing other functionality. So it should expand and collapse on click.
What is the recommended way to do this?
Thanks!
You could use a re-templated checkbox as your node (containing whatever template you are currently using) with its IsChecked property bound to the IsExpanded property of the TreeViewItem.
Here is a template I've just test that seems to do the job:
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=IsExpanded}">
<CheckBox.Template>
<ControlTemplate>
<TextBlock Text="{Binding Header}"></TextBlock>
</ControlTemplate>
</CheckBox.Template>
</CheckBox>
</HierarchicalDataTemplate>
Just replace the ControlTemplate contents with whatever you need.
If you are using a standard TreeViewItem, then you can capture the click event:
private void OnTreeViewMouseUp( object sender, MouseButtonEventArgs e )
{
var tv = sender as TreeView;
var item = tv.SelectedItem as TreeViewItem;
if( item != null )
item.IsExpanded = !item.IsExpanded;
e.Handled = true;
}
private void OnTreeViewPreviewMouseDoubleClick( object sender, MouseButtonEventArgs e )
{
e.Handled = true;
}
Most likely in your case, you'll need to do something with your binding and ViewModel. Here's a good article from CodePlex: Simplifying the WPF TreeView by Using the ViewModel Pattern.
Just use selected item changed event and use the following,
private void treeview_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
TreeViewItem item = (TreeViewItem)treeview.SelectedItem;
item.IsExpanded = true;
}
where treeview is the name of your TreeView, you could include an if to close/open based on its current state.
I have very little experience working with WPF to this point, so I am not 100% certain here. However, you might check out the .HitTest method of both the Treeview and TreeView Item (the WPF Treeview is essentially the Windows.Controls.Treeview, yes? Or a derivation thereof?).
THe HIt Test method does not always automatically appear in the Intellisense menu for a standard Windows.Forms.Treeview (I am using VS 2008) until you type most of the method name. But it should be there. You may have to experimnt.
You can use the .HitTest Method to handle the MouseDown event and return a reference to the selected treeview item. You must test for a null return, however, in case the use clicks in an area of the control which contains no Tree Items. Once you have a reference to a specific item, you should be able to set its .expanded property to the inverse of whatever it is currently. again, some experimentation may be necessary here.
As I said, I have not actually used WPF yet, so I could have this Wrong . . .
The answer of Metro Smurf (thanks to which I got where I wanted to be) suggests the right approach . You could simply hook up to the SelectedItemChanged event of the Treeview. Then cast the e.NewValue passed in the eventhandler as TreeViewItem, and access its IsExpanded property to set it to true.
void MyFavoritesTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
((TreeViewItem)e.NewValue).IsExpanded = true;
}
Then for the final touch, you can also hook up the items in your Treeview by casting them as TreeViewItem as suggested, and then you can hook up to the various manipulation events, like:
var item = tv.SelectedItem as TreeViewItem;
item.Expanded += item_Expanded;
And then do whatever you need to do in the eventhandler
void item_Expanded(object sender, RoutedEventArgs e)
{
// handle your stuff
}

Print Button inside DataTemplate? How to write code

I have a ContentControl element whose ContentTemplate is determined at run time from the Resource Dictionary. In the datatemplate I have a visual (Convas) and what I want is to also have a button in datatemplate which when clicked should print the visual element(canvas).
As I said that the DateTemplate is inside the Resource Dictionary so How can I write a Code on Click Event of that button and where it should be?
Any response would be much appreciated.
Sounds like you can use the Button.Click attached event. Just add it to the ContentControl.
<ContentControl
Button.Click="Button_Click"
ContentTemplate="<template with a button>"
/>
and the handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
}
If you have multiple buttons in your template, you can use e.Source to figure it out. And I think you can use MouseButtonEventArgs instead.

Resources