Selecting Control Text

January 17, 2006 — 8 Comments

Last year I wrote a post on selecting text in a textbox control in two parts.  You can find Part 1 here and Part 2 here.  Another question came up in the news groups on selecting the text of a NumericUpDown control.  Again sounds straight forward since the full .NET framework supports UpDownBase.Select.  To get this functionality requires a little bit of work but is possible. 


Basically to send a message to a control you have to PInvoke SendMessage in coredll.dll (user32.dll for the desktop).  Here is the signature for that:


[DllImport(“coredll.dll”, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);


To tell the control to select the text, the int msg parameter should be EM_SETSEL which is defined as follows:


private const int SETSEL = 0x00B1;


Now putting all together.  Using Visual Studio 2005 we can create a class called NumericUpDownWithSelect and override the OnGotFocus method.  Here is the implementation


protected override void OnGotFocus(EventArgs e)
{
     base.OnGotFocus(e);

     if (!this.suppressOnGotFocus)
     {
          ThreadStart t = delegate()
          {
               this.Invoke(new SelectAllInvoke(SelectAll));
          };
     new Thread(t).Start();
}


You will notice we are using an anonymous method to invoke the SelectAll Method on the main thread.  Here is the delegate signature and the implementation of SelectAll() and Select():


private delegate void SelectAllInvoke();
public void SelectAll()
{
     this.SelectInternal(0, this.Value.ToString().Length);
}

public void Select(int start, int length)
{
     this.SelectInternal(start, length);
}


SelectInternal() is the code that does the actual work. 


private void SelectInternal(int start, int length)
{
     this.suppressOnGotFocus = true;
     if (!this.Focused)
          this.Focus();
     IntPtr ret = SendMessage(this.Handle, SETSEL, start, length);
     this.suppressOnGotFocus = false;
}


So putting it all together in a nice class that inherits from System.Windows.Forms.NumericUpDown you can extend the functionality of the base class.  Here is the full implementation:


public class NumericUpDownWithSelect : NumericUpDown
{
     public NumericUpDownWithSelect()
     {
     }

     [DllImport(“coredll.dll”, SetLastError = true)]
     private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

     private const int GETSEL = 0x00B0;
     private const int SETSEL = 0x00B1;

     private delegate void SelectAllInvoke();

     private bool suppressOnGotFocus = false;

     protected override void OnGotFocus(EventArgs e)
     {
          base.OnGotFocus(e);
          if (!this.suppressOnGotFocus)
          {
               ThreadStart t = delegate()
               {
                    this.Invoke(new SelectAllInvoke(SelectAll));
               };
               new Thread(t).Start();
          }
     }

     public void SelectAll()
     {
          this.SelectInternal(0, this.Value.ToString().Length);
     }

     public void Select(int start, int length)
     {
          this.SelectInternal(start, length);
     }

     private void SelectInternal(int start, int length)
     {
          this.suppressOnGotFocus = true;
          if (!this.Focused)
               this.Focus();
          IntPtr ret = SendMessage(this.Handle, SETSEL, start, length);
          this.suppressOnGotFocus = false;
     }
}


Download the binaries here: NumericUpDownWithSelect_Binaries.zip (3.67 KB) 

8 responses to Selecting Control Text

  1. Thank you so much for this. You just made an unsable GUI perfect!

  2. I find your solution a bit awkward. By explicitly importing Core.dll stuff and sending messages I think the solution isn’t portable. Does it work in Mono?

    Since NumericUpDown already has a Select() method, why not use that one? I’ve created an EnhancedNumericUpDown class that adds the SelectAll() method to NumericUpDown. This method uses Select(0,Text.length) to select all text. Then I use a simple Timer to schedule a call to SelectAll() when the EnhancedNumericUpDown receives focus. I’ve also added a AutoSelect property that you can use to enable/disable this AutoSelect behaviour (Borland Delphi’s Edit controls also have this property).

    You can download my stuff here:

    http://www.wirwar.com/EnhancedNumericUpDown.zip

  3. Leon, There really is nothing awkward about the solution, it adds functionality to a Compact Framework control similar to the desktop. Select() is not supported in the .NET Compact Framework 1.0 or 2.0. This control was written for Compact Framework only and not for Full .NET Framework or Mono.

  4. Oh, I see. I didn’t realise you were talking about the Compact Framework 🙂 I just Googled for "NumericUpDown SelectAll" and found your page.

  5. A workaround for this may be to simply place a textbox over the text portion of the NumericUpDown control (it vusually appears to simply be a numericupdown control). Then progmatically set the numericupdown.valuechanged event to change the value of the overlaid textbox. Also set the textbox.valuechanged event to change the value of the numericupdown.text value. Kind of a wierd way of doing it, but I had to do it because I don’t know J#, just Visual Basic. I don’t want to take the time to translate the above code into Visual Basic.
    -Corey

  6. Thanks for this code Mark. It is concise, elegant and effective. As well as solving my immediate problem, it teaches me a new way to approach this kind of issue.

    Keep up the great work.

    Many thanks,
    StonePiano

  7. Great code, Thanks a lot!! 🙂

  8. if u need the VB .NET code here it is

    Imports System.Runtime.InteropServices
    Imports System.Threading

    Public Class NumericUpDownWithSelect
    Inherits NumericUpDown
    Public Sub NumericUpDownWithSelect()

    End Sub

    <DllImport("coredll.dll", SetLastError:=True)> _
    Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As IntPtr
    End Function

    Private Const GETSEL As Integer = 176
    Private Const SETSEL As Integer = 177

    Private Delegate Sub SelectAllInvoke()

    Private suppressOnGotFocus As Boolean = False

    Private Sub SelectInternal(ByVal start As Integer, ByVal length As Integer)
    Me.suppressOnGotFocus = True
    If Not (Me.Focused) Then
    Me.Focus()
    End If
    Dim ret As IntPtr = SendMessage(Me.Handle, SETSEL, start, length)
    Me.suppressOnGotFocus = False
    End Sub

    Public Sub SelectAll()
    Me.SelectInternal(0, Me.Value.ToString().Length)
    End Sub

    Public Sub [Select](ByVal start As Integer, ByVal length As Integer)
    Me.SelectInternal(start, length)
    End Sub

    Protected Overloads Overrides Sub OnGotFocus(ByVal e As EventArgs)
    Me.OnGotFocus(e)
    If Not Me.suppressOnGotFocus Then

    Dim t As New Thread(AddressOf NumericUpDownWithSelect)
    Me.Invoke(New SelectAllInvoke(AddressOf SelectAll))
    t.Start()
    End If
    End Sub
    End Class

Leave a Reply