【问题标题】:How to resolve unwanted flickering of Controls when the Form has a background Image?当表单有背景图像时,如何解决控件不必要的闪烁?
【发布时间】:2021-07-02 21:58:11
【问题描述】:

问题概述

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

我的尝试

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

VB.NET 代码

只是一些<element>.Hide()s

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

【问题讨论】:

标签: vb.net winforms custom-controls double-buffering


【解决方案1】:

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

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

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

您可以使您的 UserControl 真正透明,防止其背景被绘制

  • 使用SetStyle()设置ControlStyles.Opaque,所以背景不画。
  • ControlStyles.OptimizedDoubleBuffer设置为False:这个UserControl当然不能使用DoubleBuffering,否则我们又回到了第一方(父控件的背景用于构建BufferedGraphcs对象)
  • 覆盖 CreateParams 以将 WS_EX_TRANSPARENT 添加到 UserControl 窗口的扩展样式中,因此系统不会干扰,让 UserControl 后面的其他窗口首先绘制它们的内容(但我们赢了以后不要画我们的)。
  • WS_CLIPCHILDREN 样式需要被移除(因为基类 Control 添加了它)否则当窗体调整大小时用户控件的子控件可能消失

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

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

看起来是这样的:

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

完整的 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 UserControl

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

【讨论】:

  • 是的,它成功了!将 DoubleBuffered 设置为 True 修复了它,但感谢您提供深入的解释!
猜你喜欢
  • 2012-12-03
  • 2021-05-19
  • 1970-01-01
  • 1970-01-01
  • 2014-03-19
  • 1970-01-01
  • 2013-10-20
  • 2020-08-17
  • 2017-08-24
相关资源
最近更新 更多