У меня есть диалоговое окно "по умолчанию", подобное следующему: введите описание изображения здесь

И я пытаюсь изменить вкладки и вставить RichEditCtrl в первую вкладку.

    InitCommonControlsEx;
    CWnd* pTab = GetDlgItem(IDC_TAB1);
    if (pTab) {
        CRect rect;

        m_TabCtrl = (CTabCtrl*)pTab;
        m_TabCtrl->GetClientRect(&rect);
        
        m_TabCtrl->InsertItem(0, "Stats");
        m_TabCtrl->InsertItem(1, "Settings");
        BOOL getRect = m_TabCtrl->GetItemRect(0, &rect);

        if (!m_richEditCtrl.Create(WS_VISIBLE | ES_READONLY | ES_MULTILINE | ES_AUTOHSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | WS_VSCROLL, rect, m_TabCtrl, 0))
            return FALSE;

        m_font.CreateFont(-11, 0, 0, 0, FW_REGULAR, 0, 0, 0, BALTIC_CHARSET, 0, 0, 0, 0, "Courier New");
        m_richEditCtrl.SetFont(&m_font);
    }

Образец, который я изменяю ранее, использовал только RichTextCtrl и «создал» его внутри текстового поля «заполнитель». Это сработало отлично, но я хотел вставить RichTextCtrl во вкладку и создать другую вкладку для отображения некоторых данных. Проблема в том, что теперь у меня просто две пустые вкладки. Я знаю, что настройки родительского диалогового окна «Обрезать дочерние элементы» и «Обрезать братьев и сестер» могут иметь значение, но я не уверен, что, если мне нужно, если то и другое. Я также знаю, что мой RichEditCtrl все еще существует, потому что я все еще отправляю ему данные, но он определенно не отображается.

Эта часть моей программы даже не такая уж срочная, и я просто пытаюсь заставить ее работать по принципу на данный момент ...

0
Skewjo 7 Дек 2020 в 01:30

1 ответ

Лучший ответ

Tab Controls создают иллюзию, что разделители и область отображения была частью того же элемента управления. Это не так. Элемент управления вкладкой на самом деле представляет собой просто метки плюс область отображения заполнителя. Реализация содержимого области отображения является обязанностью приложения.

Как правило, для реализации полнофункционального элемента управления вкладками требуются следующие шаги:

  1. Создайте элемент управления вкладкой и метки.
  2. Создайте дочерние элементы управления области отображения. Обычно элементы управления, составляющие одну «страницу», размещаются в диалоговом окне.
  3. Подпишитесь на сообщение TCN_SELCHANGE и динамически обновляйте видимость элементов управления, т.е. скрыть все элементы управления, которые не являются частью текущей «страницы», и показать все элементы управления, которые есть. Размещение всех элементов управления для «страницы» внутри диалогового окна упрощает эту задачу, требуя только переключения видимости диалоговых окон.

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

  1. Элемент управления вкладкой, на который ссылается IDC_TAB1, должен иметь стиль WS_CLIPCHILDREN, чтобы область отображения не закрывала дочерние элементы управления.
  2. m_richEditCtrl необходимо создать со стилем WS_CHILD.
  3. Рассчитайте размер области отображения с помощью CTabCtrl :: AdjustRect и используйте это для изменения размера m_richEditCtrl, чтобы заполнить всю область отображения (если это то, что вы хотите).

С этими изменениями вы должны увидеть элемент управления вкладкой, область отображения которого заполнена элементом управления Rich Edit. Переключение между вкладками пока не меняет содержимое области отображения. Это то, что вам нужно реализовать в соответствии с требованиями вашего приложения.


Следующий пример кода основан на создаваемом мастером диалоговом приложении с именем MfcTabCtrl . Из сгенерированного ресурса диалогового окна (IDD_MFCTABCTRL_DIALOG) было удалено все содержимое, оставив только пустой шаблон диалогового окна.

Точно так же основная реализация диалогового окна лишилась большей части своей функциональности, оставив только жизненно важные части. Это заголовок MfcTabCtrlDlg.h :

#pragma once

#include "afxdialogex.h"

// Control identifiers
UINT constexpr IDC_TAB{ 100 };
UINT constexpr IDC_RICH_EDIT{ 101 };

class CMfcTabCtrlDlg : public CDialogEx
{
public:
    CMfcTabCtrlDlg(CWnd* pParent = nullptr);

protected:
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnTabChanged(NMHDR* pNMHDR, LRESULT* pResult);

    // Convenience implementation to calculate the display area
    RECT GetDisplayArea();

    virtual BOOL OnInitDialog();
    DECLARE_MESSAGE_MAP()

private:
    CTabCtrl m_TabCtrl{};
    CRichEditCtrl m_richEditCtrl{};
};

Файл реализации MfcTabCtrlDlg.cpp тоже не очень обширен:

#include "MfcTabCtrlDlg.h"

CMfcTabCtrlDlg::CMfcTabCtrlDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_MFCTABCTRL_DIALOG, pParent)
{
}

void CMfcTabCtrlDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);

    // Resize tab control only after it has been created
    if (IsWindow(m_TabCtrl)) {
        m_TabCtrl.MoveWindow(0, 0, cx, cy);
        // Determine display area
        auto const disp_area{GetDisplayArea()};
        // Resize child control(s) to cover entire display area
        if (!IsRectEmpty(&disp_area) && IsWindow(m_richEditCtrl)) {
            m_richEditCtrl.MoveWindow(&disp_area);
        }
    };
}

void CMfcTabCtrlDlg::OnTabChanged(NMHDR* /*pNMHDR*/, LRESULT* pResult)
{
    auto const cur_sel{ m_TabCtrl.GetCurSel() };
    switch (cur_sel) {
    // First tab selected
    case 0:
        m_richEditCtrl.ShowWindow(SW_SHOW);
        break;
    // Second tab selected
    case 1:
        m_richEditCtrl.ShowWindow(SW_HIDE);
        break;
    }

    // Allow other subscribers to handle this message
    *pResult = FALSE;
}

// Returns the display area in client coordinates relative to the dialog.
// Returns an empty rectangle on failure.
RECT CMfcTabCtrlDlg::GetDisplayArea()
{
    RECT disp_area{};

    if (IsWindow(m_TabCtrl)) {
        m_TabCtrl.GetWindowRect(&disp_area);
        m_TabCtrl.AdjustRect(FALSE, &disp_area);
        this->ScreenToClient(&disp_area);
    }
    
    return disp_area;
}

// The message map registers only required messages
BEGIN_MESSAGE_MAP(CMfcTabCtrlDlg, CDialogEx)
    ON_WM_SIZE()
    ON_NOTIFY(TCN_SELCHANGE, IDC_TAB, &CMfcTabCtrlDlg::OnTabChanged)
END_MESSAGE_MAP()


BOOL CMfcTabCtrlDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Set up tab control to cover entire client area
    RECT client{};
    GetClientRect(&client);
    m_TabCtrl.Create(WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN, client, this, IDC_TAB);
    m_TabCtrl.InsertItem(0, L"Stats");
    m_TabCtrl.InsertItem(1, L"Settings");

    // Set up rich edit control.
    // The WS_BORDER style is set strictly to make it visible.
    auto const disp_area{ GetDisplayArea() };
    m_richEditCtrl.Create(WS_BORDER | WS_VISIBLE | WS_CHILD,
                          disp_area, &m_TabCtrl, IDC_RICH_EDIT);

    return TRUE;  // Let the system manage focus for this dialog
}

Результатом является диалоговое окно, содержащее вкладку с двумя метками. Видимость содержащегося в нем элемента управления Rich Edit переключается в обработчике уведомлений TCN_SELCHANGE, показывая его только при выборе первой вкладки. Более сложный графический интерфейс обновит видимость всех элементов управления на основе выбранной в данный момент вкладки.

Обратите внимание, что элементы управления внутри области отображения вкладок никогда не уничтожаются в течение времени существования диалогового окна. Обычно это желательно для сохранения пользовательских данных даже при переключении между вкладками. При необходимости также можно уничтожить и (воссоздать) некоторые или все дочерние элементы управления при переключении вкладок.

2
IInspectable 8 Дек 2020 в 10:11