在没有子容器的情况下检测双击mdiContainer(c#winforms)

在没有子容器的情况下检测双击mdiContainer(c#winforms),c#,winforms,events,mdi,C#,Winforms,Events,Mdi,我有一个表单,它是一个MDI容器。当表单没有子对象时,我想捕获用户双击客户端区域时的双击事件(与Photoshop的行为相同) 但是当我设置双击事件时,什么也没有发生。 如何实现这一点?不是理想的解决方案,但您可以将IsMDIContainer设置为false,直到创建子级。(如果为false,则事件触发ok)下面是一个答案,它涉及到使用面板和策略来隐藏和显示MDI应用程序的主窗体的MdiClient对象的一些“胡乱处理” private MdiClient theMdiClient; 关闭所

我有一个
表单
,它是一个
MDI容器
。当表单没有子对象时,我想捕获用户双击客户端区域时的双击事件(与Photoshop的行为相同)

但是当我设置
双击事件时,什么也没有发生。

如何实现这一点?

不是理想的解决方案,但您可以将IsMDIContainer设置为false,直到创建子级。(如果为false,则事件触发ok)

下面是一个答案,它涉及到使用面板和策略来隐藏和显示MDI应用程序的主窗体的MdiClient对象的一些“胡乱处理”

private MdiClient theMdiClient;
关闭所有MDI子窗口后,面板将填充MDI客户端区域并处理鼠标事件。如果将面板的BackColor属性设置为与默认MDI客户端区域背景颜色匹配(如何执行此操作将在下面解释),则可以实现“无缝”的视觉过渡。。。尽管:考虑到您可能希望使颜色变化为有意的策略,以让用户知道双击,或者您想处理的任何其他鼠标事件,都是可用的。 正如本例所示,虽然您可以实现这一点,但我鼓励您考虑这一功能是否“真的值得”

我没有评估Gareth S.在上面提出的合理建议,即通过在主MDI窗体上切换“IsMdiContainer”属性来解决此问题,原因很简单,我担心可能需要重新创建复杂的窗口配置,在“IsMdiContainer属性”为true的窗体上,将“IsMdiContainer”设置为false的副作用是从窗体的控件集合中删除MdiClient控件:我怀疑这会有一些棘手的副作用,但我可能完全错了

我更喜欢隐藏和显示MdiClient对象的策略,而不是将其作为设置属性的副作用删除,然后需要将其重新添加到主MDI窗体的控件集合中才能再次使用。你可以做另一个选择

我将不讨论如何在内部处理MdiClient对象,而是“切中要害”,并向您展示如何解决它在处理某些鼠标事件方面的不足

假设:

  • 您已经定义了一个名为“Form2”的“辅助”窗体,该窗体将用于主MDI窗体的所有子窗口
  • 代码:

  • 将面板添加到主MDI表单:将其DockStyle设置为“Fill”;将其背景色设置为使用系统的AppWorkspace颜色作为背景,以匹配用户的机器颜色首选项设置(MdiClient默认背景色就是使用这种颜色)。在此面板中为“双击”或“鼠标双击”编写事件处理程序,具体取决于您的首选项

  • 在主MDI窗体中:声明一个窗体范围的变量以保存对MdiClient对象的引用

    private MdiClient theMdiClient;
    
    假设您也在表单范围级别声明了一组子窗口:

    Form2 f2 = new Form2();
    Form2 f3 = new Form2();
    Form2 f4 = new Form2();
    Form2 f5 = new Form2();
    
  • 主MDI表单加载事件:我们需要获取对MdiClient对象的引用:如果您知道表单(面板)上只有一个控件,则可以非常确定MainMdiForm.Controls[1]在表单加载事件期间将包含MdiClient对象,但最好迭代以确保:

    注意:我们解释了在每个子级中设置对MDI主窗体的引用的调用 下面第5节中的窗口(幻灯片)

  • 现在,这个问题更有趣的方面是:当主MDI表单中没有可见的子窗口时,如何重新显示面板。首先,我们将在MDI主窗体中定义一个公共方法:然后我们可以假设,如果此方法由每个子窗口中的FormClosed事件处理程序调用,那么:如果主MDI窗体的MDIChilds.Length属性正好是#1:最后一个剩余可见子窗口刚刚关闭:请注意,然后我们将删除MdiClient对象从主MDI窗体的控件集合:

    public void MDIChildClosed()
    {
        if (this.MdiChildren.Length == 1)
        {
            this.Controls.Remove(theMdiClient);
            panel1.Visible = true; 
        }
    }
    
  • 我们在Form2中创建一个公共属性(所有子窗口都是Form2的实例),以保存对主MDI窗体的引用,主MDI窗体是其MDI父窗体:

    public Form1 theMDIParent { get; set; }
    
    然后,我们使用对Mdi父窗口(Form 1)的引用来处理子窗口(Form2)的FormClosed事件,以调用其公共“MdiChildClosed()方法:

  • 在主MDI窗体(Form1)的MdiChildClosed()方法中:如前所述,我们从主MDI窗体的控件集合中删除MdiClient对象,并使处理鼠标事件的面板再次可见:

    public void MDIChildClosed()
    {
        if (this.MdiChildren.Length == 1)
        {
            this.Controls.Remove(theMdiClient);
            panel1.Visible = true; 
        }
    }
    
  • 现在,您可能想知道:当最终用户(通过按钮、菜单等)在所有Mdi子窗口关闭后创建新的Mdi子窗口,并且面板再次显示时,如何恢复MdiClient对象。无论您如何触发新mdi子窗口的创建,您都将在主mdi窗体(Form1)中执行以下操作:

  • 总结:大量的工作,imho,一个相对较小的回报。显然,您可以通过多种方式重构此代码,并以更优雅的方式进行操作。我个人认为MDI类型的应用程序早已“过时”


  • 对我来说,这次探索中唯一可能出现的“火花”是,你可以在你的面板上放置各种配置控件(如这里所用),也许可以选择隐藏所有MDI子窗口并显示面板,而不是等待所有MDI子窗口关闭。

    客户端区域中的“MouseDown”可以在代码中处理,无子类、无保护覆盖、无WndProc,并且没有将IsMDIContainer设置为“false”:当未显示子窗口时;或者,当显示子窗口时;或者,在所有情况下。需要代码:询问。一旦你按下鼠标,我想你可以用定时器“假装”检测到双击,尽管这可能不优雅。不理想,但实际上也不太可怕。
    public void MDIChildClosed()
    {
        if (this.MdiChildren.Length == 1)
        {
            this.Controls.Remove(theMdiClient);
            panel1.Visible = true; 
        }
    }
    
      // hide the panel
      panel1.Hide();
      // add the MdiClient back
      this.Controls.Add(theMdiClient);
    
      // example of creating a new child Form
      Form2 f6 = new Form2();
      f6.MdiParent = this;
      f6.theMDIParent = this;
      f6.Show();