tags:

views:

388

answers:

2

Suppose I wanted to do this, so I can find the current position of the mouse relative to a Visual, without needing access to a specific mouse event:

public static Point GetMousePosition(this Visual relativeTo)
{
    return relativeTo.PointFromScreen(GetMousePositionOnScreen());
}

Sometimes (usually when I've just switched between two tab controls) PointFromScreen throws an InvalidOperationException with the message This Visual is not connected to a PresentationSource.

On looking at the properties available on Visual I can't see any relating to a PresentationSource.

Given a Visual, how can I tell if it is going to throw that exception when I call PointFromScreen on it?

+1  A: 

There's a static method PresentationSource.FromVisual which:

Returns the source in which a provided Visual is presented.

I know this doesn't solve the underlying problem, but you could check that the Visual is connected to a PresentationSource before calling PointFromScreen. It would prevent the exception, but you'd need to do some more investigation as to why it wasn't connected in the first place.

ChrisF
True - I'm using a timer. Occasionally the timer fires when the Visual is not in a good state. I can safely ignore the timer if that occurs. Will give this a try.
Daniel Earwicker
+1  A: 

Hi, I had a similar problem with a custom-made visual.

The solution was to defer the problematic task via Dispatcher (background)...

public void MyProblematicDisplayMethod(Symbol TargetSymbol)
{
    this.HostingScrollViewer.BringIntoView(TargetSymbol.HeadingContentArea);
    ...
    // This post-call is needed due to WPF tricky rendering precedence (or whatever it is!).
    this.HostingScrollViewer.PostCall(
        (scrollviewer) =>
        {
            // in this case the "scrollviewer" lambda parameter is not needed
            var Location = TargetSymbol.Graphic.PointToScreen(new Point(TargetSymbol.HeadingContentArea.Left, TargetSymbol.HeadingContentArea.Top));
            ShowOnTop(this.EditBox, Location);
            this.EditBox.SelectAll();
        });
     ...
}

/// <summary>
/// Calls, for this Source object thread-dispatcher, the supplied operation with background priority (plus passing the source to the operation).
/// </summary>
public static void PostCall<TSource>(this TSource Source, Action<TSource> Operation) where TSource : DispatcherObject
{
    Source.Dispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(delegate(Object state)
                                        { Operation(Source); return null; }),
        null);
}

I've used that PostCall in other ScrollViewer related rendering situations.

Néstor Sánchez A.
Oh yeah, I have to do that so much in WPF that I have a similar helper method - perhaps everyone does...
Daniel Earwicker