armadillo.mx
armadillo.mx

Reputation: 1001

How to make a control to be painted/refreshed properly

I have a control derived from checkbook which I called "SettingBooleanButton", but when any window or dialog is dragged over the control the control keeps signs of the drag

The next image shows the effect of dragging an application window over control

ScreenShot

This is the code block that I have for OnPaint()

Public Class SettingBooleanButton
    Inherits CheckBox

    Private _settingSection As String
    Private _settingName As String
    Private _associatedSetting As Setting

    Public Event StateChange(ByVal affectedSetting As Setting)

    Sub New()
        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        Appearance = Appearance.Button
        FlatStyle = FlatStyle.Flat
        TextAlign = ContentAlignment.MiddleCenter
        AutoSize = False
    End Sub

    Public Property SettingSection As String
        Get
            Return _settingSection
        End Get
        Set(value As String)
            _settingSection = value
        End Set
    End Property

    Public Property SettingName As String
        Get
            Return _settingName
        End Get
        Set(value As String)
            _settingName = value
        End Set
    End Property

    ''' <summary>
    ''' Sets a boolean value to indicate the initial checked state of the control.
    ''' </summary>
    ''' <value>
    '''   <c>true</c> to set it as [checked state]; otherwise, <c>false</c>.
    ''' </value>
    Public Property CheckedState As Boolean
        Get
            Return Checked
        End Get
        Set(value As Boolean)
            _associatedSetting = New Setting(_settingSection, _settingName, String.Empty)

            RemoveHandler CheckedChanged, AddressOf StateChanged
            Checked = value
            SetText()
            AddHandler CheckedChanged, AddressOf StateChanged
        End Set
    End Property

    Private Sub StateChanged(sender As Object, e As EventArgs)
        If IsNothing(_associatedSetting) Then
            Return
        End If

        _associatedSetting.Value = Checked.ToString()
        SetText()
        RaiseEvent StateChange(_associatedSetting)
    End Sub

    Public Sub SetText()
        If Checked Then
            Font = New Font(Font.FontFamily, Font.Size, FontStyle.Bold)
            ForeColor = Color.WhiteSmoke
            Text = Resource.SettingBooleanButton_TrueState
        Else
            Font = New Font(Font.FontFamily, Font.Size, FontStyle.Regular)
            ForeColor = SystemColors.ControlText
            Text = Resource.SettingBooleanButton_FalseState
        End If
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
        MyBase.OnPaint(e)

        If Checked Then
            ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, Color.Black, ButtonBorderStyle.Solid)               
        End If
    End Sub

End Class

Upvotes: 1

Views: 1887

Answers (2)

TheWhitde
TheWhitde

Reputation: 41

Sometimes the simplest solutions (or causes) are overlooked.

I have a panel with 15 buttons on it and each has an image. Depending on rows selected from a data grid they all might be enabled or disabled.

It all worked fine except toggling between enabled and disabled was taking 2+ seconds and caused lag when multi-selecting from the data grid.

Tried a few things, then I thought maybe it was something to do with the images.

The images were all in an imagelist and size was set to 24,24 which was a compromise between 32,32 and 16,16. I changed the size in the imagelist to 32,32 as that is the native size of all the images... and shazam!!! All the buttons are basically rendered instantly now. No idea ATM whether being small PNG images makes a difference... but I'm going to convert all the images I have to ICO format.

Also... as all my buttons are on a panel I enable/disable the panel which in turn enables and disables all the children on it.

Upvotes: 0

Hans Passant
Hans Passant

Reputation: 941227

   ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, ...)

Using e.ClipRectangle like this is a traditional bug in a Paint event handler. It is not a rectangle that matches the border you want to draw. It is only the part of the control that needs to be painted. Which is usually the entire control, but not always. Such as in your case when you drag a window across your control, only the part that is revealed needs to be repainted. So now you are painting the border in the wrong position, producing those black lines.

You only ever use the ClipRectangle if your painting code is expensive and you want to take the opportunity to skip that expensive code when it isn't needed anyway. Which is pretty rare, clipping in Windows is already pretty efficient.

You'll need to pass the actual rectangle of your border. Fix:

   ControlPaint.DrawBorder(e.Graphics, Me.ClientRectangle, _
                           Color.Black, ButtonBorderStyle.Solid)

Upvotes: 8

Related Questions