Wine Too
Wine Too

Reputation: 4655

Hiding up/down buttons on NumericUpDown control

I am trying to subclass NumericUpDown in several ways to get better functionality and appearance.

Since NUD is construct of two controls I would like to hide up/down buttons in cases where property "Increment" is set to 0.

This code is in subclass:

Protected Overrides Sub OnTextBoxResize(ByVal source As Object, ByVal e As System.EventArgs)

        Controls(0).Hide()
End Sub

... and it work OK. But in that function I cannot check a value of Increment property like this:

Protected Overrides Sub OnTextBoxResize(ByVal source As Object, ByVal e As System.EventArgs)

If Me.Increment = 0 Then
      Controls(0).Hide()
End if
End Sub

In scope of this function Me is not reachable. I am also try with using local variables but can't find which event is fired before OnTextBoxResize to read value of Increment property.

What to do in such case to get desired functionality?

Upvotes: 4

Views: 6588

Answers (2)

LarsTech
LarsTech

Reputation: 81610

This seems to work fairly well. It Shadows the Increment property in order to set the visibility of the spinner controls when the Increment value is being changed. There is an underlying private method the base control calls called PositionControls which you cannot stop — that method could create some flicker, but on my test, it didn't.

Public Class MyNumBox
  Inherits NumericUpDown

  Shadows Property Increment As Decimal
    Get
      Return MyBase.Increment
    End Get
    Set(value As Decimal)
      MyBase.Increment = value
      OnTextBoxResize(Me, EventArgs.Empty)
    End Set
  End Property

  Protected Overrides Sub OnHandleCreated(e As EventArgs)
    MyBase.OnHandleCreated(e)
    OnTextBoxResize(Me, EventArgs.Empty)
  End Sub

  Protected Overrides Sub OnTextBoxResize(source As Object, e As EventArgs)
    If Me.IsHandleCreated Then
      Me.Height = Me.PreferredHeight
      Me.Controls(0).Visible = (MyBase.Increment > 0)
      Dim borderWidth As Integer = 0
      If Me.BorderStyle > BorderStyle.None Then
        borderWidth = SystemInformation.Border3DSize.Width
      End If
      Dim textWidth As Integer
      If Me.Increment = 0 Then
        textWidth = Me.ClientSize.Width - (borderWidth * 2)
      Else
        textWidth = Me.ClientSize.Width - Me.Controls(0).Width - (borderWidth * 2)
      End If
      If Me.UpDownAlign = LeftRightAlignment.Left Then
        If Me.Increment = 0 Then
          Me.Controls(1).SetBounds(borderWidth, borderWidth, _
                                   textWidth, Me.Controls(1).Height)
        Else
          Me.Controls(1).SetBounds(borderWidth + Me.Controls(0).Width, _
                                   Me.Controls(1).Top, textWidth, Me.Controls(1).Height)
        End If
      Else
        Me.Controls(1).SetBounds(borderWidth, Me.Controls(1).Top, _
                                 textWidth, Me.Controls(1).Height)
      End If
      Me.Refresh()
    End If
  End Sub
End Class

In the OnTextBoxResize override, I am re-positioning the controls into their proper place, and this version does account for the UpDownAlign property.

Upvotes: 4

Idle_Mind
Idle_Mind

Reputation: 39122

If you can, read this thread over at EE where I answered a similar question. It resizes the edit portion so that the control is redrawn correctly when the buttons have been hidden and the control is resized. *Otherwise the portion of the control where the buttons used to be leaves ghosts behind.

One solution to your specific problem is to wait for the VisibleChanged() event and check the Increment() property from there. Here is a conversion of my previous answer with some minor changes:

Public Class NoArrowNumericUpDown
    Inherits NumericUpDown

    Private itb As InnerTextBox = Nothing

    Protected Overrides Sub OnVisibleChanged(e As System.EventArgs)
        If Me.Visible Then
            If Me.Increment = 0 AndAlso IsNothing(itb) Then
                Dim ctl As Control = Me.Controls(0) ' get the spinners
                Me.Controls.Remove(ctl)  ' remove the spinners
                ctl = Me.Controls(0) ' get the edit control
                itb = New InnerTextBox(Me, ctl)
            End If
        End If

        MyBase.OnVisibleChanged(e)
    End Sub

    Public Class InnerTextBox
        Inherits NativeWindow

        Private parentControl As Control = Nothing
        Const WM_WINDOWPOSCHANGING As Integer = &H46

        Public Sub New(parentControl As Control, InnerTextBox As Control)
            Me.parentControl = parentControl
            Me.AssignHandle(InnerTextBox.Handle)
        End Sub

        Protected Overrides Sub WndProc(ByRef m As Message)
            Select Case m.Msg
                Case WM_WINDOWPOSCHANGING
                    Dim wp As WindowPos = CType(System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(WindowPos)), WindowPos)
                    If Me.parentControl IsNot Nothing Then
                        wp.cx = Me.parentControl.ClientSize.Width - 2 * wp.x
                        wp.cy = Me.parentControl.ClientSize.Height
                        System.Runtime.InteropServices.Marshal.StructureToPtr(wp, m.LParam, True)
                    End If
                    Exit Select
            End Select

            MyBase.WndProc(m)
        End Sub

        Public Structure WindowPos
            Public hwnd As IntPtr
            Public hwndInsertAfter As IntPtr
            Public x As Integer
            Public y As Integer
            Public cx As Integer
            Public cy As Integer
            Public flags As UInteger
        End Structure

    End Class

End Class

EDIT: You could just enclose your code in a Try/Catch block?

Public Class NoArrowNumericUpDown
    Inherits NumericUpDown

    Protected Overrides Sub OnTextBoxResize(ByVal source As Object, ByVal e As System.EventArgs)
        Try
            If Me.Increment = 0 Then
                Controls(0).Hide()
            End If
        Catch ex As Exception
        End Try
    End Sub

End Class

Upvotes: 1

Related Questions