在WPF中,Visual.PointFromScreen在什么情况下抛出InvalidOperationException?

时间:2021-02-24 22:12:31

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:

假设我想这样做,所以我可以找到鼠标相对于Visual的当前位置,而无需访问特定的鼠标事件:

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.

有时(通常当我刚刚在两个选项卡控件之间切换时)PointFromScreen会抛出InvalidOperationException,并显示消息This Visual未连接到PresentationSource。

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

在查看Visual上可用的属性时,我看不到与PresentationSource有关的任何内容。

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

给定一个Visual,当我在其上调用PointFromScreen时,如何判断它是否会抛出该异常?

5 个解决方案

#1


20  

There's a static method PresentationSource.FromVisual which:

有一个静态方法PresentationSource.FromVisual:

Returns the source in which a provided Visual is presented.

返回提供Visual的源。

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.

我知道这并不能解决根本问题,但您可以在调用PointFromScreen之前检查Visual是否连接到PresentationSource。它可以防止异常,但你需要做一些更多的调查,看看为什么它首先没有连接。

#2


3  

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)...

解决方案是通过Dispatcher延迟有问题的任务(在这种情况下延迟执行具有后台优先级)...

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.

我在其他ScrollViewer相关的渲染情况中使用过PostCall。

#3


2  

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

我发现你可以在调用PointFromScreen之前测试IsVisible以防止InvalidOperationException。

#4


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.

我遇到了类似的问题。我发现异常发生在视觉上,应该是垃圾收集。修复视觉中的内存泄漏解决了问题。

#5


1  

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:

球赛后期,但这些反应对我有所帮助。我只是想指出,在完全加载之前,PresentaionSources没有连接到可视元素。因此,如果在构造函数中设置可能在您尝试调用PointFromScreen的可视元素之前触发的事件已准备好显示在屏幕上,那么您将收到该错误。如前所述,您可以将方法包装为:

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.

在确定视觉已在屏幕上呈现至少一次之前,您还可以考虑不调用您的方法。

#1


20  

There's a static method PresentationSource.FromVisual which:

有一个静态方法PresentationSource.FromVisual:

Returns the source in which a provided Visual is presented.

返回提供Visual的源。

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.

我知道这并不能解决根本问题,但您可以在调用PointFromScreen之前检查Visual是否连接到PresentationSource。它可以防止异常,但你需要做一些更多的调查,看看为什么它首先没有连接。

#2


3  

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)...

解决方案是通过Dispatcher延迟有问题的任务(在这种情况下延迟执行具有后台优先级)...

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.

我在其他ScrollViewer相关的渲染情况中使用过PostCall。

#3


2  

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

我发现你可以在调用PointFromScreen之前测试IsVisible以防止InvalidOperationException。

#4


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.

我遇到了类似的问题。我发现异常发生在视觉上,应该是垃圾收集。修复视觉中的内存泄漏解决了问题。

#5


1  

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:

球赛后期,但这些反应对我有所帮助。我只是想指出,在完全加载之前,PresentaionSources没有连接到可视元素。因此,如果在构造函数中设置可能在您尝试调用PointFromScreen的可视元素之前触发的事件已准备好显示在屏幕上,那么您将收到该错误。如前所述,您可以将方法包装为:

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.

在确定视觉已在屏幕上呈现至少一次之前,您还可以考虑不调用您的方法。