【问题标题】:Button inside a WinForms textboxWinForms 文本框内的按钮
【发布时间】:2013-04-07 22:53:55
【问题描述】:

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

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

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


编辑

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

【问题讨论】:

  • @Highcore,你是一头无可救药的小马。请将 [winforms] 标签添加到您忽略的标签中,我不想再看到您的 wpf 咆哮了。
  • @HighCore 在汉斯所说的同一件事之前,我已经提醒过你。我强烈建议您从现在开始避免 WinForms 问题。
  • @HansPassant .... 我刚下班回来,正在处理问题——看来你们这些好人把他吓跑了!

标签: c# .net winforms


【解决方案1】:

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

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

【讨论】:

  • +1 谢谢汉斯 - 我会看看它是否在我的项目中实现;看起来很简单——找个位图:导入到项目资源中;给出位图名称;将您的代码与新的命名位图一起使用 ....
  • 似乎工作正常:我还添加了 btn.Click += btn_Click; 行,以便它做一些事情。如果我破坏了一些好的代码,我会用我所拥有的东西来修改 OP,因为我不是 100% 确定的。
  • @Hans Passant - 这是一个很棒的解决方案,谢谢。您能否详细说明一下 SendMessage 函数的工作原理?
  • 我知道这是去年发布的(并且已关闭),但我已将上面的代码实现到自定义文本框类中,同时还实现了水印功能。我无法阻止文本在按钮下消失。我什至尝试修改文本框的客户端大小。
  • 我没有设置位置属性,而是使用“btn.Dock = DockStyle.Right”,所以当我的文本框变大时,按钮会保持在右侧。
【解决方案2】:

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

}

【讨论】:

  • 遗憾的是,使用这个版本有一些非常奇怪的副作用。例如,您可以通过展开 ButtonTextBox 下的“Button”成员在设计器中设置 Button 的 Image 字段,但是一旦您点击 Debug / Run,Button 就会“忘记”图像,回到设计器中,设置就消失了还有……
【解决方案3】:

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

现在通过重写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;
        }
    }
}

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

【讨论】:

  • 对解决方案投了赞成票,但我想知道它是如何做到的。这些数字代表什么?
  • 感谢您的回复。我知道 0x0030 是 WM_SETFONT 消息,但是为什么 2 和 num
【解决方案4】:

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

【讨论】:

    【解决方案5】:

    只需对已接受的解决方案进行小幅扩展,就需要对按钮进行一些调整。以下是搜索框的调整:

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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

      • 我在 OP 中显示的第二个屏幕打印来自 Excel 表单,所以它一定在某个地方吗?我可以从 Excel 库中继承它吗?
      • @whytheq - 使用 Windows API 和 MFC 用 C++ 编写的 Excel。它是一个自定义的书面控件。您可以在 WinForms 中执行类似的操作,但您需要创建自定义控件。我建议学习WPF。更容易做到这一点
      • @thorkia - 看起来 excel 的想法有点牵强,但请查看 Hans 解决方案 + cmets 到 OP。
      【解决方案7】:

      对 Hans Passant 的好主意的一个小补充 - 如果文本框是可调整大小的,您可以添加一个绑定到底层按钮,以便它的 Location 也可以根据 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);
      

      【讨论】:

        猜你喜欢
        • 2013-12-02
        • 1970-01-01
        • 2022-12-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-10
        • 1970-01-01
        相关资源
        最近更新 更多