Botón dentro de un cuadro de texto de winforms

¿Los cuadros de texto de winforms tienen propiedades que hacen que un botón incrustado, al final de la caja, sea posible?

Algo así como el botón de favoritos en el cuadro de dirección de Chrome:

enter image description here

También he visto algo como lo siguiente en algunos formularios de Excel:

enter image description here


EDITAR

He seguido la respuesta de Hans Passant con la adición de un controlador de eventos de clic y parece funcionar bien:

protected override void OnLoad(EventArgs e) { var btn = new Button(); btn.Size = new Size(25, textBoxFolder.ClientSize.Height + 2); btn.Location = new Point(textBoxFolder.ClientSize.Width - btn.Width, -1); btn.Cursor = Cursors.Default; btn.Image = Properties.Resources.arrow_diagright; btn.Click += btn_Click; textBoxFolder.Controls.Add(btn); // Send EM_SETMARGINS to prevent text from disappearing underneath the button SendMessage(textBoxFolder.Handle, 0xd3, (IntPtr)2, (IntPtr)(btn.Width << 16)); base.OnLoad(e); } [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp); private void btn_Click(object sender, EventArgs e) { MessageBox.Show("hello world"); } 

Obtener el botón dentro del TextBox solo requiere agregarlo a la caja ‘Colección de controles. También deberá hacer algo razonable para evitar que el texto dentro del cuadro desaparezca debajo del botón; eso requiere un poquito de pinvoke. Me gusta esto:

  protected override void OnLoad(EventArgs e) { var btn = new Button(); btn.Size = new Size(25, textBox1.ClientSize.Height + 2); btn.Location = new Point(textBox1.ClientSize.Width - btn.Width, -1); btn.Cursor = Cursors.Default; btn.Image = Properties.Resources.star; textBox1.Controls.Add(btn); // Send EM_SETMARGINS to prevent text from disappearing underneath the button SendMessage(textBox1.Handle, 0xd3, (IntPtr)2, (IntPtr)(btn.Width < < 16)); base.OnLoad(e); } [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp); 

Se veía así mientras probaba el margen derecho (debería haber elegido un bitmap más bonito):

enter image description here

Aquí está la respuesta envuelta en una subclase de TextBox.

 public class ButtonTextBox : TextBox { private readonly Button _button; public event EventHandler ButtonClick { add { _button.Click += value; } remove { _button.Click -= value; } } public ButtonTextBox() { _button = new Button {Cursor = Cursors.Default}; _button.SizeChanged += (o, e) => OnResize(e); this.Controls.Add(_button); } public Button Button { get { return _button; } } protected override void OnResize(EventArgs e) { base.OnResize(e); _button.Size = new Size(_button.Width, this.ClientSize.Height + 2); _button.Location = new Point(this.ClientSize.Width - _button.Width, -1); // Send EM_SETMARGINS to prevent text from disappearing underneath the button SendMessage(this.Handle, 0xd3, (IntPtr)2, (IntPtr)(_button.Width < < 16)); } [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp); } 

Si está dispuesto a agregar una referencia a otra biblioteca, podría considerar usar Krypton Toolkit (disponible en https://github.com/ComponentFactory/Krypton ). El kit de herramientas básico que debe poder utilizar de forma gratuita (sin las cintas, el navegador o la funcionalidad del espacio de trabajo) le permite agregar “especificaciones de botón” a varios controles (incluidos cuadros de texto) que aparecen visualmente tal como lo describe.

Vi en Reflector que Control contiene el método “SendMessage (int, int, int)” y encontré otra forma.

 using System; using System.Reflection; using System.Windows.Forms; static class ControlExtensions { static BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; static Type[] SendMessageSig = new Type[] { typeof(int), typeof(int), typeof(int) }; internal static IntPtr SendMessage(this Control control, int msg, int wparam, int lparam) { MethodInfo MethodInfo = control.GetType().GetMethod("SendMessage", flags, null, SendMessageSig, null); return (IntPtr)MethodInfo.Invoke(control, new object[] { msg, wparam, lparam }); } } 

Ahora al anular WndProc en ButtonTextBox podemos lograr el efecto deseado.

 public class ButtonTextBox : TextBox { Button button; public ButtonTextBox() { this.button = new Button(); this.button.Dock = DockStyle.Right; this.button.BackColor = SystemColors.Control; this.button.Width = 21; this.Controls.Add(this.button); } protected override void WndProc(ref Message m) { base.WndProc(ref m); switch (m.Msg) { case 0x30: int num = this.button.Width + 3; this.SendMessage(0xd3, 2, num < < 16); return; } } } 

Y creo que esta es una forma mucho más segura.

Una pequeña adición a la buena idea de Hans Passant: si el cuadro de texto es redimensionable, podría agregar un enlace al botón subyacente para que su ubicación también se ajuste en los cambios de ClientSize:

  //Adjust the Location of the button on Size Changes of the containing textBox. var binding = new Binding("Location", textBox1, "ClientSize"); binding.Format += (s, e) => e.Value = e.Value is Size ? new Point(((Size)e.Value).Width - btn.Width, -1) : new Point(0, 0); btn.DataBindings.Add(binding); 

Solo una pequeña expansión en la solución aceptada, para que el botón se vea y actúe correctamente, se necesitan algunos ajustes. Aquí hay ajustes para un cuadro de búsqueda:

  private static readonly int SEARCH_BUTTON_WIDTH = 25; private void ConfigureSearchBox() { var btn = new Button(); btn.Size = new Size(SEARCH_BUTTON_WIDTH, searchBox.ClientSize.Height + 2); btn.Dock = DockStyle.Right; btn.Cursor = Cursors.Default; btn.Image = Properties.Resources.Find_5650; btn.FlatStyle = FlatStyle.Flat; btn.ForeColor = Color.White; btn.FlatAppearance.BorderSize = 0; btn.Click += btn_Click; searchBox.Controls.Add(btn); this.AcceptButton = btn; // Send EM_SETMARGINS to prevent text from disappearing underneath the button SendMessage(searchBox.Handle, 0xd3, (IntPtr)2, (IntPtr)(btn.Width < < 16)); } [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp); private void btn_Click(object sender, EventArgs e) { MessageBox.Show("hello world"); } 

No. Para hacer algo como eso, necesitas crear tu propio Control de usuario. Se puede juntar fácilmente desde un cuadro de texto y un botón. La dificultad es que si desea propiedades similares al cuadro de texto, tendrá que crear todas ellas. Al final es mucho código.