4、MFC:菜单栏、工具栏与状态栏

菜单栏、工具栏与状态栏

  • 1、菜单栏
    • 1.1 简介
    • 1.2 创建
      • 属性设置
      • 菜单消息
      • 成员函数
    • 1.3 实例
  • 2、工具栏
    • 2.1 简介
    • 工具栏属性
    • 2.2 创建
      • 消息
      • CToolBar类的主要成员函数
    • 2.3 实例
  • 3、状态栏
    • 3.1 简介
    • 3.2 创建
      • CStatusBar类
      • 状态栏创建
    • 3.3 实例

1、菜单栏

1.1 简介

菜单在界面设计中是经常使用的一种元素,包括Windows系统中的窗口、智能终端设备的应用界面等都会经常见到菜单的身影。我们在对可视化窗口操作时,菜单确实提供了很大方便。
菜单分为:

  1. 下拉式菜单:下拉式菜单一般在窗口标题栏下面显示,下拉式菜单通常是由主菜单栏、子菜单及子菜单中的菜单项和分隔条所组成的。
  2. 弹出式菜单:弹出式菜单一般可以通过单击鼠标右键等操作显示。它的主菜单不可见,只显示子菜单。

1.2 创建

属性设置

在这里插入图片描述

  1. 菜单项中含有“…”则表示点击后会弹出对话框。
  2. 热键:菜单项的标题文本中都有一个字母带下划线,带下划线的字母为热键。中文则为括号中的字母。

主菜单栏上的“File”中字母“F”带下划线,F就是热键,程序运行并显示窗口时,在键盘上点击Alt+F就等同于直接点菜单项File,弹出File下的子菜单后,点击“Open”的热键O就可以实现与直接点菜单项Open相同的功能。
热键的定义:可在属性中直接设置。Caption为“&File”,很明显,只要在要定义为热键的字母前加&就可以了。
在这里插入图片描述

  1. 快捷键:用这些组合键就能实现与相应菜单项一样的功能。
    定义:“Open”菜单项的Caption属性,为“&Open…\tCtrl+O”,这里的\t表示在显示前面的文本后跳格再显示快捷键Ctrl+O,但这样设置其Caption属性只是能显示出快捷键,要实现快捷键的功能还需要在Accelerator资源中设定。
    在这里插入图片描述
  • Accelerator中有四列,分别为:ID、Modifier、Key和Type。ID就是菜单项的ID,Modifer和Key就代表了组合键。例如,Open菜单项的ID为ID_FILE_OPEN,Modifer为“Ctrl”,Key为“O”。
  1. 创建:右键插入新列、分隔符、菜单项等;也可以直接输入。
    需要为菜单项设置ID、热键和快捷键(Accelerator)。
    在这里插入图片描述

菜单消息

菜单主要能发送两种消息:COMMAND消息和UPDATE_COMMAND_UI消息。

  1. COMMAND消息:在菜单项被点击时发送该消息。
  2. UPDATE_COMMAND_UI消息:用来维护菜单项的各项状态,包括激活、禁用、变灰、选中、未选中等。在下拉菜单每次打开的时候,所有菜单项的此消息都会被发送出去。如果所属类中为菜单项的该消息添加了处理函数,则执行相应函数更新菜单状态,如果菜单项没有此消息处理函数,也没有COMMAND消息的处理函数,那么它就会变灰。

成员函数

MFC为菜单的操作提供了CMenu类。

  1. BOOL LoadMenu(UINT nIDResource);
    加载菜单资源,并将其附加到CMenu对象上。参数nIDResource指定了要加载的菜单资源的ID。如果菜单加载成功则返回TRUE,否则返回FALSE。
  2. BOOL DeleteMenu(UINT nPosition,UINT nFlags);
    在菜单中删除一个菜单项。参数nPosition指定要删除的菜单项。参数nFlags就用来解释nPosition的意义,为MF_BYCOMMAND时说明nPosition表示菜单项的ID,为MF_BYPOSITION时说明nPosition表示菜单项的位置,第一个菜单项的位置为0。如果删除菜单项成功则返回TRUE,否则返回FALSE。
  3. BOOL TrackPopupMenu(UINT nFlags,int x,int y,CWnd* pWnd,LPCRECT lpRect = 0);
    用来在指定位置显示一个浮动的弹出式菜单。参数nFlags指定屏幕坐标和鼠标位置的标志,可以是以下取值:
    TPM_CENTERALIGN:菜单在水平方向上相对于参数x指定的坐标值居中显示
    TPM_LEFTALIGN:菜单的左侧与参数x指定的坐标值对齐
    TPM_RIGHTALIGN:菜单的右侧与参数x指定的坐标值对齐
    TPM_BOTTOMALIGN:菜单的底部与参数y指定的坐标值对齐
    TPM_TOPALIGN:菜单项的顶部与参数y指定的坐标值对齐
    TPM_VCENTERALIGN:菜单在垂直方向上相对于参数y指定的坐标值居中显示
    参数x指定弹出式菜单的水平方向的屏幕坐标,参数y指定菜单顶部垂直方向上的屏幕坐标,参数pWnd指明哪个窗口拥有此弹出式菜单,不能为NULL,参数lpRect忽略。
  4. UINT CheckMenuItem(UINT nIDCheckItem,UINT nCheck);
    在弹出菜单中为菜单项增加选中标记或移除选中标记。参数nIDCheckItem指定要选中或取消选中的菜单项。参数nCheck指定菜单项的选中状态和如何根据nIDCheckItem确定菜单项的位置,可以是MF_CHECKED或MF_UNCHECKED与MF_BYPOSITION或MF_BYCOMMAND的组合,这几个标志的含义如下:
    MF_BYCOMMAND:为默认值。说明参数nIDCheckItem表示菜单项的ID
    MF_BYPOSITION:说明参数nIDCheckItem表示菜单项的位置,第一个菜单项的位置是0
    MF_CHECKED:为菜单项添加选中标记
    MF_UNCHECKED:为菜单项移除选中标记
    该函数返回菜单项之前的状态:MF_CHECKED或MF_UNCHECKED, 如果菜单项不存在则返回0xFFFFFFFF。
  5. UINT EnableMenuItem(UINT nIDEnableItem,UINT nEnable);
    激活、禁用菜单项或使其变灰。参数nIDEnableItem指定要激活、禁用或变灰的菜单项。参数nEnable指定操作的类型,可以是MF_DISABLED、MF_ENABLED或MF_GRAYED与MF_BYCOMMAND或MF_BYPOSITION的组合,这些值的含义如下:
    MF_BYCOMMAND:同CheckMenuItem
    MF_BYPOSITION:同CheckMenuItem
    MF_DISABLED:禁用菜单项,使其不能被选择但不变灰
    MF_ENABLED:激活菜单项,使其能够被选择并由变灰状态恢复
    MF_GRAYED:禁用菜单项,使其不能被选择并变灰
    该函数返回菜单项之前的状态:MF_DISABLED、MF_ENABLED或MF_GRAYED
  6. CMenu* GetSubMenu(int nPos) const;
    获取弹出菜单的CMenu对象。参数nPos指定弹出菜单在菜单中的位置,不能使用ID。返回值是CMenu对象的指针,该CMenu对象的m_hMenu成员为由nPos指定的弹出菜单的句柄,如果不存在这样的CMenu对象则返回NULL,然后创建一个临时弹出菜单。

1.3 实例

  • 下拉式菜单实现
  1. 创建菜单项:设置ID,名称,热键,快捷键等;
  2. 创建消息相应函数(消息响应函数应该创建在CMainFrame类中);
ON_COMMAND(IDM_TOOLSDRAW, &CMainFrame::OnToolsdraw)
ON_UPDATE_COMMAND_UI(IDM_TOOLSDRAW, &CMainFrame::OnUpdateToolsdraw)void CMainFrame::OnToolsdraw()
{// TODO: 在此添加命令处理程序代码
}void CMainFrame::OnUpdateToolsdraw(CCmdUI* pCmdUI)
{// TODO: 在此添加命令更新用户界面处理程序代码
}
  1. 编写函数体中处理代码;数据成员和函数成员实现
    在这里插入图片描述
  • 弹出式菜单实现
    鼠标右键的点击为消息响应函数操作。
  1. 创建弹出菜单资源;
  2. 通过消息响应函数来弹出菜单项,有两种实现方式:
void CSingleDocAppView::OnRButtonDown(UINT nFlags, CPoint point)
{// TODO: 在此添加消息处理程序代码和/或调用默认值CMenu menu;       // 菜单(包含主菜单栏和子菜单)   CMenu* pSubMenu;  // 右键菜单   // 加载菜单资源到menu对象   menu.LoadMenu(IDR_POPUP_EDIT);// 因为右键菜单是弹出式菜单,不包含主菜单栏,所以取子菜单   pSubMenu = menu.GetSubMenu(0);// 将坐标值由客户坐标转换为屏幕坐标   ClientToScreen(&point);// 弹出右键菜单,菜单左侧与point.x坐标值对齐   pSubMenu->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this);CView::OnRButtonDown(nFlags, point);
}
void CSingleDocAppView::OnRButtonUp(UINT /* nFlags */, CPoint point)
{ClientToScreen(&point);OnContextMenu(this, point);
}void CSingleDocAppView::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERStheApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}
  1. 在弹出式菜单弹出的相关类中对菜单项的消息响应函数进行实现。

2、工具栏

2.1 简介

工具栏一般位于主框架窗口的上部,菜单栏的下方,由一些带图片的按钮组成。当用户用鼠标单击工具栏上某个按钮时,程序会执行相应的操作,如果鼠标没有点击,只是停留在某个按钮上一会后,会弹出一个小窗口显示提示信息。
工具栏一般位于主框架窗口的上部,菜单栏的下方,由一些带图片的按钮组成。当用户用鼠标单击工具栏上某个按钮时,程序会执行相应的操作,如果鼠标没有点击,只是停留在某个按钮上一会后,会弹出一个小窗口显示提示信息。

工具栏属性

资源位置:在资源视图的Toolbar栏下
在这里插入图片描述
以IDR_MAINFRAME工具栏的第一个按钮为例说明工具栏按钮的各项属性。用鼠标单击工具栏资源上的第一个按钮,属性页中就会显示其属性。下面分别讲解各项属性。

  1. ID属性:ID_FILE_NEW。不知大家是否还记得,菜单IDR_MAINFRAME的菜单项File->New的ID也是ID_FILE_NEW,两者ID相同,正是如此才使得工具栏第一个按钮与菜单项File->New能实现相同的功能。如果想让工具栏某个按钮与菜单栏某个菜单项点击后执行的操作相同,就要为两者设置相同的ID。
  2. Prompt属性:Create a new document\nNew。此属性为工具栏按钮的提示文本。在鼠标指向此按钮时,状态栏中会显示“Create a new document”,当弹出提示信息窗口时会显示包含“New”的提示信息。“\n”是两者的分隔转义符。
  3. Height属性:15。此属性为工具栏按钮的像素高度。
  4. Width属性:16。此属性为工具栏按钮的像素宽度。

在这里插入图片描述

  • 工具栏资源的最右边总是会有一个待编辑的按钮,我们对其进行编辑后,工具栏资源会自动增加一个新的空白按钮,这也实现了按钮的添加操作。如果我们想要删除某个按钮,就可以用鼠标左键点住它,拖出工具栏资源的范围即可。
  • 另外,我们看到,第三个按钮(保存按钮)和第四个按钮(剪切按钮)之间有一些间隙,在运行程序后会出现一个竖的分隔线,所以想要在两个按钮之间添加分隔线的话,可以用鼠标左键拖住右边的按钮往右稍移动一些就可以了。

2.2 创建

消息

一般情况下工具栏中的按钮在菜单栏中都有对应的菜单项,两者实现的功能相同,要想实现这种效果,只需要将工具栏按钮的ID与对应的菜单栏中菜单项的ID设置为相同值即可。
在实际使用工具栏时,除了前面讲的资源编辑外,其他使用与菜单类似。例如,对COMMAND消息和UPDATE_COMMAND_UI消息,可像菜单应用实例那样为工具栏按钮添加消息处理函数。

  • 如果工具栏按钮对应的菜单项已经添加了消息处理函数,那么就不必再为它添加了,因为它的ID与菜单项相同,所以会调用同样的消息处理函数。这样点击工具栏按钮与点击相应菜单项执行相同的功能,在菜单项为选中、激活或禁用等状态时,工具栏按钮会有一样的状态。

CToolBar类的主要成员函数

MFC为工具栏的操作提供了CToolBar类。

  1. virtual BOOL CreateEx(
    CWnd* pParentWnd,
    DWORD dwCtrlStyle = TBSTYLE_FLAT,
    DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP,
    CRect rcBorders = CRect(0, 0, 0, 0),
    UINT nID = AFX_IDW_TOOLBAR
    );
    创建工具栏对象。参数pParentWnd为工具栏父窗口的指针。参数dwCtrlStyle为工具栏按钮的风格,默认为TBSTYLE_FLAT,即“平面的”。参数dwStyle为工具栏的风格,默认取值WS_CHILD | WS_VISIBLE | CBRS_ALIGN_TOP,由于是主框架窗口的子窗口,所以要有WS_CHILD和WS_VISIBLE风格,CBRS_ALIGN_TOP风格表示工具栏位于父窗口的顶部, 各种风格可以参见MSDN的Toolbar Control and Button Styles中的定义。参数rcBorders为工具栏边框各个方向的宽度,默认为CRect(0, 0, 0, 0),即没有边框。参数nID为工具栏子窗口的ID,默认为AFX_IDW_TOOLBAR。
  2. BOOL LoadBitmap(UINT nIDResource);
    为工具栏加载位图。参数nIDResource为位图资源的ID。成功则返回TRUE,否则返回FALSE。注意,这里的位图资源应当为每个工具栏按钮都提供位图,如果图片不是标准大小(16像素宽,15像素高),则需要调用SetSizes成员函数调整按钮大小和图片大小。
  3. BOOL LoadToolBar(UINT nIDResource);
    加载由nIDResource指定的工具栏。参数nIDResource为要加载的工具栏的资源ID。成功则返回TRUE,否则返回FALSE。
  4. void SetSizes(SIZE sizeButton,SIZE sizeImage);
    设置工具栏按钮的大小和图片的大小。参数sizeButton为工具栏按钮的像素大小。参数sizeImage为图片的像素大小。
  5. void SetButtonStyle(int nIndex,UINT nStyle);
    设置工具栏按钮或分隔线的风格,或者为按钮分组。参数nIndex为将要进行设置的按钮或分隔线的索引。参数nStyle为按钮风格,可以是以下取值:
    TBBS_BUTTON 标准按钮(默认)
    TBBS_SEPARATOR 分隔条
    TBBS_CHECKBOX 复选框
    TBBS_GROUP 标记一组按钮的开始
    TBBS_CHECKGROUP 标记一组复选框的开始
    TBBS_DROPDOWN 创建下拉列表按钮
    TBBS_AUTOSIZE 按钮的宽度根据按钮文本计算,而不基于图片大小
    TBBS_NOPREFIX 按钮的文本没有快捷键前缀
  6. UINT GetButtonStyle(int nIndex) const;
    获取工具栏按钮或分隔条的风格。风格可参考SetButtonStyle。参数nIndex为按钮或分隔条的索引。
  7. BOOL SetButtonText(int nIndex,LPCTSTR lpszText);
    设置工具栏按钮的文本。参数nIndex为工具栏按钮的索引。参数lpszText为指向要设置的文本字符串的指针。设置成功则返回TRUE,否则返回FALSE。
  8. CString GetButtonText(int nIndex) const;
    获取工具栏按钮上显示的文本。参数nIndex为工具栏按钮的索引。

2.3 实例

CMainFrame类中看到,它创建工具栏所使用的类并不是常用的CToolBar类,而是CMFCToolBar类。CMFCToolBar类是自VS2008以来MFC提供的类,它与CToolBar类有些类似,但功能更丰富。这里要注意,CMFCToolBar类与CToolBar类并没有任何派生关系。
以CMFCToolBar类讲解工具栏的创建步骤:

  1. 创建工具栏资源。
  2. 构造CMFCToolBar类的对象。
  3. 调用CMFCToolBar类的Create或CreateEx成员函数创建工具栏。
  4. 调用LoadToolBar成员函数加载工具栏资源。
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||!m_wndToolBar.LoadToolBar(theApp.m_bHiColorIcons ? IDR_MAINFRAME_256 : IDR_MAINFRAME)){TRACE0("未能创建工具栏\n");return -1;      // 未能创建}CString strToolBarName;bNameValid = strToolBarName.LoadString(IDS_TOOLBAR_STANDARD);ASSERT(bNameValid);m_wndToolBar.SetWindowText(strToolBarName);CString strCustomize;bNameValid = strCustomize.LoadString(IDS_TOOLBAR_CUSTOMIZE);ASSERT(bNameValid);m_wndToolBar.EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize);
}

因为创建框架窗口时需要调OnCreate函数,所以工具栏的创建也是在OnCreate中完成的。

  • 工具栏的停靠
    创建好工具栏后,如果想要停靠工具栏,也需要添加相应的停靠代码。工具栏停靠的步骤及需要调用的函数如下(前两个步骤可以颠倒顺序):
  1. 在框架窗口中启用停靠
    若要将工具栏停靠到某个框架窗口,则必须启用该框架窗口(或目标)以允许停靠。可以在CFrameWndEx类中调用下面的成员函数来实现:
BOOL EnableDocking(DWORD dwDockStyle);

该函数采用一个DWORD参数,用来指定框架窗口的哪个边可以接受停靠,可以有四种取值:CBRS_ALIGN_TOP(顶部)、CBRS_ALIGN_BOTTOM(底部)、CBRS_ALIGN_LEFT(左侧)、CBRS_ALIGN_RIGHT(右侧)。如果希望能够将控制条停靠在任意位置,将CBRS_ALIGN_ANY作为参数传递给EnableDocking。

  1. 工具栏启用停靠
    框架窗口启用停靠准备好后,必须以相似的方式准备工具栏。为想要停靠的每一个工具栏CMFCToolBar对象调用下面的函数:
virtual void EnableDocking(DWORD dwAlignment);

允许工具栏停靠到框架窗口,并指定工具栏应停靠的目标边。此函数指定的目标边必须与框架窗口中启用停靠的边匹配,否则工具栏无法停靠,为浮动状态。

  1. 停靠工具栏
    当用户试图将工具栏放置在允许停靠的框架窗口某一边时,需要框架CFrameWndEx类调用以下函数:
void DockPane(CBasePane* pBar,UINT nDockBarID=0,LPCRECT lpRect=NULL);
  • 参数pBar为要停靠的控制条的指针,参数nDockBarID为要停靠的框架窗口某条边的ID,可以是以下四种取值:AFX_IDW_DOCKBAR_TOP、AFX_IDW_DOCKBAR_BOTTOM、AFX_IDW_DOCKBAR_LEFT、AFX_IDW_DOCKBAR_RIGHT

CMainFrame类的OnCreate函数实现中,工具栏的停靠过程:

// TODO: 如果您不希望工具栏和菜单栏可停靠,请删除这五行m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);EnableDocking(CBRS_ALIGN_ANY);DockPane(&m_wndMenuBar);DockPane(&m_wndToolBar);

3、状态栏

3.1 简介

状态栏相信大家在很多窗口中都能见到,它总是用来显示各种状态。状态栏实际上也是一个窗口,一般分为几个窗格,每个窗格分别用来显示不同的信息和状态等,如菜单项和工具栏按钮的提示信息。
用MFC向导生成的单文档或多文档程序都会自动创建状态栏,该状态栏被分为了几个窗格,分别用来显示菜单项和工具栏按钮的提示信息及Caps Lock、Num Lock、Scroll Lock键的状态。

3.2 创建

CStatusBar类

  1. virtual BOOL Create(CWnd* pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, UINT nID = AFX_IDW_STATUS_BAR);
    创建一个状态栏。参数pParentWnd为状态栏父窗口的指针,参数dwStyle为状态栏的风格,除了标准的Windows风格外,它还支持:
    CBRS_TOP:位于框架窗口的顶部。
    CBRS_BOTTOM:位于框架窗口的底部。
    CBRS_NOALIGN:父窗口大小改变时状态栏不会被重新定位。
    参数nID指定状态栏的ID。
  2. BOOL SetIndicators(const UINT* lpIDArray, int nIDCount);
    为每个指示器设置显示文本,具体来说,就是用lpIDArray数组中的对应元素为每个指示器设置一个ID,然后加载每个ID代表的字符串,设置为这些指示器的显示文本。参数lpIDArray为指向一个ID数组的指针,参数nIDCount为lpIDArray数组的元素个数。
  3. UINT GetItemID(int nIndex) const;
    获取由nIndex指定的指示器的ID。参数nIndex为要获取ID的指示器索引。
  4. CString GetPaneText(int nIndex) const;
    获取状态栏窗格中显示的文本。参数nIndex为要获取文本的窗格的索引。返回值为包含窗格文本的CString对象。
  5. BOOL SetPaneText(int nIndex, LPCTSTR lpszNewText, BOOL bUpdate = TRUE);
    设置状态栏窗格的显示文本。参数nIndex为要设置文本的窗格的索引,参数lpszNewText为指向新的窗格文本的指针,参数bUpdate表示是否设置后立即更新显示。如果设置成功则返回TRUE,否则返回FALSE。

状态栏创建

在CMainFrame类中看到,创建状态栏时使用的是CMFCStatusBar类对象。CMFCStatusBar类是自VS2008以来提供的状态栏类,用法与CStatusBar类相似,甚至很多成员函数也类似,但它的功能更加丰富。
创建步骤:

  1. 构造一个CMFCStatusBar类的对象。
    在MainFrm.h文件中,为CMainFrame类定义了一个成员对象:CMFCStatusBar m_wndStatusBar;。
  2. 调用CMFCStatusBar::Create函数来创建状态栏窗口。
    在CMainFrame::OnCreate函数的实现中,我们可以找到CMFCStatusBar::Create函数的调用:
if (!m_wndStatusBar.Create(this))   
{   TRACE0("Failed to create status bar\n");   return -1;      // fail to create   
}
  1. 调用CMFCStatusBar::SetIndicators函数为状态栏划分窗格,并为每个指示器设置显示文本。
    CMFCStatusBar::SetIndicators函数需要一个ID数组的参数,在MainFrm.cpp中,如下定义了一个窗格ID的数组:
static UINT indicators[] =   
{   ID_SEPARATOR,           // status line indicator   ID_INDICATOR_CAPS,   ID_INDICATOR_NUM,   ID_INDICATOR_SCRL,   
};
  • indicators数组定义了状态栏窗格的划分信息。第一个元素一般为ID_SEPARATOR,对应的窗格用来显示命令提示信息,上面数组中的后三项为指示器文本的字符串ID,可以根据这些ID在String Table字符串资源中找到相应的字符串,查找方法是,在Resource View资源视图中,打开String Table字符串资源,可以看到有ID、Value和Caption三列,在ID列中找到需要的ID,对应的Caption列文本就是要查找的字符串。ID_INDICATOR_CAPS、ID_INDICATOR_NUM和ID_INDICATOR_SCRL对应的字符串分别是CAP、NUM、SCRL,对应的三个窗格分别为Caps Lock指示器、Num Lock指示器和Scroll Lock指示器。

在CMainFrame::OnCreate函数中调用:

m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));

状态栏就创建完成了,之后我们可以通过CMFCStatusBar::SetPaneText设置窗格的文本。

3.3 实例

在状态栏添加一个事件窗格,用来显示系统时间。

  1. 在Resource View资源视图中打开String Table字符串资源,然后在最后一行的下一个空白行中,或者任意处点右键选择“New String”,添加一个新的字符串资源.
    在这里插入图片描述
  2. 在indicators数组的第一个元素ID_INDICATOR_SCRL后插入IDS_INDICATOR_TIME。
static UINT indicators[] =
{ID_SEPARATOR,           // 状态行指示器ID_INDICATOR_CAPS,ID_INDICATOR_NUM,ID_INDICATOR_SCRL,IDS_INDICATOR_TIME
};
  1. 要实时显示系统时间,就需要使用一个定时器,每秒钟更新一次时间显示。在CMainFrame::OnCreate函数中开启定时器.
// 启动定时器,定时器ID为1,定时时间为1000ms,即1s   SetTimer(1, 1000, NULL);  
  1. 在Class View类视图中找到CMainFrame类,右键选择“Properties”,然后在显示出来的属性页中,点工具栏上的Messages按钮,即显示出消息列表,找到WM_TIMER消息,添加其消息处理函数void CMainFrame::OnTimer(UINT_PTR nIDEvent),并修改此函数实现如下:
void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{// TODO: 在此添加消息处理程序代码和/或调用默认值CString strTime;// 获取系统当前时间,并保存到curTime   CTime curTime = CTime::GetCurrentTime();// 格式化curTime,将字符串保存到strTime   strTime = curTime.Format(_T("%H:%M:%S"));// 在状态栏的时间窗格中显示系统时间字符串   m_wndStatusBar.SetPaneText(4, strTime);CFrameWndEx::OnTimer(nIDEvent);
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/32250.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

渗透测试-若依框架的杀猪交易所系统管理后台

前言 这次是带着摸鱼的情况下简单的写一篇文章,由于我喜欢探究黑灰产业,所以偶尔机遇下找到了一个加密H币的交易所S猪盘,我记得印象是上年的时候就打过这一个同样的站,然后我是通过指纹查找其它的一些站,那个站已经关…

海外短剧系统如何征服观众心

海外短剧系统要征服观众的心,需要综合考虑多个方面。 1、紧凑的剧情设计: 短小精悍:海外短剧通常每集时长不超过半小时,甚至有的仅有几分钟。这种紧凑的剧情设计让观众能够在短时间内迅速沉浸在故事中,无需花费大量时间…

STM32驱动-ads1112

汇总一系列AD/DA的驱动程序 ads1112.c #include "ads1112.h" #include "common.h"void AD5726_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE );//PORTA、D时钟使能 G…

大脑临界状态:探索思维背后的物理机制

在深度思考或创造性灵感的涌现时刻,个体常体验到一种介于混乱与有序之间的特殊心理状态。这种感受实则反映了大脑在认知过程中的临界状态,这是一种涉及复杂物理现象的心理活动表现。近期研究表明,大脑结构中存在着与临界性密切相关的物理特性…

为什么挂牌量是跟踪楼市情况的核心指标?

通过挂牌量,可以跟踪被动卖出者的数量,从而理解楼市的进展。 引子 楼市的“5.17”新政落地有一个多月了,然而,资本市场对“楼市的复苏预期”却在不断地下修。 以房地产开发的龙头企业保利发展为例,市场在“5.17”新…

学习笔记——路由网络基础——动态路由

五、动态路由 1、动态路由概述 动态路由:通过在设备上运行某种协议,通过该协议自动交互路由信息的过程。 动态路由协议有自己的路由算法,能够自动适应网络拓扑的变化,适用于具有一定数量三“层设备的网络。 动态路由协议适用场…

Python发送Email的性能怎么样?如何配置?

Python发送Email怎么配置SMTP?批发邮件的方法技巧? Python是一种广泛使用的编程语言,因其简洁和强大的功能深受开发者喜爱。在许多应用场景中,Python发送Email是一个常见需求。那么,Python发送Email的性能怎么样呢&am…

基于Java的农机电招平台系统

你好呀,我是计算机学姐码农小野!如果你对农机电招平台系统感兴趣或有相关开发需求,可以私信联系我。 开发语言 Java 数据库 MySQL 技术 B/S结构,SpringBoot框架 工具 Eclipse,Navicat,Tomcat8.0 系…

代理的匿名级别有哪些?为什么匿名性很重要?

在互联网时代,代理服务器在许多领域都扮演着重要的角色。无论是个人用户还是企业,都可能需要使用代理来保护自己的隐私和数据安全。在选择代理服务时,匿名级别是一个重要的考虑因素。本文将介绍代理的匿名级别,并解释为什么匿名性…

华为RH2288 V3安装 Linux 系统,安装过程心得

带着U盘,怀着激动的心情进入机房安装操作系统,结果没有显示器和键盘鼠标,傻眼了。 作为过来人,温馨提醒,进入机房前记得先打听,准备好这些:机房房间号、机柜编号、物理机编号、键盘、鼠标、显示…

ShareX,屏幕截图、屏幕录制和文件共享,还提供了丰富的高级功能和自定义选项

ShareX是一个免费开源的Windows应用程序,用于屏幕截图、屏幕录制和文件共享。它不仅支持基本的屏幕截图功能,还提供了丰富的高级功能和自定义选项,使其成为提高工作效率和截图体验的利器。以下是ShareX v16.1.0便携版的主要功能和特色&#x…

别再滥用std::async了,strace命令暴露了一个乱开线程问题

用strace查看进程的系统调用后,发现一个std::async滥用问题 问题现象 进程的系统调用clone次数持续增加 使用工具strace发现进程clone系统调用过多且一直在增加 strace -c -p PID问题分析 clone在做什么:创建进程(线程) 查看…

【吊打面试官系列-Mysql面试题】什么是存储过程?用什么来调用?

大家好,我是锋哥。今天分享关于 【什么是存储过程?用什么来调用?】面试题,希望对大家有帮助; 什么是存储过程?用什么来调用? 答:存储过程是一个预编译的 SQL 语句,优点是…

昇思25天学习打卡营第4天|数据变换(Transforms)

一、简介: 数据变换是指将已有的数据转换成可以提供给模型直接训练和验证的数据格式,在深度学习中一般被称为数据预处理,之前在昇思25天学习打卡营第3天|数据集Dataset-CSDN博客 介绍数据集的时候已经有了一个简单的使用,下面将具…

docker 基本用法及跨平台使用

一、Docker的优点 docker 主要解决的问题就是程序开发过程中编译和部署中遇到的环境配置的问题。 1.1 Docker与其他虚拟机层次结构的区别** 运行程序重点关注点在于环境。 VM虚拟机是基于Hypervisor虚拟化服务运行的。 Docker是基于内核的虚拟化技术实现的。 1.2 Docker的技…

基于uniapp的h5接入企业微信客服在线聊天

首先说下企业微信接入场景,支持的接入场景有以下几种,基本上涵盖了微信生态大部分场景: 接入步骤 1.创建企业微信号 按照官方操作步骤注册,需要注意的是未认证仅支持接入100人,已认证支持接入2000人. 2.创建客服账号 每个客服账号支持配置人工或是机器人或是人工机器人回复…

分享uniapp + Springboot3+vue3小程序项目实战

分享uniapp Springboot3vue3小程序项目实战 经过10天敲代码,终于从零到项目测试完成,一个前后端分离的小程序实战项目学习完毕 时间从6月12日 到6月22日,具有程序开发基础,第一次写uniapp,Springboot以前用过,VUE3也…

C++ (week8):数据库

文章目录 一、数据库简介1.数据库2.MySQL(1)数据库的结构(2)MySQL的三种使用方式(3)命令行(4)Navicat Premium 二、SQL1.SQL (Structured Query Language),即结构化查询语言2.数据定义语言 DDL (Data Definition Language) ,创建、修改、删除数据库、表结…

dex文件结构(二):dex文件加载基本原理

return mClassLoader; } 1.3 ApplicationLoaders.getClassLoader public ClassLoader getClassLoader(String zip, String libPath, ClassLoader parent){ //Class.getSystemClassLoader返回的是一个PathClassLoader //baseParent是BootClassLoader ClassLoader basePare…

刷代码随想录有感(113):动态规划——爬楼梯plus

题干&#xff1a; 代码&#xff1a; #include<bits/stdc.h> using namespace std;int main(){int n,m;cin>>n>>m;vector<int>dp(n 1, 0);dp[0] 1;for(int j 0; j < n; j){for(int i 1; i < m; i){if(j > i)dp[j] dp[j - i];}}cout<&…