c# - 更改选项卡图标时 TabControl 闪烁

标签 c# .net vb.net winforms

我快要在这里失去理智了。我在谷歌上搜索了一个小时,试图解决这个小问题,但问题却非常严重。

我的表单上有一个带有两个选项卡的 TabControl。这些选项卡中的每一个都有一个 16x16 图标和一些文本。这里没有发生任何疯狂的事情。

我需要让其中一个选项卡图标在某些情况下闪烁。所以我创建了两个图像,light_on.pnglight_off.png并将它们添加到 TabControl 使用的 ImageList 中。我设置了一个背景计时器,在两个图像之间切换以模拟闪烁的图标。工作良好。

但是,它会导致所有选项卡标题重绘,从而使它们闪烁。

无论您尝试做什么,TabControl 都不支持双缓冲。

我发现人们使用这段代码成功地控制了闪烁:

    Protected Overrides ReadOnly Property CreateParams() As CreateParams 
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or &H2000000
            Return cp
        End Get
    End Property

这是有效的,因为它不会闪烁...但图标也不再在视觉上发生变化,除非鼠标光标悬停在导致重绘的对象上。

有没有人有任何可能有效的替代解决方案或技巧?这实际上是该软件的一项非常重要的功能。

骨架代码:

Public Class Form1
    Dim BlinkTimer As Windows.Forms.Timer
    Dim BlinkToggler As Boolean = False

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        InitBlinker()
    End Sub

    Private Sub InitBlinker()
        BlinkTimer = New Windows.Forms.Timer
        AddHandler BlinkTimer.Tick, AddressOf Blinker_Tick
        With BlinkTimer
            .Enabled = True
            .Interval = 250
        End With
        StartBlinker()
    End Sub
    Public Sub StartBlinker()
        SomeTabPage.ImageKey = "light_off.png"
        BlinkToggler = False
        BlinkTimer.Start()
    End Sub
    Public Sub StopBlinker()
        SomeTabPage.ImageKey = "light_off.png"
        BlinkToggler = False
        BlinkTimer.Stop()
    End Sub
    Private Sub Blinker_Tick()
        If BlinkToggler Then
            SomeTabPage.ImageKey = "light_on.png"
        Else
            SomeTabPage.ImageKey = "light_off.png"
        End If
        BlinkToggler = Not BlinkToggler
    End Sub

End Class

最佳答案

这是手工绘制图像的快速技巧(有几处需要调整,但这只是一个开始)。

Imports System.Threading

Public Class MyTabControl
    Inherits TabControl

    Private tabsImages As New Concurrent.ConcurrentDictionary(Of TabPage, List(Of String))
    Private tabsImagesKeys As New Concurrent.ConcurrentDictionary(Of TabPage, String)

    Private cycleImagesThread As Thread

    Private mInterval As Integer = 500

    Public Sub New()
        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)

        Me.DrawMode = TabDrawMode.OwnerDrawFixed

        cycleImagesThread = New Thread(AddressOf CycleImagesLoop)
        cycleImagesThread.Start()
    End Sub

    Protected Overrides Sub OnHandleCreated(e As EventArgs)
        If Me.FindForm IsNot Nothing Then AddHandler CType(Me.FindForm, Form).FormClosing, Sub() cycleImagesThread.Abort()
        MyBase.OnHandleCreated(e)
    End Sub

    Private Sub CycleImagesLoop()
        Do
            Thread.Sleep(mInterval)

            If tabsImagesKeys.Count > 0 Then
                For Each tabImageKey In tabsImagesKeys
                    Dim index = tabsImages(tabImageKey.Key).IndexOf(tabImageKey.Value)
                    index += 1
                    index = index Mod tabsImages(tabImageKey.Key).Count
                    tabsImagesKeys(tabImageKey.Key) = tabsImages(tabImageKey.Key)(index)
                Next

                Me.Invalidate()
            End If
        Loop
    End Sub

    Public Property Interval As Integer
        Get
            Return mInterval
        End Get
        Set(value As Integer)
            mInterval = value
        End Set
    End Property

    Public Sub SetImages(tabPage As TabPage, images As List(Of String))
        If tabsImages.ContainsKey(tabPage) Then
            tabsImages(tabPage) = images
        Else
            tabsImages.TryAdd(tabPage, images)
        End If
        tabsImagesKeys(tabPage) = images.First()
    End Sub

    Protected Overrides Sub OnDrawItem(e As DrawItemEventArgs)
        Dim g As Graphics = e.Graphics
        Dim r As Rectangle = e.Bounds
        Dim tab As TabPage = Me.TabPages(e.Index)
        Dim tabImage As Image

        Using b = New SolidBrush(IIf(e.State = DrawItemState.Selected, Color.White, Color.FromKnownColor(KnownColor.Control)))
            g.FillRectangle(b, r)
        End Using

        If tabsImagesKeys.Count > 0 OrElse Me.ImageList IsNot Nothing Then
            If tabsImagesKeys.ContainsKey(tab) Then
                tabImage = Me.ImageList.Images(tabsImagesKeys(tab))
                g.DrawImageUnscaled(tabImage, r.X + 4, r.Y + (r.Height - tabImage.Height) / 2)
            End If
            r.X += Me.ImageList.ImageSize.Width + 4
        End If

        Using b = New SolidBrush(tab.ForeColor)
            Dim textSize = g.MeasureString(tab.Text, tab.Font)
            g.DrawString(tab.Text, tab.Font, b, r.X, r.Y + (r.Height - textSize.Height) / 2)
        End Using

        MyBase.OnDrawItem(e)
    End Sub
End Class

按照以下步骤设置控件:

  1. 首先,将 ImageList 控件分配给 MyTabControl 并用图像填充它。
  2. 接下来,调用SetImages 方法来定义应该在每个选项卡上显示哪些图像。

    MyTabControl1.SetImages(TabPage1,新列表(字符串)来自 {“icon.gif”,“icon2.gif”}) MyTabControl1.SetImages(TabPage2,新列表(字符串)来自 {"myImage1.gif", "myImage2.gif"})

请注意,SetImages 方法的第二个参数是应该存在于 ImageList 上的键列表。 控件将完成剩下的工作......

关于c# - 更改选项卡图标时 TabControl 闪烁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14287452/

相关文章:

vb.net - Visual Basic 中的 SignalR

vb.net - 高亮显示ListView中的特定行

c# - 调用链中同一装饰器的多次注册

c# - MVVM - 通过双击选择列表框中的项目并加粗

c# - .net clr方法表结构

c# - 使用c#在连续纸上打印

c# - Unity3d - For 循环中的协程

c# - 为什么泛型类型在方法签名中出现两次?

.net - BindingRedirect 到不同的程序集名称

c# - 将 PHP HMAC SHA256 转换为 C#