【问题标题】:In WPF, under what circumstances does Visual.PointFromScreen throw InvalidOperationException?在WPF中,Visual.PointFromScreen在什么情况下会抛出InvalidOperationException?
【发布时间】:2011-01-10 09:14:44
【问题描述】:

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

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

有时(通常当我刚刚在两个选项卡控件之间切换时)PointFromScreen 会抛出一个带有消息的InvalidOperationException 此视觉对象未连接到 PresentationSource。

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

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

【问题讨论】:

    标签: wpf .net-3.5


    【解决方案1】:

    有一个静态方法PresentationSource.FromVisual

    返回显示提供的视觉对象的源。

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

    【讨论】:

    • 是的 - 我正在使用计时器。有时,当 Visual 状态不佳时,计时器会触发。如果发生这种情况,我可以放心地忽略计时器。会试一试。
    【解决方案2】:

    我在定制视觉效果方面遇到了类似的问题。

    解决方案是通过 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);
    }
    

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

    【讨论】:

    • 哦,是的,我必须在 WPF 中做很多事情,以至于我有一个类似的辅助方法 - 也许每个人都这样做......
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      比赛迟到了,但这些回应对我有帮助。我只是想指出 PresentaionSources 在完全加载之前不会连接到视觉元素。因此,如果在您的构造函数中设置的事件可能会在您尝试调用 PointFromScreen 的可视元素准备好显示在屏幕上之前被触发,那么您将收到该错误。虽然如前所述,您可以将您的方法包装在以下内容中:

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

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

      【讨论】:

      • 您不能返回 null,因为 Point 是不可为 null 的值类型。您可能想要返回类似“return new Point()”
      【解决方案5】:

      由于内存泄漏,视觉对象被丢弃但仍在内存中,可能会发生异常。

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

      【讨论】:

      • 你是如何修复内存泄漏的?
      • 我使用了一个工具来查找和修复漏洞。您可以使用许多工具中的任何一种,例如dotMemory,memprofiler。
      • 内存泄漏的原因是什么?
      • @xr280xr ,现在不记得了,该工具给出了持有对象的原因(事件处理程序,被另一个对象引用等)
      猜你喜欢
      • 2012-04-23
      • 2016-07-16
      • 2010-12-29
      • 2011-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多