当窗体有背景图像时,如何解决不需要的控件闪烁?

How to resolve unwanted flickering of Controls when the Form has a background Image?

问题概述

当我试图隐藏一些标签、文本框和一个按钮时,我遇到了这个问题:所有这些控件在隐藏时都会闪烁,但其他不涉及的控件都很好。我不要这个效果。
此外,这不会发生在 Show() 上,只会发生在 Hide().

我试过的:

删除背景图片解决了这个问题,但我确实希望我的背景图片可见。
当我在网上搜索时,我学会了如何有意地创建这种效果,但是我找不到消除它的解决方案。
其他人说这只是硬件限制,但我确实有能力 CPU.

VB.NET代码:

只是一些<element>.Hide()

Private Sub GetTaskNameButton_Click(sender As Object, e As EventArgs) Handles GetTaskNameButton.Click
    GetTaskNameButton.Hide()
    TaskInputTextBox.Hide()
    TaskNameInputLabel.Hide()
    TaskTimeLabel.Hide()
    TaskHourLabel.Hide()
    TaskHoursBox.Hide()
    TaskMinutesLabel.Hide()
    TaskMinsBox.Hide()
    TasksCheckedListBox.Items.Add(TaskInputTextBox.Text + " >Time: " + TaskHoursBox.Text + "Hr " + TaskMinsBox.Text + "Min")
    TaskInputTextBox.Text = ""
    TaskHoursBox.Text = ""
    TaskMinsBox.Text = ""
End Sub

CPU 限制 无关。这与窗体的背景及其子控件的内容的呈现有关。
在此处阅读说明:How to fix the flickering in User Controls.
(但是 WS_EX_COMPOSITED 在这里帮不了你)。

由于您有一些组合功能的控件,您可以构建一个 UserControl 以将单个控件分组到一个专门的 entity 中,该实体提供功能并包含执行此任务并在做出选择时通知(所有输入值均已验证并单击 提交 按钮)。

要处理此 UserControl 的透明度,您必须稍微调整其标准行为,因为当您拥有带背景图像的表单时仅设置 BackColor = Color.Transparent 是不够的:将模拟透明度考虑表单的 Background 颜色,而不是背景图像的内容(图像不是颜色)。

您可以使您的 UserControl 实际上透明,以防止其背景被 绘制

  • 使用SetStyle() to set ControlStyles.Opaque,所以不画背景。
  • 设置ControlStyles.OptimizedDoubleBufferFalse:这个UserControl当然不能使用DoubleBuffering,否则我们又回到了第一步( Parent Control的背景用于构建BufferedGraphcs对象)
  • 覆盖 CreateParams to add WS_EX_TRANSPARENT 到 UserControl Window 的扩展样式,因此系统不会 干扰 ,让其他 windows 落后UserControl 首先绘制它们的内容(但之后我们不会绘制我们的内容)。
  • 需要删除 WS_CLIPCHILDREN 样式(因为基础 class、Control 添加了它)否则 UserControls 的子控件可能 消失 调整表单大小时。

单击 Add Task 按钮时,UserControl 可以引发 public 事件,传入自定义 EventArgs 对象 - 验证后 - 输入的值。
表单可以订阅此事件并在引发事件时读取自定义 EventArgs 属性。

  • 由于您的表单有背景图像,因此设置 DoubleBuffered = True

这是它的样子:

此处显示的图片大小为 3840x2560(可从网上免费下载)。
尝试调整 Form 的大小而不对其进行双缓冲 :)

完整 UserControl 的 PasteBin,以备不时之需: Transparent UserControl


假设添加到窗体的 UserControl (AddNewTask) 名为 AddNewTask1,您可以使用设计器或在代码中将事件处理程序添加到其 AddTaskClicked 事件,在表单构造函数:

Public Class SomeForm
    Public Sub New()
        InitializeComponent()
        AddHandler AddNewTask1.AddTaskClicked, AddressOf OnTaskAdded
    End Sub

    Private Sub OnTaskAdded(sender As Object, e As AddNewTask.AddTaskEventArgs)
        Dim values As String = $"{e.TaskName}: Hours: {e.TaskHours}, Minutes: {e.TaskMinutes}"
    End Sub
End Sub

AddNewTask用户控件:

Public Class AddNewTask

    Private Const WS_EX_TRANSPARENT As Integer = &H20
    Private Const WS_CLIPCHILDREN As Integer = &H2000000

    Public Event AddTaskClicked As EventHandler(Of AddTaskEventArgs)

    Public Sub New()
        SetStyle(ControlStyles.Opaque Or ControlStyles.ResizeRedraw, True)
        SetStyle(ControlStyles.OptimizedDoubleBuffer, False)
        InitializeComponent()
    End Sub

    Protected Overrides ReadOnly Property CreateParams As CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.Style = cp.Style And Not WS_CLIPCHILDREN
            cp.ExStyle = cp.ExStyle Or WS_EX_TRANSPARENT
            Return cp
        End Get
    End Property

    Private Sub btnAddTask_Click(sender As Object, e As EventArgs) Handles btnAddTask.Click
        Dim hours As UInteger
        Dim sHours = If(String.IsNullOrEmpty(txtHours.Text.Trim()), "0", txtHours.Text)

        If (Not UInteger.TryParse(sHours, hours)) Then
            ShowInputErrorMessage("Invalid Hours", txtHours)
            Return
        End If

        Dim minutes As UInteger
        Dim sMinutes = If(String.IsNullOrEmpty(txtMinutes.Text.Trim()), "0", txtMinutes.Text)

        If (Not UInteger.TryParse(sMinutes, minutes)) Then
            ShowInputErrorMessage("Invalid Minutes", txtMinutes)
            Return
        End If

        Hide()
        Dim args = New AddTaskEventArgs(txtTaskName.Text, hours, minutes)
        RaiseEvent AddTaskClicked(Me, args)

        txtHours.Clear()
        txtMinutes.Clear()
        txtTaskName.Clear()
        ActiveControl = txtTaskName
    End Sub

    Private Sub ShowInputErrorMessage(msg As String, ctrl As TextBox)
        MessageBox.Show(msg)
        ctrl.Select()
        ctrl.SelectAll()
    End Sub

    Public Class AddTaskEventArgs
        Inherits EventArgs
        Public Sub New(sTaskName As String, hours As UInteger, minutes As UInteger)
            TaskName = sTaskName
            TaskHours = hours
            TaskMinutes = minutes
        End Sub
        Public ReadOnly Property TaskName As String
        Public ReadOnly Property TaskHours As UInteger
        Public ReadOnly Property TaskMinutes As UInteger
    End Class
End Class