C# WinForms 文本框中的按钮

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15868817/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 18:13:38  来源:igfitidea点击:

Button inside a WinForms textbox

c#.netwinforms

提问by whytheq

Do WinForms textboxes have any properties that make an embedded button, at the end of the box, possible?

WinForms 文本框是否具有使嵌入按钮(在框的末尾)成为可能的任何属性?

Something like the favorites button on the Chrome address box:

类似于 Chrome 地址框中的收藏夹按钮:

enter image description here

在此处输入图片说明

I've also seen something like the following in some Excel forms:

我还在某些 Excel 表单中看到过类似以下内容:

enter image description here

在此处输入图片说明



EDIT

编辑

I've followed Hans Passant's answer with the addition of a click event handler and it seem to work ok:

我按照 Hans 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");
}

采纳答案by Hans Passant

Getting the button inside the TextBox just requires adding it to the box' Controls collection. You'll also need to do something reasonable to prevent the text inside the box disappearing underneath the button; that requires a wee bit of pinvoke. Like this:

获取 TextBox 内的按钮只需要将其添加到框的 Controls 集合中。您还需要做一些合理的事情来防止框内的文本在按钮下方消失;这需要一点点pinvoke。像这样:

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

Looked like this while I tested the right margin (should have picked a prettier bitmap):

当我测试右边距时看起来像这样(应该选择更漂亮的位图):

enter image description here

在此处输入图片说明

回答by zam664

No. In order to do something like that you need to create your own User Control. It can be easily put together from a text box and button. The difficulty is that if you want similar properties to the text box, you would need to create all them. In the end it is a lot of code.

不。为了做类似的事情,您需要创建自己的用户控件。它可以通过文本框和按钮轻松组合在一起。困难在于,如果您想要与文本框相似的属性,则需要创建所有这些属性。最后是很多代码。

回答by erdomke

If you are willing to add a reference to another library, you could consider using the Krypton Toolkit (available at https://github.com/ComponentFactory/Krypton). The basic toolkit that you should be able to use for free (without the ribbons, navigator, or workspace functionality) does allow you to add "button specs" to various controls (including text boxes) which appear visually just as you describe.

如果您愿意添加对另一个库的引用,您可以考虑使用 Krypton Toolkit(可在https://github.com/ComponentFactory/Krypton 获得)。您应该能够免费使用的基本工具包(没有功能区、导航器或工作区功能)确实允许您将“按钮规格”添加到各种控件(包括文本框),这些控件与您描述的一样直观。

回答by Herman Schoenfeld

Here's the answer wrapped in a TextBox subclass.

这是包含在 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);

}

回答by Marwie

A minor addition on the nice idea of Hans Passant - if the textbox is resizable, you could add a binding to the underlying button so that it's Location gets adjusted on ClientSize changes as well:

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

回答by johnarleyburns

Just a small expansion on the accepted solution, to get the button to look and act properly a few tweaks are needed. Here are tweaks for a search box:

只是对已接受解决方案的一个小扩展,为了让按钮看起来和动作正确,需要进行一些调整。以下是对搜索框的调整:

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

回答by Bahrom

I saw in Reflector that Control contains "SendMessage(int,int,int)" method and I found another way.

我在 Reflector 中看到 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 });
    }
}

Now by overriding WndProc in ButtonTextBox we can achieve desired effect.

现在通过覆盖 ButtonTextBox 中的 WndProc 我们可以达到预期的效果。

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

And I think this is much safer way.

而且我认为这是更安全的方式。