Search code examples
.netvb.netwinformsprogress-barpinvoke

Change (custom) ProgressBar color


I'm creating a custom progress bar with a property

Public Class CustomProgressBar : Inherits ProgressBar

    Private _State As ProgressStates

    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, <MarshalAs(UnmanagedType.LPWStr)> ByVal lParam As String) As Int32
    End Function

    <Category("Appearance")> _
    <DefaultValue(ProgressStates.Normal)> _
    <Description("The progress state, Red=Error, Yellow=Warning, Green=Normal")> _
    Public Property State As ProgressStates
        Get
            Return _State
        End Get
        Set(value As ProgressStates)
            _State = value
            SendMessage(MyBase.Handle, 1040, value, 0)
        End Set
    End Property

End Class

ProgressStates

Public Enum ProgressStates

    Normal = 1
    [Error] = 2
    Warning = 3

End Enum

In the designer I set my custom property to Error and it works fine (in the designer), but when I run my application, progress value sets automatically to 0 and property is not applied

Designer and executable


Solution

  • It has nothing to do with the Property exactly, but the PInvoke is imperfect either in the source or your conversion. I suspect you started with this old C# answer.

    Imports System.Runtime.InteropServices
    Class NativeMethods
        <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=False)>
        Private Shared Function SendMessage(hWnd As IntPtr,
                                            Msg As UInt32,
                                            w As IntPtr,
                                            l As IntPtr) As IntPtr
        End Function
    
        Private Const PBM_SETSTATE = &H400 + 16
        Friend Enum PBMStates
            Normal = 1
            [Error] = 2
            Paused = 3
        End Enum
    
        Friend Shared Sub SetProgressState(ctl As ProgressBar, state As PBMStates)
            SendMessage(ctl.Handle, PBM_SETSTATE, New IntPtr(state), IntPtr.Zero)
        End Sub
    End Class
    

    According to the MSDN docs, PBM_SETSTATE returns the previous state. I've ignored that and made it a Sub. Since it only is supposed to be used with a ProgressBar I it to accept only a ProgressBar control rather than a control handle (which could be from any control). Finally, the code is Shared and in a NativeMethods class so CA will not complain. Usage:

    NativeMethods.SetProgressState(ProgressBar1, NativeMethods.PBMStates.Error)
    

    Result:

    enter image description here