当窗体有背景图像时,如何解决不需要的控件闪烁?
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.OptimizedDoubleBuffer
为False
:这个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
问题概述
当我试图隐藏一些标签、文本框和一个按钮时,我遇到了这个问题:所有这些控件在隐藏时都会闪烁,但其他不涉及的控件都很好。我不要这个效果。
此外,这不会发生在 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.OptimizedDoubleBuffer
为False
:这个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