Getting the Logical UIElement under the mouse in WPF - wpf

It seems that all of the way of retrieving an element under the Mouse relates to Visual Hit testing.
Is there some mechanism that I'm missing which would allow me to grab the actual UIElement that represents the current visual tree that the HitTest returns?
Summary of what I'm doing:
I have a custom tooltip class which relies on doing something based on the UIElement that the mouse is over.
Simply put, it hooks into the owning Window's PreviewMouseMove event and updates a "current Item". This current item should represent the UIElement that the mouse is currently over top of.
Unfortunately everything I've encountered with Mouse.DirectlyOver, VisualTreeHelper.HitTest (callbacks included) doesn't work.
Can anyone offer insight in how to accomplish a seemingly simple task in WPF within Window's MouseMove event?

Use the Mouse.DirectlyOver property:
var UIElement = Mouse.DirectlyOver as UIElement;

I don't know any explicit way, but the idea is pretty simple,
1) Find the visual tree element
2) Test if the element is present in logical tree,
3) If it's present, you've found the answer.
4) Otherwise you're gonna have to call VisualTreeHelper.GetParent() and continue the algorithm.
Ps, LogicalTreeHelper.GetParent(your visual tree element) MIGHT also work.

Related

LayoutUpdated event has stopped firing. Handlers are in place, any ideas?

I have a control which allows the user to add and manipulate n graphical and text layers. The layers are each usercontrols that are added to the parent control's canvas. I use the parent control's LayoutUpdated event to refresh transform specific variables and other stuff. With no change to the handler method, the LayoutUpdate event stopped firing. I've obviously done something that caused it, but I haven't been tinkering with anything that can logically tie to this new problem. Any ideas are appreciated - thanks.
JUST TO CLARIFY: I'm not asking for a solution - just an anecdote of experience with something similar - That will probably be more helpful than you can imagine. Thanks
The cause, in this case, was a WPF UserControl (kid) defined in xaml at design time inside a canvas without any dependency property values set. The canvas is inside another usercontrol of different type (mom). At runtime, a mom is instantiated and her kid is sized, positioned, and made visible within the canvas of mom based on the runtime size of mom as well as calling arguments that denote whether or not kid is to be visible. This instance of kid is only sized and positioned if it is requested to be visible, if not, it is only hidden (not sized, not positioned).
The presence of this instantiated, but unsized and unpositioned element within the canvas of mom was causing the failure of layoutupdated events to fire. This situation effected not only the instance in question, but all other instances created at runtime in the project afterward. (NOTE - no exception was raised)
The solution appropriate for this circumstance was to explicitly size and place this element within the canvas - even if it is not needed. The layoutupdated events then fire as expected. I would love to provide a more profound and generalized answer, but perhaps wiser people can include their insight as well. Thanks

How to animate WPF WebBrowser

Just documenting this as a question an answer so that somebody else doesn't have to suffer the same pain.
I have a WPF application that animates pages, much like swiping on an iPhone. All was good until one of the pages needed to contain a WebBrowser. It did not respond at all well to the animation - when it was supposed to slide in, it wouldn't appear until you focused it, and when it was supposed to slide out, it would go away until you moved the mouse over it. In both cases it just popped in/out rather than animating.
Complicating matters, during the project it was decided to move back to .net 3.5 instead of 4 for unrelated reasons.
So the question is: how can I either (a) get the WebBrowser to properly animate; or (b) how can I hide the WebBrowser at the start of animation and show it again at the end. The animation is currently defined in XAML, and I don't particularly want to change it to code.
And a follow up question is: is there a better way, still using .net 3.5?
UPDATE The WPF WebBrowser is so pathetically lame compared to the WinForms one, I have swapped over, using WindowsFormsHost. Everything below still applies, but the WebBrowser is now not so nobbled (eg. it has a DocumentCompleted event).
I pretty quickly gave up on the option to animate the WebBrowser, as it just got all too hard, and instead decided to hide and re-show it. The start of the animation is triggered by a Command on the View Model. It then finds the page that should be displayed, creates it, and kicks off the animation through an attached property that reflects the transition state.
I created an interface, IRequireTransitionInfo, such that a call to IRequireTransitionInfo.TransitioningFrom gives it a chance to hide itself and IRequireTransitionInfo.TransitioningTo to show again. TransitioningFrom was easy, but TransitioningTo had to be called when the storyboard completed.
Initially, in the constructor of the View Model, it went looking for the Storyboard and hooked into its Completed event, as in the code below:
Storyboard animation = Application.Current.FindResource("SlideAnimation") as Storyboard;
if (animation != null)
{
animation.Completed += new EventHandler(animation_Completed);
}
And then the event handler:
void animation_Completed(object sender, EventArgs e)
{
IRequireTransitionInfo info = currentViewModel as IRequireTransitionInfo;
if (info != null)
info.TransitioningTo(currentView);
}
This seemed to be working pretty well with .net 4. After downgrading to .net 3.5, when the code above to hook up the Completed event ran, I got the following error:
Specified value of type 'System.Windows.Media.Animation.Storyboard' must have IsFrozen set to false to modify.
Despite some of the other answers on SO, you cannot unfreeze a frozen Freezable, and moving the code into the constructor of the MainWindow didn't help.
I went down the path of an attached property on the Storyboard that was bound to a command on the View Model.
<Storyboard x:Key="SlideAnimation" local:EventCommand.StoryboardCompleted="{Binding Source={StaticResource Locator}, Path=Current.MainViewModel.StoryboardCompletedCommand}">
However, this resulted in the following error at runtime:
Cannot convert the value in attribute 'ContentTemplate' to object of type 'System.Windows.DataTemplate'. Cannot freeze this Storyboard timeline tree for use across threads.
It seems you can't do any databinding on a Storyboard (under .net 3.5 at least). Consequently, I solved the problem somewhat inelegantly by having the attached property just define the string name of a resource that was expected to implement an interface supporting notification of storyboard completion.
<Storyboard x:Key="SlideAnimation" local:EventCommand.StoryboardCompletedHandler="Locator">
If anybody knows of a better way to handle this situation under .net 3.5, I would be glad to hear.

Getting wpf gridid

If in my wpf application there are multiple grids and a dragable user control.Can anyone suggest code that could return different grid id every time the control is dragged over different grids.
You can use Mouse.DirectlyOver then go up the visual tree to find the first Grid up the tree.
Mouse.DirectlyOver returns the IInputElement that is under the mouse at the time you check the property.
You can walk up the visual tree using a method described in this SO question
Edit: I found the SO question about visual tree walking i was thinking about. (much better than the first link IMHO).

WPF: Determine if a Panel is visible to the user

I have a WPF usercontrol (myGraphicControl) in a tab (WPF application).
When the form size changes, I redraw the graph in myGraphicControl.
Since the redrawing operation is a I need to do it only the control in in the visible tab.
How the WPF (user)control can detect if it's "visible" actually or not?
PS.
by Visible I mean that user can see it.
say, if a Visible TextBox is located in the currently invisible tab, this textBox is not visible by the user.
I don't believe there is a quick-fix solution here, but you may be able to do something using UIElement.InputHitTest(Point).
You could make a call similar to
//get the coordinates of the top left corner of the child control within
//the parent
var childTopLeft = childControl.TranslatePoint(new Point(), parentControl);
//check whether or not the child control is returned when you request the element
//at that coordinate through hit testing
var isVisible = (parentControl.InputHitTest(childTopLeft) == childControl);
However, I should point out that I haven't tried this myself, and that it probably won't work in the following scenarios:
Transparent items - generally, transparent backgrounds cause hit testing of a control to pass to the parent
Partially occluded items - you can only hit-test one point at a time, so if only part of your child control is visible you will have to check the correct point
I've found that while Steve's method generally works, it works much more reliably if you get a point from somewhere in the middle of the child control. I'm guessing that maybe layout rounding somewhere along the way makes the InputHitTest check somewhat inexact. So, change his first line to the following and you're golden:
var childTopLeft = childControl.TranslatePoint(new Point(childControl.RenderSize.Width/2, childControl.RenderSize.Height/2), parentControl);
Maybe UIElement.IsVisible will be helpful? It works for tab contents well.
Anyway you can use a solution described here.
I have one more solution. The current implementation of TabControl removes inactive tabs from visual tree. So, another way to determine whether your element is visible is to find PresentationSource. It will be null for elements of inactive tabs.

WPF TreeView PreviewMouseDown on TreeViewItem

If I handle the PreviewMouseDown event on TreeViewItem, I get events for everything I click on that TreeViewItem include the little +/- box to the left (Windows XP). How can I distinguish that?
I tried the following:
// We've had a single click on a tree view item
// Unfortunately this is the WHOLE tree item, including the +/-
// symbol to the left. The tree doesn't do a selection, so we
// have to filter this out...
MouseDevice device = e.Device as MouseDevice;
// This is bad. The whole point of WPF is for the code
// not to know what the UI has - yet here we are testing for
// it as a workaround. Sigh...
// This is broken - if you click on the +/- on a item, it shouldn't
// go on to be processed by OnTreeSingleClick... ho hum!
if (device.DirectlyOver.GetType() != typeof(Path))
{
OnTreeSingleClick(sender);
}
What I'm after is just single click on the tree view item excluding the extra bits it seems to include.
If my understanding is correct, e.OriginalSource should contain the control you actually clicked on. Make sure it's a TextBlock (or whatever is actually in your TreeView). You could also use something like this post to determine which TreeViewItem the OriginalSource belongs to, if you're paranoid.
My solution is to use the SelectionItemChanged event, seems to avoid issues with the +/- button.

Resources