Добавление кнопки в ComboBox и изменение размера внутреннего элемента управления TextBox

Я пытаюсь создать собственное поле со списком. Я хочу иметь кнопку внутри поля со списком, чтобы пользователь мог добавлять элементы в свой источник данных. Я знаю, что мне нужно использовать WndProc, чтобы получить внутреннее текстовое поле, но я не уверен, как его получить, а затем отредактировать его длину, чтобы не закрывать кнопку при выборе.

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

вот как выглядит поле со списком, когда я ввожу

вот как я хочу, чтобы выглядело поле со списком

мой код - это абсолютный беспорядок, от попыток разобраться в этом я ничего не добился. Я скажу, что добавил элемент управления «Кнопка» в поле со списком, поскольку хочу получить доступ к встроенным функциям.

public ComboBoxButton() : base()
{
    
    _button1.Size = buttonSize;
    this.DrawMode = DrawMode.OwnerDrawVariable;
    _button1.BackColor = SystemColors.Control;
    _button1.Text = "?";
    _button1.Location = buttonLocation;
    this.Controls.Add(_button1);
    SetStyle(ControlStyles.OptimizedDoubleBuffer, true);            
}

Я попробовал несколько разных способов получить контроль над редактированием через

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);

Я также нашел этот код и надеялся, что смогу отредактировать его, чтобы изменить размер текстового поля, но не смог.

private class TextWindow : NativeWindow
{
    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    private struct COMBOBOXINFO
    {
        public Int32 cbSize;
        public RECT rcItem;
        public RECT rcButton;
        public int buttonState;
        public IntPtr hwndCombo;
        public IntPtr hwndEdit;
        public IntPtr hwndList;
    }

    [DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
    private static extern IntPtr SendMessageCb(IntPtr hWnd, int msg, IntPtr wp, out COMBOBOXINFO lp);

    public TextWindow(ComboBox cb)
    {
        COMBOBOXINFO info = new COMBOBOXINFO();
        info.cbSize = Marshal.SizeOf(info);
        SendMessageCb(cb.Handle, 0x164, IntPtr.Zero, out info);
        this.AssignHandle(info.hwndEdit);
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == (0x0302))
        {
            MessageBox.Show("No pasting allowed!");
            return;
        }
        base.WndProc(ref m);
    }
}

private TextWindow textWindow;

protected override void OnHandleCreated(EventArgs e)
{
    textWindow = new TextWindow(this);
    base.OnHandleCreated(e);
    listBoxHandle = GetComboBoxListInternal(this.Handle, out COMBOBOXINFO info);
}

protected override void OnHandleDestroyed(EventArgs e)
{
    textWindow.ReleaseHandle();            
    base.OnHandleDestroyed(e);
}
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы получить дескриптор и положение элемента управления Edit вашего пользовательского ComboBox, вы можете вызвать GetComboBoxInfo , который возвращает структуру COMBOBOXINFO, которая ссылается на эту информацию (а также на кнопку со стрелкой, конечно).

Вы сейчас отправляете сообщение CB_GETCOMBOBOXINFO; то же самое, вы получите ту же информацию обратно.

Когда вы добавляете свою пользовательскую кнопку в поле со списком, вы затем учитываете ее ширину, ширину кнопки со стрелкой и вычитаете эти показатели из текущей ширины элемента управления редактированием.

В примере кода это делается при первом создании ComboBox и когда элемент управления получает сообщение WM_WINDOWPOSCHANGED, которое сообщает, что размер элемента управления был изменен. Затем вы меняете положение своей пользовательской кнопки, а также соответствующим образом меняете размер элемента управления редактированием.

Для этого вы можете вызвать SetWindowPos, чтобы установить новый размер элемента управления Edit (обратите внимание, что этот размер не может представлять собой произвольную меру; дочерние элементы управления должны покрывать всю поверхность ComboBox. Его фон фактически не закрашивается). ).

Пользовательская кнопка при нажатии вызывает публичное событие (CustomButtonClicked). Вы можете подписаться на это событие в родительской форме, как обычно. Смотрите события вашего ComboBox.

Пользовательская кнопка удаляется, а подписка на ее событие отменяется, когда ComboBox получает сообщение WM_DESTROY.


Если вы не можете использовать значения NULL, просто удалите ?, как в Button? customButton = null;.

using System.ComponentModel;
using System.Runtime.InteropServices;

public class ComboBoxWithButton : ComboBox {
    Button? customButton = null;
    public event EventHandler<EventArgs>? CustomButtonClicked;

    public ComboBoxWithButton() {
        customButton = new Button() { Text = "?", FlatStyle = FlatStyle.Flat, Parent = this };
        customButton.FlatAppearance.BorderSize = 0;
        customButton.Click += CustomButton_Click;
    }

    [DefaultValue(4)]
    protected internal int EditControlOffset { get; set; } = 4;

    protected override void OnHandleCreated(EventArgs e) {
        base.OnHandleCreated(e);
        UpdateControlsPosition();
    }

    protected override void WndProc(ref Message m) {
        base.WndProc(ref m);
        switch (m.Msg) {
            case WM_WINDOWPOSCHANGED:
                UpdateControlsPosition();
                break;
            case WM_DESTROY:
                if (customButton != null) {
                    customButton.Click -= CustomButton_Click;
                    customButton.Dispose();
                }
                break;
            default:
                break;
        }
    }

    private void UpdateControlsPosition() {
        if (customButton != null) {
            SuspendLayout();
            Rectangle arrowRect = GetComboBoxInfoInternal(Handle, out Rectangle editRect, out IntPtr editHwnd);
            if (arrowRect.IsEmpty || editRect.IsEmpty) return;
            // Same size as the Arrow Button: change as required
            customButton.Size = arrowRect.Size;
            customButton.Location = new Point(arrowRect.Left - customButton.Width - 1, 1);
            editRect.Width = ClientSize.Width - customButton.Width * 2 - EditControlOffset;
            SetWindowPos(editHwnd, IntPtr.Zero, 0, 0, editRect.Width, editRect.Height, SWP_Flags);
            ResumeLayout(false);
        }
    }

    private void CustomButton_Click(object? sender, EventArgs e) => OnEditButtonClicked(e);

    protected virtual void OnEditButtonClicked(EventArgs e) {
        CustomButtonClicked?.Invoke(this, e);
    }

    const int SWP_NOMOVE = 0x0002;
    const int SWP_NOZORDER = 0x0004;
    const int SWP_SHOWWINDOW = 0x0040;
    const int SWP_Flags = SWP_NOMOVE | SWP_NOZORDER;
    const int WM_DESTROY = 0x0002;
    const int WM_WINDOWPOSCHANGED = 0x0047;

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int uFlags);

    [StructLayout(LayoutKind.Sequential)]
    internal struct RECT {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;

        public RECT(int left, int top, int right, int bottom) {
            Left = left; Top = top; Right = right; Bottom = bottom;
        }

        public static RECT FromRectangle(Rectangle rect) => new RECT(rect.X, rect.Y, rect.Right, rect.Bottom);
        public readonly Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct COMBOBOXINFO {
        public int cbSize;
        public RECT rcItem;
        public RECT rcButton;
        public int buttonState;
        public IntPtr hwndCombo;
        public IntPtr hwndEdit;
        public IntPtr hwndList;
        //public COMBOBOXINFO() => cbSize = Marshal.SizeOf<COMBOBOXINFO>();
    }

    internal static Rectangle GetComboBoxInfoInternal(IntPtr cboHandle, out Rectangle editRect, out IntPtr editHandle) {
        var cbInfo = new COMBOBOXINFO() { cbSize = Marshal.SizeOf<COMBOBOXINFO>() };
        GetComboBoxInfo(cboHandle, ref cbInfo);
        editHandle = cbInfo.hwndEdit;
        editRect = cbInfo.rcItem.ToRectangle();
        return cbInfo.rcButton.ToRectangle();
    }
}

Привет, Джими, спасибо, это сработало.

Hack.Sign 29.05.2024 17:08

Привет, Джими, может быть, ты поможешь мне с моим следующим шагом? Я планировал использовать это в datagridview в качестве элемента управления редактированием.

Hack.Sign 04.06.2024 21:07

Другие вопросы по теме