In WPF, under what circumstances does Visual.PointFromScreen throw InvalidOperationException?
Asked Answered
S

5

20

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?

Selfmortification answered 28/1, 2010 at 11:50 Comment(0)
D
27

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.

Doherty answered 28/1, 2010 at 12:43 Comment(1)
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.Selfmortification
R
5

I've found you can test IsVisible before calling PointFromScreen to protect against the InvalidOperationException.

Rewire answered 22/7, 2016 at 19:16 Comment(0)
P
4

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

The solution was to defer the problematic task via Dispatcher (deferred execution with background priority in this case)...

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.

Prolegomenon answered 24/2, 2010 at 21:2 Comment(1)
Oh yeah, I have to do that so much in WPF that I have a similar helper method - perhaps everyone does...Selfmortification
A
3

Late to the ballgame, but these responses helped me. I just wanted to point out That PresentaionSources are not connected to visual elements until they are completely loaded. So if in your constructor you are setting up events that may get fired prior to the visual element you are attempting to call PointFromScreen on is ready to be displayed on screen, then you will get that error. While as mentioned before you can wrap your method in something like:

public static Point GetMousePosition(this Visual relativeTo)
{
    if(PresentationSource.FromVisual(relativeTo) != null)
       return relativeTo.PointFromScreen(GetMousePositionOnScreen());
    else
       return new Point();
}

you could also consider not calling your method until you are sure that the visual has been render on screen at least once.

Admeasurement answered 23/6, 2014 at 18:41 Comment(1)
You cannot return null because Point is a non-nullable value type. You maybe want to return something like "return new Point()"Kingdon
T
1

The exception may occur because a visual is discarded but still in memory due to a memory leak.

I was having a similar problem. I found the exception occurring in a visual which was supposed to be garbage collected. Fixing the memory leaks in the visual solved the problem.

Trap answered 25/6, 2014 at 11:8 Comment(4)
how you fixed the memory leak?Bit
I used a tool to find and fix the leaks. You can use any of the many tools e.g. dotMemory, memprofiler.Trap
What was the cause of the memory leak?Conduction
@Conduction , don't remeber now, the tool gives the reason why the object is held (Event handler, referenced by another object, etc.)Trap

© 2022 - 2024 — McMap. All rights reserved.