对话框控件与文档视图架构详解
1. 列表框控件与组合框控件
在开发过程中,我们常常会用到列表框和组合框控件。
1.1 列表框控件
有时候,了解当前所选的所有项是很有用的。要实现这一点,需要使用
CListBox
类的功能。
CListBox
包含几个用于获取和更改多选列表框选择的成员函数:
-
GetSelCount()
函数:返回当前所选项目的数量。
-
GetSelItems()
函数:将所选项目的索引填充到一个数组中。
以下是一个
OnOK()
函数示例,用于显示列表框中所选的团队:
void TeamDialog::OnOK(){
CDialog::OnOK();
CListBox *teams = (CListBox *) GetDlgItem(IDC_TEAMS);
if(teams->GetSelCount() > 0) {
int selected[6]; // 列表框中有 6 个字符串
int numSelected = teams->GetSelItems(6, selected);
for(int i = 0; i < numSelected; i++) {
// 从索引获取字符串
CString buffer;
teams->GetText(selected[i], buffer);
MessageBox(buffer, "You picked");
}
}
else {
MessageBox("No teams were selected", "Teams Dialog");
}
}
如果没有选择任何团队,消息框会通知用户;如果选择了一个或多个团队,则会显示每个所选团队的消息框。
CListBox
的
GetText()
成员函数用于从索引获取字符串值,
selected
数组将填充所选团队的索引。
1.2 组合框控件
组合框由一个下拉列表框中显示的字符串列表组成,看起来像一个带有箭头的编辑控件,用于下拉字符串列表。
组合框条目的初始化有两种方法:
-
使用组合框属性窗口
:如果每次显示组合框时都知道其初始内容,可以通过组合框属性窗口将这些字符串添加到资源文件中。该属性窗口有一个“Data”选项卡,可在其中输入字符串。注意,在“Data”窗口中输入项目时,必须按
Ctrl + Enter
移动到下一行,按
Enter
会关闭属性窗口。
-
使用
AddString()
和
InsertString()
成员函数
:可以使用
CComboBox
类的
AddString()
和
InsertString()
成员函数向组合框中添加条目,类似于列表框的操作。以下是一个
OnInitDialog()
函数示例,用于添加组合框的项目:
BOOL MajorsDialog::OnInitDialog() {
CDialog::OnInitDialog();
CComboBox *majors = (CComboBox *) GetDlgItem(IDC_MAJOR);
CString entries[] = {"English", "Mathematics", "History", "Engineering", "Computer Science", "Chemistry"};
for(int i = 0; i < 6; i++)
majors->AddString(entries[i]);
return TRUE;
}
有两个
DDX
函数用于确定组合框中哪个项目被选中,具体取决于你是想要所选项目的索引还是其实际值:
-
DDX_CBIndex()
函数:获取所选索引,并使用相应的
int
成员变量。
-
DDX_CBString()
函数:将所选字符串传输到
CString
成员变量中。
以下是一个
DoDataExchange()
函数示例,将组合框与
m_Major
成员变量关联:
void MajorsDialog::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
DDX_CBString(pDX, IDC_MAJOR, m_Major);
}
如果在数据交换时
IDC_MAJOR
组合框中没有选择任何项目,则
m_Major
对象将包含一个零长度的字符串。以下是一个
OnOK()
消息处理程序示例,用于显示组合框中所选的字符串:
void CComboBoxDlg::OnOK() {
CDialog::OnOK();
if(m_Major.GetLength() > 0)
MessageBox(m_Major, "You selected");
else
MessageBox("You did not make a selection", "Warning");
}
2. 文档视图架构概述
文档视图架构是一种重要的编程模式,它基于将程序中的数据与用户查看数据的方式分离的概念。
2.1 文档与视图的概念
-
文档
:是程序中的持久数据,即用户退出程序后仍需存在的数据。当程序再次执行时,用户可以打开之前保存的任何文档。例如,在 Microsoft Excel 中创建的电子表格保存为
*.xls文件,这就是一个文档。文档的概念并不局限于文字处理和电子表格文档,任何你希望保存的数据都可以是文档。 - 视图 :负责显示和操作文档中的数据。一个文档可以有多个视图,当文档中的数据发生变化时,所有关联的视图都会收到通知并相应地更新自身显示。例如,一个文档表示股票在一天交易中的价格变化,一个视图可以使用条形图显示价格,另一个视图可以用折线图显示,第三个视图以文本形式列出价格。
需要注意的是,一个文档可以有一个或多个视图,但一个视图只能与一个文档关联。
2.2 SDI 与 MDI
文档视图应用程序可以使用单文档界面(SDI)或多文档界面(MDI):
| 界面类型 | 特点 | 示例 |
| ---- | ---- | ---- |
| SDI | 用户一次只能打开一个文档,必须关闭当前文档才能打开另一个不同的文档 | Windows 程序中的记事本和写字板 |
| MDI | 用户可以一次打开多个文档,每个文档显示在自己的子窗口中 | Microsoft Word 和 Excel |
3. 文档视图应用程序的类
开发使用文档视图架构的应用程序涉及以下几个重要的类:
| 类名 | 功能 |
| ---- | ---- |
|
CDocument
| 包含应用程序中的数据,提供打开和保存文档的基本功能 |
|
CView
| 为文档提供视图,负责显示和操作文档数据 |
|
CFrameWnd
| 为视图提供窗口 |
|
CDocTemplate
| 将
CDocument
、
CView
和
CFrameWnd
关联起来,对应特定类型的文档 |
|
CWinApp
| 创建
CDocTemplate
类 |
应用程序对象创建文档模板,模板对象负责创建文档和框架窗口,框架窗口创建与当前文档关联的视图。
4.
CDocument
类详解
文档是通过从
CDocument
类派生一个类来创建的,
CDocument
提供了在存档中存储和检索文档数据以及更新视图的基本功能。
以下是一个表示股票价格和符号的文档类示例:
class CStockPriceDoc : public CDocument {
protected:
DECLARE_DYNCREATE(CStockPriceDoc)
public:
// 成员变量表示文档的数据
CString m_StockSymbol;
double m_CurrentPrice;
CArray<double, double> m_PriceHistory;
// 从 CDocument 重写的成员函数
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
DECLARE_MESSAGE_MAP()
};
DECLARE_DYNCREATE
宏使
CStockPriceDoc
对象能够在运行时动态实例化,它需要与
IMPLEMENT_DYNCREATE
宏一起使用,该宏需要出现在
CStockPriceDoc
的实现文件中:
IMPLEMENT_DYNCREATE(CStockPriceDoc, CDocument)
4.1
OnNewDocument()
函数
当用户从菜单中选择“File/New”时,会调用
OnNewDocument()
函数。当前文档将关闭,允许用户在文档已修改的情况下保存当前文档。在 SDI 应用程序中,选择“File/New”实际上不会创建新文档,而是将当前文档的数据重置为表示一个空(新)文档。
以下是
CStockPriceDoc
类中
OnNewDocument()
函数的示例:
BOOL CStockPriceDoc::OnNewDocument() {
if (!CDocument::OnNewDocument())
return FALSE;
// 清除成员变量
m_StockSymbol.Empty();
m_PriceHistory.RemoveAll();
m_CurrentPrice = 0.0;
return TRUE;
}
4.2 修改文档
如果用户在未保存更改的情况下调用
OnNewDocument()
函数,MFC 会处理确保在数据丢失之前保存文档的细节,只要程序通知框架数据已更改。
-
设置修改标志
:如果文档的数据被用户更改,需要将文档的修改标志设置为
TRUE
。可以使用
SetModifiedFlag()
函数来更改标志:
void SetModifiedFlag(BOOL bModified = TRUE);
在类的任何“set”函数或任何更改数据的其他函数中调用此函数。当文件保存时,框架会自动调用
SetModifiedFlag()
并传入
FALSE
参数。
-
通知视图
:由于一个文档可以有多个视图,当数据发生变化时,需要通知所有视图。调用
SetModifiedFlag()
后,文档对象应调用
UpdateAllViews()
函数:
void UpdateAllViews(CView* pSender, LPARAM lHint = 0L, CObject* pHint = NULL);
pSender
参数指向发起修改的视图,如果为
NULL
,则通知所有视图;如果不为
NULL
,则该视图不会收到通知。
lHint
和
pHint
参数可用于向视图发送有关更改类型的数据。
以下是一个修改股票价格的示例函数:
void CStockPriceDoc::OnNewStockPrice(double newPrice) {
// 更新数据
m_PriceHistory.Add(m_CurrentPrice);
m_CurrentPrice = newPrice;
// 设置修改标志为 true
SetModifiedFlag(TRUE);
// 更新所有视图
UpdateAllViews(NULL);
}
4.3
Serialize()
函数
当当前文档正在保存或现有文档正在打开时,会调用
Serialize()
函数。该函数用于存储和检索文档的数据:
virtual void Serialize(CArchive& ar);
ar
参数是一个存档,通常是用户计算机上的一个文件。框架会初始化
CArchive
对象并将其与从“File/Open”或“File/Save”对话框中选择的文件关联起来。
以下是
CStockPriceDoc
类的
Serialize()
函数示例:
void CStockPriceDoc::Serialize(CArchive& ar) {
if (ar.IsStoring()) {
int size = m_PriceHistory.GetSize();
ar << m_StockSymbol << m_CurrentPrice << size;
for(int i = 0; i < size; i++)
ar << m_PriceHistory[i];
}
else {
int size = 0;
ar >> m_StockSymbol >> m_CurrentPrice >> size;
for(int i = 0; i < size; i++) {
double price;
ar >> price;
m_PriceHistory.Add(price);
}
}
}
综上所述,无论是列表框和组合框控件的使用,还是文档视图架构的应用,都为我们开发功能强大、用户体验良好的程序提供了有力的支持。通过合理运用这些技术,我们可以更高效地实现各种复杂的功能。
5.
CView
类详解
CView
类派生自
CWnd
,它为文档提供基本的查看和修改数据的功能。当文档数据发生变化时,
CView
类会收到通知并更新其显示。
5.1
OnUpdate()
函数
UpdateAllViews()
函数会调用每个视图的
OnUpdate()
成员函数,该函数用于处理文档数据变化的通知。以下是
OnUpdate()
函数的基本声明:
virtual void OnUpdate(CView* pSender, LPARAM lHint = 0L, CObject* pHint = NULL);
在这个函数中,视图可以根据
lHint
和
pHint
参数提供的信息,决定如何更新自身的显示。例如,如果
lHint
表示股票价格发生了变化,视图可以重新绘制图表以反映新的价格。
以下是一个简单的
OnUpdate()
函数示例:
void CStockPriceView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) {
if (lHint == PRICE_CHANGED) {
// 重新绘制视图以反映新的股票价格
Invalidate();
}
CView::OnUpdate(pSender, lHint, pHint);
}
在这个示例中,
PRICE_CHANGED
是一个自定义的常量,用于表示股票价格发生了变化。当收到这个通知时,视图会调用
Invalidate()
函数,请求重绘自身。
5.2 视图的绘制
视图的绘制通常在
OnDraw()
函数中完成。
OnDraw()
函数是一个虚函数,需要在派生类中重写。以下是一个简单的
OnDraw()
函数示例,用于绘制股票价格的文本视图:
void CStockPriceView::OnDraw(CDC* pDC) {
CStockPriceDoc* pDoc = GetDocument();
if (pDoc) {
CString str;
str.Format("Stock Symbol: %s\nCurrent Price: %.2f", pDoc->m_StockSymbol, pDoc->m_CurrentPrice);
pDC->TextOut(10, 10, str);
}
}
在这个示例中,
GetDocument()
函数用于获取与视图关联的文档对象。然后,使用
Format()
函数将股票符号和当前价格格式化为一个字符串,并使用
TextOut()
函数将其绘制到视图上。
6.
CDocTemplate
类详解
CDocTemplate
类用于将
CDocument
、
CView
和
CFrameWnd
关联起来,对应特定类型的文档。它负责创建文档和框架窗口,以及管理文档的打开、关闭和保存等操作。
6.1 创建
CDocTemplate
对象
在应用程序的初始化过程中,通常会创建一个或多个
CDocTemplate
对象。以下是一个创建
CDocTemplate
对象的示例:
BOOL CMyApp::InitInstance() {
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CStockPriceDoc),
RUNTIME_CLASS(CMainFrame), // 主框架窗口类
RUNTIME_CLASS(CStockPriceView)); // 视图类
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 其他初始化代码...
return TRUE;
}
在这个示例中,使用
CSingleDocTemplate
类创建了一个单文档模板对象。
IDR_MAINFRAME
是资源 ID,
RUNTIME_CLASS()
宏用于获取类的运行时信息。
6.2
CDocTemplate
的工作流程
CDocTemplate
的工作流程可以用以下 mermaid 流程图表示:
graph LR
A[应用程序启动] --> B[创建 CDocTemplate 对象]
B --> C[用户打开文档]
C --> D[CDocTemplate 创建文档对象]
D --> E[CDocTemplate 创建框架窗口]
E --> F[框架窗口创建视图对象]
F --> G[视图关联到文档]
当用户打开一个文档时,
CDocTemplate
会创建一个文档对象,然后创建一个框架窗口,最后框架窗口会创建一个或多个视图对象,并将视图与文档关联起来。
7. 文档视图架构的优势与应用场景
文档视图架构具有以下几个显著的优势:
-
数据与视图分离
:将程序中的数据与用户界面分离,使得代码更加易于维护和扩展。当数据模型发生变化时,只需要更新视图的显示逻辑,而不需要修改数据处理代码。
-
多视图支持
:一个文档可以有多个不同的视图,用户可以根据需要选择不同的方式查看数据。例如,在股票交易系统中,用户可以同时查看股票价格的折线图和表格视图。
-
内置的保存和恢复功能
:MFC 提供了内置的保存和恢复文档数据的功能,使得开发人员不需要手动实现这些功能,大大提高了开发效率。
文档视图架构适用于以下应用场景:
-
数据处理应用程序
:如电子表格软件、数据库管理系统等,这些应用程序需要处理大量的数据,并提供多种方式查看和编辑数据。
-
图形处理应用程序
:如绘图软件、图像处理软件等,这些应用程序需要提供不同的视图来显示和编辑图形数据。
总结
通过对列表框和组合框控件以及文档视图架构的详细介绍,我们了解了如何使用这些技术来开发功能强大的 Windows 应用程序。列表框和组合框控件可以方便地实现用户选择和输入的功能,而文档视图架构则提供了一种有效的方式来管理和显示程序中的数据。
在实际开发中,我们可以根据具体的需求选择合适的控件和架构。同时,合理运用 MFC 提供的类和函数,可以提高开发效率,减少代码量。希望本文对大家在 Windows 应用程序开发方面有所帮助。
超级会员免费看
14

被折叠的 条评论
为什么被折叠?



