【问题标题】:Make a windows forms control unresponsive使 Windows 窗体控件无响应
【发布时间】:2018-02-28 12:56:28
【问题描述】:

我想以某种方式使 Windows 窗体控件变得不负责任——比如将 Control.Enabled 设置为 False,但没有它的视觉效果(我将有一些自定义的“控件正忙”指示器,所以它不会不清楚最终用户为什么控件没有响应)。

原因是:我正在为 windows 窗体编写一个繁忙的指示器控件,并希望它尽可能通用。

目的是能够像下面这样使用它。

Dim busy_control As Control = ...
ShowBusyIndicator(busy_control)
BeginDoWork() 'starts a worker thread

'in some OnCompleted-Event:
HideBusyIndicator(busy_control)

我当前的问题是,我想确保busy_control 根本不会对任何用户输入做出反应。

在当前版本中,我通过处理 GotFocus 事件确保控件失去焦点并且永远无法再次获得焦点。由于覆盖控件的Parent是busy_control,所以我还将OnMouseWheel事件设置为handled(否则busy_control可以滚动)。

我想还有更多这样的事件。这就是为什么我想“禁用”控件而不实际将启用设置为 false。

有什么办法吗?

【问题讨论】:

  • 如果你想保证在你的运行任务完成之前它不会接受输入,不要多线程它。只需取消注册您的 OnClick 事件以确保没有任何事情发生,然后在 UI 线程上运行您的方法,然后重新注册事件。但是,这很可能会让您的用户感到困惑。用户期望“禁用”行为。
  • 如果我想显示动画 gif(或让应用程序负责),我无法在 UI 线程上完成工作。如果没有一些反射黑客,我也无法禁用所有事件 - 这感觉很脏。
  • 正确。如果你想保持应用程序响应,你不能在 UI 线程上工作。如果您只是希望这个表单对象本身表现得好像它被禁用了,您不需要反射来删除它的处理程序,但是有一个更好的方法。等一下……
  • 在禁用它之前使用 Graphics.DrawToBitmap() 对其进行截图。在您放在控件前面的 PictureBox 中显示它。假装控件仍然可用是非常糟糕的 UI。
  • @HansPassant 的“忙碌指示器”通过绘制一个带有旋转圆圈的半透明覆盖层,很明显控件不可用

标签: vb.net winforms


【解决方案1】:

我也建议您不要这样做...但是,如果您必须...

只需将您想要抑制/禁用的控件传递到此子项即可。显然,当您重新启用控件时,您需要删除创建的图片框...但我会让您自己弄清楚。

Private Sub SuppressControl(ByRef CtrlToSuppress As Control)
    ''create an image of the control
    Dim ctrlImage As New Bitmap(CtrlToSuppress.Width, CtrlToSuppress.Height)
    CtrlToSuppress.DrawToBitmap(ctrlImage, CtrlToSuppress.ClientRectangle)
    ''create a picturebox in the same location/size as the control
    Dim PictureBox1 As New PictureBox
    PictureBox1.Left = CtrlToSuppress.Left
    PictureBox1.Top = CtrlToSuppress.Top
    PictureBox1.Width = CtrlToSuppress.Width
    PictureBox1.Height = CtrlToSuppress.Height
    ''Set the anchors to maintain alignment on form resize
    PictureBox1.Anchor = CtrlToSuppress.Anchor
    ''place the image in picturebox
    PictureBox1.Image = ctrlImage
    ''add the picture box to the form
    CtrlToSuppress.Parent.Controls.Add(PictureBox1)
    ''and bring it to the front
    PictureBox1.BringToFront()
    ''Suppress the control
    CtrlToSuppress.Enabled = False
End Sub

【讨论】:

  • 在此版本中,如果调整窗体大小以移动控件,则覆盖层很容易与繁忙的控件不同步。就像我说的那样,这将是一个关于控件正忙的视觉指示器(在这种情况下,我会用 ctrlImage 做一些事情),所以我看不出有任何理由说明这会是一个如此糟糕的想法。
  • 如果您设置 set PictureBox1.Anchor = CtrlToSuppress.Anchor,即使表单移动或调整大小...它们应该保持对齐,除非您在代码中的其他位置手动移动表单上的控件禁用后?我从来没有说过这是“一个糟糕的主意”,只是我不会推荐它。如果你有理由这样做(或者即使你只是想这样做没有特别的原因),那取决于你。只是从外部看来,对于习惯于 Winforms“常规”行为的用户来说,这似乎需要做很多额外的工作。
  • Anchor 使它更好一点,但不适用于停靠控件。好吧,我正在尝试提供一个易于使用的“忙碌指示器”供我的同事使用。至于用户,他们会看到一个旋转的圆圈改变了忙碌控制的颜色——我怀疑会有任何混乱。
  • 说实话,我没有考虑过停靠控件。抱歉,这是我能找到的最接近你要找的东西了。
  • 实际上,如果您添加“PictureBox1.Dock= CtrlToSuppress.Dock”,它似乎仍然可以与停靠控件一起使用。
【解决方案2】:

您最好的选择可能是透明的Panel。通过将它放在您想要“禁用”的控件上,您可以拦截任何点击它的尝试,因为Panel 将注册点击。您也可以将OnClick 句柄添加到Panel

由于您已经在使用“微调器”之类的东西,您可能希望将加载指示器放在 Panel 上。这样一来,用户就不会对为什么单击没有执行任何操作或控件没有看起来 禁用感到困惑。如果没有,只需使整个Panel 透明即可。有关如何控制 Panel 透明度的信息,请参见 here

请注意,在链接的示例中,由于操作系统的限制,您需要在Form 级别进行透明性的技术说明。您将在该链接中找到帮助您构建所需内容的参考资料,具体取决于您所针对的 Windows 版本。

我怎么强调都不过分,最好使用微调器来覆盖您禁用的内容。这比让控件假装它没有被禁用要清晰得多(老实说,也更容易编码)。

【讨论】:

  • 微调器就是我所说的“忙碌指示器”。我有这个。它旋转。现在我想确保用户不能对繁忙的控件做任何事情,不能进入它,不能单击或右键单击它,什么都没有。
  • 您还必须处理键盘输入。用户可以使用 Tab 和 Alt-Tab 进行导航,然后使用空格“单击”按钮。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-20
相关资源
最近更新 更多