更改选项卡图标时TabControl闪烁

use*_*916 6 .net c# vb.net winforms

我要在这里失去理智.我一直在谷歌搜索一小时试图解决这个小问题,但令人难以置信的加重问题.

我有TabControl我的形式有两个选项卡上.每个选项卡都有一个16x16图标和一些文本.这里没什么可疯狂的.

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

但是,它导致所有标签标题重绘,这使它们闪烁.

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
Run Code Online (Sandbox Code Playgroud)

这有效,因为它不会闪烁......但除非鼠标光标悬停在导致重绘的内容之上,否则图标也不会在视觉上发生变化.

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

骨架代码:

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
Run Code Online (Sandbox Code Playgroud)

xfx*_*xfx 3

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

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
Run Code Online (Sandbox Code Playgroud)

请按照以下步骤设置控件:

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

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

请注意,该方法的第二个参数SetImages是应该存在于ImageList. 控制器将完成剩下的工作...