Как передать данные в немодальное диалоговое окно в настольном приложении Windows (MFC, C++)?

Ситуация довольно простая, но я ее не совсем хорошо понимаю.

У меня есть приложение WinMFC, которое также является названием проекта решения. Я хочу передать данные из главного диалогового окна приложения (WinMFCDialog.cpp + WinMFCDialog.h) в немодальный настраиваемый диалог (CustomDialog.cpp + CustomDialog.h).

Немодальность означает, что CustomDialog функционирует как отдельное окно, не перехватывая фокус на самом себе, поэтому я могу взаимодействовать с WinMFCDialog отдельно.

Вот коды:

WinMFCDlg.h
#pragma once

class CWinMFCDlg : public CDialogEx
{
public:
    CWinMFCDlg(CWnd* pParent = NULL);   

    enum { IDD = IDD_WINMFC_DIALOG };

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    


protected:
    HICON m_hIcon;

    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnBnClickedButton1();
};
WinMFCDlg.cpp
#include "stdafx.h"
#include "WinMFC.h"
#include "WinMFCDlg.h"
#include "afxdialogex.h"
#include "CustomDialog.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg();

    enum { IDD = IDD_ABOUTBOX };

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    

protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()

CWinMFCDlg::CWinMFCDlg(CWnd* pParent /*=NULL*/): 
    CDialogEx(CWinMFCDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CWinMFCDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CWinMFCDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON1, &CWinMFCDlg::OnBnClickedButton1)
END_MESSAGE_MAP()

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

    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        BOOL bNameValid;
        CString strAboutMenu;
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
        ASSERT(bNameValid);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }
    SetIcon(m_hIcon, TRUE);         
    SetIcon(m_hIcon, FALSE);        

    return TRUE;
}

void CWinMFCDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialogEx::OnSysCommand(nID, lParam);
    }
}

void CWinMFCDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this);

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Нарисуйте значок
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialogEx::OnPaint();
    }
}

HCURSOR CWinMFCDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}


void CWinMFCDlg::OnBnClickedButton1()
{
    // Modeless dialog, in which I want to pass the data
    CustomDialog *pDlg = new CustomDialog();
    pDlg->Create(IDD_CUSTOM_DIALOG, this);
    pDlg->ShowWindow(SW_SHOW);
}
CustomDialog.h
#pragma once

class CustomDialog : public CDialog
{
    DECLARE_DYNAMIC(CustomDialog)

public:
    CustomDialog(CWnd* pParent = NULL);
    virtual ~CustomDialog();

    enum { IDD = IDD_CUSTOM_DIALOG };

protected:
    virtual void DoDataExchange(CDataExchange* pDX);

    DECLARE_MESSAGE_MAP()
};
CustomDialog.cpp
#include "stdafx.h"
#include "WinMFC.h"
#include "CustomDialog.h"
#include "afxdialogex.h"

IMPLEMENT_DYNAMIC(CustomDialog, CDialog)

CustomDialog::CustomDialog(CWnd* pParent /*=NULL*/)
    : CDialog(CustomDialog::IDD, pParent)
{

}

CustomDialog::~CustomDialog()
{
}

void CustomDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CustomDialog, CDialog)
END_MESSAGE_MAP()
WinMFC.h (основное приложение, на всякий случай)
#pragma once

#ifndef __AFXWIN_H__
    #error "включить stdafx.h до включения этого файла в PCH"
#endif

#include "resource.h"

class CWinMFCApp : public CWinApp
{
public:
    CWinMFCApp();

public:
    virtual BOOL InitInstance();
    DECLARE_MESSAGE_MAP()
};

extern CWinMFCApp theApp;
WinMFC.cpp (основное приложение, на всякий случай)
#include "stdafx.h"
#include "WinMFC.h"
#include "WinMFCDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CWinMFCApp
BEGIN_MESSAGE_MAP(CWinMFCApp, CWinApp)
    ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()

// Craetion of CWinMFCApp
CWinMFCApp::CWinMFCApp()
{
    m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
}

// Single object CWinMFCApp
CWinMFCApp theApp;

// Initialization of CWinMFCApp
BOOL CWinMFCApp::InitInstance()
{
    INITCOMMONCONTROLSEX InitCtrls;
    InitCtrls.dwSize = sizeof(InitCtrls);

    InitCtrls.dwICC = ICC_WIN95_CLASSES;
    InitCommonControlsEx(&InitCtrls);

    CWinApp::InitInstance();
    AfxEnableControlContainer();
    CShellManager *pShellManager = new CShellManager;
    SetRegistryKey(_T("Test MFC APP"));

    CWinMFCDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
    }
    else if (nResponse == IDCANCEL)
    {
    }

    if (pShellManager != NULL)
    {
        delete pShellManager;
    }

    return FALSE;
}

Я ввожу данные в поле редактирования, которое создается в WinMFCDlg:

Как передать данные в немодальное диалоговое окно в настольном приложении Windows (MFC, C++)?

Данные должны отображаться после в CustomDialog в элементе статического поля:

Как передать данные в немодальное диалоговое окно в настольном приложении Windows (MFC, C++)?

Итак, я попытался использовать функции DDX/DDV для передачи данных из текста редактирования диалогового окна WinMFC в статическое текстовое поле пользовательского диалогового окна.

Вот что я пытался сделать:

  1. Я добавил подпись функции ниже в WinMFCh (не в классе, поэтому это не метод)
void DDX_TextNotEmpty (CDataExchange* pDX, int nIDC, CString& value);
class CWinMFCApp : public CWinApp
{
// Class definition
}
  1. Я написал реализацию этого в WinMFC.cpp.
void DDX_TextNotEmpty (CDataExchange* pDX, int nIDC, CString& value)
{
    HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
    if (pDX->m_bSaveAndValidate)
    {
        int nLen = ::GetWindowTextLength(hWndCtrl);
        ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
        value.ReleaseBuffer();
        if (value.IsEmpty ())
        {
            AfxMessageBox (_T("Enter something in the text field."), MB_ICONSTOP);
            pDX->Fail ();   //Fail () sets the focus on to the edit control
        }
    }
    else { ::SetWindowText(hWndCtrl, value); }
}
  1. В CustomDialog.h я добавил публичный член, который должен хранить полученные данные:
class CustomDialog : public CDialog
{
    DECLARE_DYNAMIC(CustomDialog)

public:
    CString m_string;
    CustomDialog(CWnd* pParent = NULL);

// ... Default stuff created by VS2010 ...
}
  1. После того, как я изменил конструктор по умолчанию, чтобы установить значение члена по умолчанию в CustomDialog.cpp
CustomDialog::CustomDialog(CWnd* pParent /*=NULL*/)
    : CDialog(CustomDialog::IDD, pParent), m_string(_T("default value"))
{

}
  1. Я после попытки вызвать эту функцию в CustomDialog.cpp
void CustomDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_TextNotEmpty(pDX, IDC_EDIT_DATA, m_string);
}

В итоге я получил ошибку:

Как передать данные в немодальное диалоговое окно в настольном приложении Windows (MFC, C++)?

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

Буду рад любой помощи.

Примечание: почему вы используете такую ​​древнюю версию VS (вы отметили vs-2010)?

wohlstad 23.04.2024 11:51

Также: вы пытались нажать кнопку «Отладка» в диалоговом окне подтверждения? Он должен прерваться отладчиком в той точке, которая нарушила утверждение.

wohlstad 23.04.2024 11:52

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

IInspectable 23.04.2024 11:52

Нажмите «Отладка» (что бы это ни было на вашем языке). Он покажет исходную строку MFC, в которой возникает проблема. Это поможет вам (и нам) выяснить, в чем проблема. Также посмотрите трассировку стека, которая поможет вам определить, какая строка исходного кода вызвала проблему. Отредактируйте вопрос и предоставьте эту информацию.

Jabberwocky 23.04.2024 12:20

Я решил огромную часть проблемы 😊. Теперь мои данные переходят, но не так, как я хотел. Я понял, что проблема в том, что я вызывал DDX_TextNotEmpty(pDX, IDC_EDIT_DATA, m_string) в диалоге, который еще не был создан! Я обнаружил это в режиме DEBUG. Но результат всё равно не тот, что я хотела 😣. Я хочу получить своего рода автоматическое обновление: текст автоматически обновлялся в модальном диалоговом окне, когда я вводил текст в поле редактирования в главном диалоговом окне. Пока я могу обновить его только с помощью кнопки в главном диалоге: m_ptrDialog->SetDlgItemTextW(IDC_STATIC, m_string);

DOJ 23.04.2024 13:10
Стоит ли изучать 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
5
91
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Самый простой способ передать данные в диалоговое окно MFC — это члены данных, установленные после вызова конструктора и, конечно же, перед вызовом Create()или DoModal(). Вы можете выбрать передачу копий ваших данных или ссылок (например, указателей) на данные, поддерживаемые родительским диалогом, документом или приложением. При закрытии (после вызова OnOK() или OnCancel()) вы можете скопировать данные в переменных-членах обратно в родительский элемент (до вызова деструктора). Именно так это делается в большинстве примеров DDX/DDV. Например:

COptionsDialog dlg;                     // Constructor
dlg.m_DataDir = m_Options.sDataDir;     // Pass Data to the dialog class
if (dlg.DoModal()==IDOK)                // Display the dialog
{
    m_Options.sDataDir = dlg.m_DataDir; // Copy modified data back to the parent
}

Проблема, с которой вы столкнулись, возможно, связана с вызовом PrepareEditCtrl() для получения нередактируемого элемента управления (статического текста). Вместо этого попробуйте PrepareCtrl(). Или замените вызов DDX_TextNotEmpty() на простой вызов DDX_Text() без проверки (DDV_()); как система может «проверять» вводимые данные от элемента управления, который не допускает ввода данных пользователем?

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

Я не совсем понимаю, что вы подразумеваете под m_Options.sDataDir? Что на самом деле должно быть?

DOJ 23.04.2024 13:53

@DOJ, не имеет ничего общего с вашим сообщением, просто пример. Предполагается, что приложение сохранит «Параметры приложения» в структуре m_Options. Один, если его членами является «Каталог данных» (т. е. папка, в которой расположены файлы данных), хранящийся в члене sDataDir (это «путь»). Пример просто демонстрирует, как такие данные могут передаваться в диалог и из него. Что касается предлагаемого вами решения, вам лучше реализовать DDV_TextNotEmpty() (т. е. только для проверки, вызываемый после вызова DDX_Text()). Просто проверьте код DDV_MaxChars() и измените его, чтобы вместо этого проверять пустой текст.

Constantine Georgiou 23.04.2024 15:02
Ответ принят как подходящий

... Ладно, вот что я понял...

Проблема заключалась в том, что я вызывал DDX_TextNotEmpty(pDX, IDC_EDIT_DATA, m_string) в диалоге, который еще не был создан (в моем случае в CustomDialog). К тому же я тоже неправильно его использовал 😄. Эта функция похожа на привязку при разработке под Android. Под этим я подразумеваю, что он обновляет переменную (m_string), когда она изменяется в пользовательском интерфейсе. Но благодаря Константину Георгиу есть встроенный DDX_Text, поэтому нет необходимости использовать эту функцию в WinMFCDlg.cpp. Итак, теперь это выглядит так:

void CWinMFCDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialogEx::DoDataExchange(pDX);
 DDX_Text(pDX, IDC_EDIT_DATA, m_string);
}

Итак, чтобы передать данные в модальное окно, мне нужно поместить свою строку внутри статического текстового элемента в моем модальном диалоговом окне, что я делаю, нажимая кнопку в WinMFCDlg, а также обновляя ее из пользовательского интерфейса. Чтобы сохранить только один модальный диалог вместо того, чтобы открывать его несколько раз, я добавил CustomDialog в качестве поля в классе WinMFCDlg:

class CWinMFCDlg : public CDialogEx
{
   public:
    CustomDialog* m_ptrDialog;
    // Don't forget to delete it
    virtual ~CWinMFCDlg(){ delete m_ptrDialog; }
   // Other stuff...
}

После того, как я инициализирую указатель в BOOL CWinMFCDlg::OnInitDialog() в WinMFCDlg.cpp следующим образом:

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

  // ...default initialization...

  m_ptrDialog = new CustomDialog();
  m_ptrDialog->Create(IDD_CUSTOM_DIALOG, this);
  return TRUE;
}

И, наконец, чтобы поместить свои данные в модальное диалоговое окно, я просто нажимаю кнопку в WinMFCDlg:

void CWinMFCDlg::OnBnClickedButton1()
{
   UpdateData(TRUE);
   // Modeless dialog
   m_ptrDialog->SetDlgItemTextW(IDC_STATIC, m_string);
   m_ptrDialog->ShowWindow(SW_SHOW);
}

... и посмотрите результат: вот он

Чего я и хотел! Но не совсем так, как я хотел... Что ж, если я пойму это «автоматическое обновление, которое я хочу получить», я добавлю решение здесь 😊.

😁 Я ПРИНЯЛА ЭТО! 😁
Ключ находился в событии, которое EditControl может обработать. Их два: EN_CHANGE и EN_UPDATE:

  • EN_CHANGE - Отображение обновляется после изменения текста;
  • EN_UPDATE — элемент управления редактированием собирается отобразить измененный текст.

До сих пор не совсем понимаю, что может быть наиболее подходящим в моем случае, во всяком случае, это работает.

Вот что я добавил в WinMFCDlg.cpp:

void CWinMFCDlg::OnEnChangeEditData()
{
    // TODO:  If this is a RICHEDIT control, the control will not
    // send this notification unless you override the CDialogEx::OnInitDialog()
    // function and call CRichEditCtrl().SetEventMask()
    // with the ENM_CHANGE flag ORed into the mask.

    // TODO:  Add your control notification handler code here
    UpdateData(TRUE);
    m_ptrDialog->SetDlgItemTextW(IDC_STATIC, m_string);
}

Не предполагайте, что механизм привязки будет таким же, как в Android или .NET; здесь нет «автоматических» обновлений. Вместо этого MFC предоставляет метод UpdateData(), который обновляет переменные-члены или элементы управления. UpdateData(FALSE) означает переменные->элементы управления, а UpdateData(TRUE) означает элементы управления->переменные и включает «проверку». Реализация OnOK() по умолчанию вызывает UpdateData(TRUE), и если все проверки пройдены, диалоговое окно закрывается.

Constantine Georgiou 23.04.2024 15:01

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