Ситуация довольно простая, но я ее не совсем хорошо понимаю.
У меня есть приложение WinMFC, которое также является названием проекта решения. Я хочу передать данные из главного диалогового окна приложения (WinMFCDialog.cpp + WinMFCDialog.h) в немодальный настраиваемый диалог (CustomDialog.cpp + CustomDialog.h).
Немодальность означает, что CustomDialog функционирует как отдельное окно, не перехватывая фокус на самом себе, поэтому я могу взаимодействовать с WinMFCDialog отдельно.
Вот коды:
#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();
};
#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);
}
#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()
};
#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()
#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;
#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:
Данные должны отображаться после в CustomDialog в элементе статического поля:
Итак, я попытался использовать функции DDX/DDV для передачи данных из текста редактирования диалогового окна WinMFC в статическое текстовое поле пользовательского диалогового окна.
Вот что я пытался сделать:
void DDX_TextNotEmpty (CDataExchange* pDX, int nIDC, CString& value);
class CWinMFCApp : public CWinApp
{
// Class definition
}
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); }
}
class CustomDialog : public CDialog
{
DECLARE_DYNAMIC(CustomDialog)
public:
CString m_string;
CustomDialog(CWnd* pParent = NULL);
// ... Default stuff created by VS2010 ...
}
CustomDialog::CustomDialog(CWnd* pParent /*=NULL*/)
: CDialog(CustomDialog::IDD, pParent), m_string(_T("default value"))
{
}
void CustomDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_TextNotEmpty(pDX, IDC_EDIT_DATA, m_string);
}
В итоге я получил ошибку:
Ну, я не знаю, что на самом деле я сделал не так, поэтому надеюсь, что мой вопрос не слишком глупый и не надоедливый, я просто хочу научиться пользоваться этой старой, но интересной штукой 😔.
Буду рад любой помощи.
Также: вы пытались нажать кнопку «Отладка» в диалоговом окне подтверждения? Он должен прерваться отладчиком в той точке, которая нарушила утверждение.
Вы можете передать произвольные данные в диалог, предоставив собственный конструктор, получающий эти данные (или предоставив члены класса для заполнения данных после создания экземпляра класса диалога). Вы можете диагностировать конкретную ошибку, которую вы получаете, нажав «Отладка». Это приведет вас к утверждению отладки в коде, который потерпел неудачу.
Нажмите «Отладка» (что бы это ни было на вашем языке). Он покажет исходную строку MFC, в которой возникает проблема. Это поможет вам (и нам) выяснить, в чем проблема. Также посмотрите трассировку стека, которая поможет вам определить, какая строка исходного кода вызвала проблему. Отредактируйте вопрос и предоставьте эту информацию.
Я решил огромную часть проблемы 😊. Теперь мои данные переходят, но не так, как я хотел. Я понял, что проблема в том, что я вызывал DDX_TextNotEmpty(pDX, IDC_EDIT_DATA, m_string) в диалоге, который еще не был создан! Я обнаружил это в режиме DEBUG. Но результат всё равно не тот, что я хотела 😣. Я хочу получить своего рода автоматическое обновление: текст автоматически обновлялся в модальном диалоговом окне, когда я вводил текст в поле редактирования в главном диалоговом окне. Пока я могу обновить его только с помощью кнопки в главном диалоге: m_ptrDialog->SetDlgItemTextW(IDC_STATIC, m_string);
Самый простой способ передать данные в диалоговое окно 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, не имеет ничего общего с вашим сообщением, просто пример. Предполагается, что приложение сохранит «Параметры приложения» в структуре m_Options
. Один, если его членами является «Каталог данных» (т. е. папка, в которой расположены файлы данных), хранящийся в члене sDataDir
(это «путь»). Пример просто демонстрирует, как такие данные могут передаваться в диалог и из него. Что касается предлагаемого вами решения, вам лучше реализовать DDV_TextNotEmpty()
(т. е. только для проверки, вызываемый после вызова DDX_Text()
). Просто проверьте код DDV_MaxChars()
и измените его, чтобы вместо этого проверять пустой текст.
... Ладно, вот что я понял...
Проблема заключалась в том, что я вызывал 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:
До сих пор не совсем понимаю, что может быть наиболее подходящим в моем случае, во всяком случае, это работает.
Вот что я добавил в 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)
, и если все проверки пройдены, диалоговое окно закрывается.
Примечание: почему вы используете такую древнюю версию VS (вы отметили vs-2010)?