【问题标题】:How to avoid visual artifacts when hosting WPF user controls within a WinForms MDI app?在 WinForms MDI 应用程序中托管 WPF 用户控件时如何避免视觉伪影?
【发布时间】:2010-04-07 04:10:42
【问题描述】:

在 WinForms MDI 应用程序中托管 WPF 用户控件时,当您有多个相互重叠的表单会导致非常明显的视觉伪影时,会出现绘图问题。在将一个子窗体拖到另一个也承载 WPF 内容的子窗体上之后,或者通过在拖动子窗体时允许主 MDI 父窗体剪裁子窗体的边缘后,这些工件大部分可见。子窗体的拖放完成后,工件通常会保留下来,但我发现将焦点设置到不同的应用程序窗口,然后重新聚焦到我的应用程序窗口,它被重绘并且一切都很好,直到孩子表格再次移动。请看下面的图片来说明问题。

Microsoft 的那些人坚持认为 WinForms MDI 已经是 MDI 的充分解决方案,不需要在 WPF 中重新发明,尽管由于明显的缺点,我很难相信他们尝试以这种方式创建 WPF 应用程序。

更新:我遗漏的一些额外注意事项是,如果我在不设置 MdiParent 的情况下创建这些表单,它们将被创建为常规表单,并且不会发生此问题。这个问题似乎是 WinForms MDI 方案所独有的。此外,我目前在 Windows 7 Enterprise 上运行,我知道 Windows XP 上的结果可能完全不同,但我无法对此进行测试。

更新:我发现了一些我认为应该分享的关于这个问题的其他相关资源。

【问题讨论】:

    标签: wpf mdi winforms-interop visual-artifacts


    【解决方案1】:

    似乎另一种解决方法是恢复为软件渲染,而不是利用硬件加速。这是 MSDN 论坛上的 suggestion by Marco Zhou

    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
            this.Loaded += delegate
            {
                var source = PresentationSource.FromVisual(this);
                var hwndTarget = source.CompositionTarget as HwndTarget;
                if (hwndTarget != null)
                {
                    hwndTarget.RenderMode = RenderMode.SoftwareOnly;
                }
            };
        }
    }
    

    我已经对此进行了测试,该解决方案似乎运行良好,到目前为止,这是我在 FoxPro 互操作方案中找到的唯一解决方案,该方案与我最初发布的 WinForms 非常相似。现在,我计划在我的 WinForms 项目的 MDI 父解决方案上使用我原来的 Refresh,但是对于我的其他本机互操作应用程序,例如当我的 WPF 控件托管在 Visual FoxPro 中时,我将使用这个解决方案。当然,除非为任何一种情况找到更优雅的解决方案。

    【讨论】:

      【解决方案2】:

      好吧,我可能已经找到了一个解决方案,尽管这感觉有点像 hack。看来,如果您在移动子 MDI 窗体时调用 MDI 父级上的 Refresh 方法,那么所提到的工件就会消失。拖动窗口时,视觉上看起来有点紧张,但它似乎比我在原始帖子中展示的示例更容易接受。

      private void Form1_Move(object sender, EventArgs e)
      {
          this.ParentForm.Refresh();
      
          System.Diagnostics.Debug.WriteLine(string.Format("Form Moved to: ({0},{1})", this.Left, this.Top));
      }
      

      我已经尝试了许多相同的组合,例如仅刷新通过调​​用 Update()Invalidate() 等方法移动的子窗口, Refresh() 并且我已经在 MDI 父级以及 Dispatcher.Invoke(DispatcherPriority.Render, ...) 上尝试了这些相同的方法InvalidateVisual() 在我托管的 WPF 控件上,但没有其他方法可以接受专门在 MDI 父级上调用 Refresh()

      我意识到这可能不是最佳解决方案,因为每次子窗口移动几个像素时,我都会强制整个主应用程序窗口刷新,但就目前而言,这是我发现唯一可行的合理解决方案.如果其他人对此有任何替代解决方案或任何改进,我将很乐意接受您的回答。

      【讨论】:

        【解决方案3】:

        检查视频驱动程序并尝试禁用硬件加速。大多数伪影是由于驱动程序错误、显卡故障或完成刷新的时间不足造成的。

        故障排除第一步:更新视频驱动程序。很明显,我知道。

        我遇到了类似的问题,检查我的视频卡设置(NVidia 控制面板)显示全局设置设置得非常高,导致刷新间隔过长,如果时间过长可能会中止。将我的设置恢复为默认值可以解决大部分问题。但我也运行大量使用 GPU 的哈希程序,所以这可能是我的剩余工件问题的原因,现在很少见,而且大多在 Visual Studio 中显示其丑陋的面貌。

        我遇到的另一个故障排除步骤是禁用 WPF 的硬件加速,这可以在“HKEY_CURRENT_USER/SOFTWARE/Microsoft/Avalon.Graphics”中完成,或者应用程序可以这样做,但这仅用于故障排除;永远不要在应用程序中设置这些,因为它将禁用所有 WPF 应用程序。我没有这个注册表设置,也没有添加它,所以我不确定它是否成功,但很多人说这解决了他们的问题。另请注意,某些应用程序提供此选项,如果可用,请尝试禁用它。

        另一个故障排除步骤是确保视频卡是适合渲染的层级。任何支持 DX9 或更高版本的卡都应该足够了,但涉及到其他因素(就像我的情况一样),所以仅仅因为它在列表中并不意味着它足以满足您的目的。

        最后,您可以使用 Visual Profiler(Windows SDK 的一部分)和其他工具来帮助更准确地确定 WPF 在图形能力方面缺乏性能的情况。

        渲染层级注释和 WPF 性能信息 --> http://msdn.microsoft.com/en-us/library/vstudio/ms742196(v=vs.90).aspx

        希望这对某人有所帮助。

        --瑞安·斯特拉斯堡

        【讨论】:

          【解决方案4】:

          您的用户控件或窗口加载事件;

          this.WindowState = System.Windows.WindowState.Minimized;
          
          this.WindowState = System.Windows.WindowState.Normal;
          

          这似乎是不好的解决方案。无需用头撞墙。

          土耳其谚语说:最好的代码是代码在运行:)

          【讨论】:

            猜你喜欢
            • 2012-03-27
            • 1970-01-01
            • 2013-05-18
            • 1970-01-01
            • 2016-10-29
            • 1970-01-01
            • 1970-01-01
            • 2013-05-29
            • 1970-01-01
            相关资源
            最近更新 更多