Textbox not clickable but editable

2019-03-06 11:39发布

问题:

I have a small form with 10 textboxes, I have them set in the right Tab Order currently the way I want them to Tab To. I was wondering if there is a way to set the textboxes up so they cannot be selected for editing unless they are Tabbed into. ie... I don't want the end user to be able to click on the textbox to edit them, I only want them editable through Tabbing.

回答1:

This should do the trick

public partial class PoorTextBox : TextBox
{
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == (int) WM.LBUTTONDOWN)
        {
            return;//Eat mouse down events 
        }
        base.WndProc(ref m);
    }
}

Window messages enum can be found here.


How to do it without inheriting TextBox :

class EatMouseDown : NativeWindow
{
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == (int)WM.LBUTTONDOWN)
        {
            return;
        }
        base.WndProc(ref m);
    }
}

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

    new EatMouseDown().AssignHandle(textBox1.Handle);//Subclass a Handle
}


How to do it without any inheritance:

Clean up part omitted, which is also important. This may be buggy but that works. Recommended way is to use inheritance. Required methods pulled from .net fw src.

class EatMouseDown
{
    public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

    #region External methods...

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SetWindowLong(HandleRef hWnd, int nIndex, WndProc wndproc);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SetWindowLongPtr(HandleRef hWnd, int nIndex, WndProc wndproc);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr GetWindowLong(HandleRef hWnd, int nIndex);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr GetWindowLongPtr(HandleRef hWnd, int nIndex);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr CallWindowProc(IntPtr wndProc, IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

    #endregion

    private const int GWLP_WNDPROC = -4;
    private IntPtr handle;
    private IntPtr originalWndProc;
    private IntPtr currentWndProc;

    public static IntPtr SetWindowLongHelper(HandleRef hWnd, int nIndex, WndProc wndProc)
    {
        return IntPtr.Size == 4
            ? SetWindowLong(hWnd, nIndex, wndProc)
            : SetWindowLongPtr(hWnd, nIndex, wndProc);
    }

    public static IntPtr GetWindowLongHelper(HandleRef hWnd, int nIndex)
    {
        return IntPtr.Size == 4
            ? GetWindowLong(hWnd, nIndex)
            : GetWindowLongPtr(hWnd, nIndex);
    }

    internal void SubclassHandle(IntPtr handle)
    {
        this.handle = handle;
        this.originalWndProc = GetWindowLongHelper(new HandleRef(this, handle), GWLP_WNDPROC);
        SetWindowLongHelper(new HandleRef(this, handle), GWLP_WNDPROC, new WndProc(this.Callback));
        this.currentWndProc = GetWindowLongHelper(new HandleRef(this, handle), GWLP_WNDPROC);
    }

    private IntPtr Callback(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam)
    {
        var m = Message.Create(hwnd, msg, wparam, lparam);
        if (m.Msg == (int)WM.LBUTTONDOWN)
        {
            return IntPtr.Zero;
        }
        return CallWindowProc(originalWndProc, hwnd, msg, wparam, lparam);
    }
}

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

    new EatMouseDown().SubclassHandle(textBox1.Handle);//Subclass a Handle
}


回答2:

Here's a similar approach to what Sriram Sakthivel had done, but using IMessageFilter instead:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        List<TextBox> TextBoxes = new List<TextBox>();
        FindTextBoxes(this, TextBoxes);
        Application.AddMessageFilter(new SuppressTextBoxClicks(TextBoxes));
    }

    private void FindTextBoxes(Control ctl, List<TextBox> TBs)
    {
        foreach(Control childCtl in ctl.Controls)
        {
            if (childCtl is TextBox)
            {
                TBs.Add((TextBox)childCtl);
            }
            else if(childCtl.HasChildren)
            {
                FindTextBoxes(childCtl, TBs);
            }
        }
    }
}

public class SuppressTextBoxClicks : IMessageFilter
{

    private List<TextBox> _TextBoxes = null;
    private const int WM_LBUTTONDOWN = 0x201;

    public SuppressTextBoxClicks(List<TextBox> TextBoxes)
    {
        _TextBoxes = TextBoxes;
    }

    public bool PreFilterMessage(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_LBUTTONDOWN:
                if (_TextBoxes != null)
                { 
                    foreach(TextBox TB in _TextBoxes)
                    {
                        if (TB.Handle.Equals(m.HWnd))
                        {
                            return true;
                        }
                    }
                }

                break;

            default:
                break;
        }
        return false;
    }

}


回答3:

Set all textboxes' Enabled property to false except the first one. On the TextChanged Event, check if it the Text is empty or not. if it was not empty, Enable the next TextBox and so on...



回答4:

You can also try this one with the Enter event foreach TextBox

    private void textBox2_Enter(object sender, EventArgs e)
    {
        if (textBox1.Text == "")
            textBox1.Focus();
    }

    private void textBox3_Enter(object sender, EventArgs e)
    {
        if (textBox1.Text == "")
            textBox1.Focus();
        else
            if (textBox2.Text == "")
                textBox2.Focus();
    }

    private void textBox4_Enter(object sender, EventArgs e)
    {
        if (textBox1.Text == "")
            textBox1.Focus();
        else
            if (textBox2.Text == "")
                textBox2.Focus();
            else
                if (textBox3.Text == "")
                    textBox3.Focus();
    }