【问题标题】:mdi child forms slow to draw when visibility changed当能见度改变时,mdi 子窗体绘制缓慢
【发布时间】:2010-05-31 12:22:22
【问题描述】:

我的应用程序具有以下 UI 配置:

主窗体是一个 MDI 容器。它的子表单附加到TabStrip

每个用户都有自己的一组子表单。根据活动用户,仅显示该用户的子表单以及选项卡。

这是通过查看主窗体的MdiChildren 并根据活动用户将其Visible 属性设置为false/true 来实现的。

        foreach (Form item in MdiChildren)
        {
            if (((OfficeFormEx)item).UserID == (int)e.NewTab.Tag)
            {
                item.Visible = true;
            }
            else
            {
                item.Visible = false;
            }
        }

这有两个不良影响。一是每个子窗体都被连续重绘,丑陋而缓慢。另一个是由于某种原因,表格从最大化变为正常,有效地将它们与主表格分开。

有什么方法可以只显示其中一个子窗体,例如用户之前正在查看的那个子窗体,而让其他窗体留在后台?最大化/正常的东西没什么大不了的,因为我可以手动再次最大化它们。

【问题讨论】:

  • +1 您的问题是为了了解@Hans Passant 已明确解释的内容。谢谢! =)

标签: c# .net winforms


【解决方案1】:

如果没有代码 sn-p,您的问题就不是很清楚。但是,您正在与 Windows MDI 实现作斗争。它不支持的一件事是隐藏子窗口,它最多只能最小化。 Windows 窗体通过销毁 Window 句柄来实现 Visible 属性,并在 Visible 属性再次设置为 True 时重新创建它。该窗口的新实例不会被最大化。

它也不支持在当前窗口最大化时将焦点切换到子窗口。 WF 的解决方法是强制活动子窗口回到正常状态。

MDI 模型根本不适合在最大化状态下显示子窗口。要获得选项卡式界面,请使用 TabControl 并在其选项卡页上显示 UserControls。

【讨论】:

  • 对不起,如果我遗漏了重要细节,但我真的没有看到包含 foreach 循环的任何意义。 :) 您对 MDI 的其余评论几乎总结了我一直在处理的内容。 :( 但是,我可以发誓其中一些在我以前的项目中有效。我得看看。感谢您提供有关幕后发生的事情的信息。
  • +1 这些是处理 MDI 应用程序时要考虑的宝贵信息。
【解决方案2】:

乍一看,我想看看Form.WindowsState 属性,如果你还没有的话。我怀疑如果您碰巧在设计时将此属性设置为FormWindowState.Maximized,那么在设置他们的Visible 属性true/false 时会更改此属性。

对于“[...]每个子表单都被连续重绘[...]”-事情,您是否尝试过在活动用户表单验证开始时使用SuspendLayout()方法,并调用@ 987654323@之后呢?

编辑#1

  • 我建议您只为当前用户加载所需的Form

这样做将减少您的应用程序使用的内存量,此外,它还将大大减少 MdiChildren 集合属性中包含的表单数量。然后,如果仍然需要,遍历集合会更快。

如果这不是您的选择,那么也许使用 Linq 可能会有所帮助:

var visibleForms = from f in MdiChildren
                   where (((OfficeFormEx)f).UserID == (int)e.NewTab.Tag)
                   select f;

var invisibleForms = from f in MdiChildren
                     where (((OfficeFormEx)f).UserID != (int)e.NewTab.Tag)
                     select f

visibleForms.ToList().ForEach(f => f.Visible = true);
invisibleForms.ToList().ForEach(f => f.Visible = false);

如果您使用的是 .NET 4.0,这可能是 PLINQ 的一个不错的候选者

请提供反馈,以便我们找到解决方案。 =)

【讨论】:

  • 不幸的是,两者都没有帮助。
  • 那么,为了帮助我们帮助您,请提供一些代码示例,说明您正在做什么来检查 MdiChild 以使其可见等。考虑到 Mdi 应用程序通常没有所有在应用程序启动时加载并打开可能的 MdiChildren。如果理解正确,也许让用户打开她/他需要的 MdiChild 是可取的。
  • 我刚刚添加了代码。在这种情况下,所有可能的 MDI 子项都会始终加载,因为每个用户都有固定数量的表单用于显示应用程序的各个部分。当活动用户发生变化时,用户只需要查看他的表单子集。
  • 不幸的是,卸载未显示的表单不是一种选择。 LINQ (.NET 3.5) 代码比我的 foreach 版本略快,总而言之,速度接近可接受。看起来 Hans Passant 提到的 MDI 的固有局限性是剩下的障碍。感谢您的努力,谢谢。
  • 感谢您告诉我。这是非常感谢的。 =)
【解决方案3】:

我最终解决了这个问题,所以这是一篇迟来的文章。

Will Marcouiller 建议 SuspendLayout()ResumeLayout(),但没有成功。这使我研究了这两种方法的实际作用,并得出结论,我需要一种方法来阻止主窗体在对 MDI 子项进行操作时重绘。

这反过来导致以下两个静态实用程序方法暂停给定控件的重绘。在我的例子中,暂停重绘主窗体会导致速度大幅提升。

/// <summary>
/// suspends drawing on a control and its children
/// </summary>
/// <param name="parent"></param>
public static void SuspendDrawing(Control control)
{
    SendMessage(control.Handle, WM_SETREDRAW, false, 0);
}

/// <summary>
/// resumes drawing on a control and its children
/// </summary>
/// <param name="parent"></param>
public static void ResumeDrawing(Control control)
{
    SendMessage(control.Handle, WM_SETREDRAW, true, 0);
    control.Refresh();
}

【讨论】:

  • 你如何使用这些?您在何时何地打电话给他们?
猜你喜欢
  • 2020-05-31
  • 1970-01-01
  • 1970-01-01
  • 2020-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-04
相关资源
最近更新 更多