2013-04-07 4 views
32

Есть ли в winforms текстовые поля какие-либо свойства, которые делают встроенную кнопку, в конце окна, возможной?Кнопка внутри текстового поля winforms

Что-то вроде кнопки избранного на Chrome поле Адрес:

enter image description here

Я также видел что-то вроде следующего в некоторых формах Excel:

enter image description here


EDIT

Я последовал ответ Ганса Passant с добавлением обработчика событий нажмите и, кажется, работает нормально:

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"); 
    } 
+7

@ Highcore, вы безнадежный однопоточный пони. Добавьте тэг [winforms] к вашим игнорированным тегам, я больше не хочу видеть ваши wpf rants. –

+0

@HighCore Я уже говорил вам то же самое, что говорит Ганс. Я настоятельно рекомендую вам просто избегать вопросов WinForms с этого момента. –

+0

@ HansPassant .... Я только что вернулся из отпуска и догнал вопросы - похоже, вы, хорошие люди, напугали его! – whytheq

ответ

44

Получение кнопки внутри TextBox просто требует добавления ее в поле «Коллекция элементов управления». Вам также нужно будет сделать что-то разумное, чтобы текст внутри коробки не исчез под кнопкой; что требует бит-булавки. Как это:

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); 

Выглядел это время я проверил правый край (должно выбрал покрасивее растровый):

enter image description here

+0

+1 спасибо Хансу - я посмотрю, реализует ли он в моем проекте; выглядит просто - найдите растровое изображение: импортируйте в ресурсы проекта; указать имя растрового изображения; используйте свой код с новым именованным bitmap .... – whytheq

+0

, похоже, работает нормально: я также добавил строку 'btn.Click + = btn_Click;', чтобы он что-то делал. Я поправлю OP с тем, что у меня на 100% не уверен, если я испортил хороший код. – whytheq

+0

@ Hans Passant - это потрясающее решение, спасибо. Не могли бы вы добавить некоторые подробности о том, как работает функция SendMessage? – JWiley

0

Нет. Для того, чтобы сделать что-то подобное, что вам нужно, чтобы создать свой собственный пользовательский элемент управления. Его можно легко собрать из текстового поля и кнопки. Трудность состоит в том, что если вы хотите, чтобы аналогичные свойства были в текстовом поле, вам нужно было бы создать их все. В конце концов, это много кода.

+0

Эта вторая экранная печать, которую я показал в OP, из формы Excel, поэтому она должна быть где-то там? Могу ли я наследовать его из библиотеки Excel? – whytheq

+1

@whytheq - Excel написан на C++ с использованием Windows API и MFC. Это индивидуальный письменный контроль. Вы можете сделать что-то подобное в WinForms, но вам нужно будет создать собственный элемент управления. Я бы предложил изучить WPF. Гораздо легче сделать это – thorkia

+0

@thorkia - похоже, что идея excel была немного надуманна, но, пожалуйста, проверьте решение Hans + комментарии к OP. – whytheq

3

Если вы готовы добавить ссылку на другую библиотеку, вы может рассмотреть возможность использования инструментария Krypton Toolkit (доступно по адресу https://github.com/ComponentFactory/Krypton). Основной набор инструментов, который вы можете использовать бесплатно (без лент, навигаторов или функций рабочего пространства), позволяет вам добавлять «спецификации кнопок» к различным элементам управления (включая текстовые поля), которые визуально отображаются так же, как вы описываете.

7

Вот ответ, заключенный в подкласс 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); 

} 
+0

К сожалению, использование этой версии имеет очень странные побочные эффекты. Например, вы можете установить поле «Изображение кнопки» в Дизайнере, разворачивая элемент «Button» под ButtonTextBox, но как только вы нажмете «Debug/Run», кнопка «забудет» изображение и обратно в дизайнере, настройка не будет а также ... – MrCC

1

Незначительная добавка на хорошей идее Ганса Passant - если текстовое поле изменяемого размера, можно добавить привязку к базовой кнопки, так что его местоположение получает настроены на 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); 
1

Просто небольшое расширение на принятом решении, чтобы заставить кнопку выглядеть и действовать должным образом, необходимы несколько настроек. Вот твики для окна поиска:

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"); 
    } 
3

я видел в рефлектор, который содержит Control «SendMessage (INT, INT, INT)» метод, и я нашел другой способ.

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 }); 
    } 
} 

Теперь, переопределив WndProc в ButtonTextBox, мы сможем добиться желаемого эффекта.

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; 
     } 
    } 
} 

И я думаю, что это намного безопаснее.

+0

Вверх проголосовали за решение, но мне осталось интересно, как он делает то, что он делает. Что стоят эти цифры? – Lara

+0

Спасибо за ответ. Я знаю, что 0x0030 является сообщением WM_SETFONT, но почему 2 и num << 16, когда wParam должен быть дескриптором шрифта, а младшее слово lParam - булевым для перерисовки? Кроме того, вы теперь думаете, что Ганс лучше всего подходит? Для меня предпочтительнее самостоятельный класс. – Lara