上一页 下一页 返回

4.2工具条的编程技术

  本节将讨论一些与工具条有关的编程技术,主要包括命令处理、命令更新、按钮风格和工具条的隐藏/显示等技术。

4.2.1命令处理

  要使菜单和工具条执行命令,光为它们指定命令ID是不行的,必须为每个命令ID定义命令处理函数。如果不为命令定义命令处理函数或下面将要提到的命令更新处理函数,则框架将自动使该命令对应的菜单项和按钮禁止(灰化),这就是4.1节中的工具条按钮和菜单项灰化的原因。

  利用ClassWizard可以很方便地加入命令处理函数,请读者按以下步骤操作:

按Ctrl+W键进入ClassWizard。

T4_6.tif (317964 bytes)

图4.6 ClassWizard对话框

如图4.6所示,在Class name栏中选择CMainFrame,在Object IDs栏中选择ID_RECORD_START,在Messages栏中双击COMMAND项,则ClassWizard会弹出一个对话框询问命令处理函数的名字,使用其提供的函数名即可。按OK按钮后,函数OnRecordStart就被加入到了Member functions栏中。

仿照第2步,为ID_RECORD_STOP定义一个命令处理函数。

按OK按钮关闭ClassWizard对话框。这时读者会发现CMainFrame类多了两个成员函数,OnRecordStart和OnRecordStop。

现在要在这两个命令处理函数中插入相应的源代码以实现其功能。当然,这里不会真的实现开始录音和停止录音的功能。我们只是让这两个函数发出一个声音,象征性地表示功能的执行,具体代码如清单4.2所示。

 

清单4.2 OnRecordStart和OnRecordStop函数

void CMainFrame::OnRecordStart()

{

// TODO: Add your command handler code here

 

MessageBeep((UINT)(-1));

}

void CMainFrame::OnRecordStop()

{

// TODO: Add your command handler code here

 

MessageBeep((UINT)(-1));

}

  编译并运行Record,可以看到Start和Stop命令已经可以执行了。

4.2.2命令更新

  虽然Start和Stop命令可以执行了,但是还有一个不足之处。在没有开始录音之前,Stop命令应该是禁止的,也即对应的菜单项和按钮应是禁止的,这是因为此时没有必要执行该命令。录音开始后,Stop命令应该允许,而Start命令则应变为禁止。我们可以利用MFC的命令更新机制实现此逻辑功能。

  在菜单下拉之前,或在工具条按钮处在空闲循环期间,MFC会发一个更新命令,这将导致命令更新处理函数的调用。命令更新处理函数可以根据情况,使用户接口对象(主要指菜单项和工具条按钮)允许或禁止。定义命令更新处理函数的方法如下:

按Ctrl+W键进入ClassWizard。

T4_7.tif (317964 bytes)

图4.7 ClassWizard对话框

如图4.7所示,在Class name栏中选择CMainFrame,在Object IDs栏中选择ID_RECORD_START,在Messages栏中双击UPDATE_COMMAND_UI项,则ClassWizard会弹出一个对话框询问命令更新处理函数的名字,使用其提供的函数名即可。按OK按钮后,函数OnUpdateRecordStart就被加入到了Member functions栏中。

仿照步2,为ID_RECORD_STOP定义一个命令更新处理函数。

按OK按钮关闭ClassWizard对话框。这时读者会发现CMainFrame类多了两个成员函数,OnUpdateRecordStart和OnUpdateRecordStop。

  命令更新处理函数有一个参数是CCmdUI类的指针,通过调用CCmdUI类的成员函数Enable(TRUE)或Enable(FALSE)可以使用户接口对象允许或禁止。需要给CMainFrame加一个布尔型成员变量以表明是否正在录音,这样命令更新处理函数可根据这个变量来决定用户接口对象的状态。请读者在CMainFrame类内加入下面一行代码:

BOOL m_bWorking;

接下来请读者按清单4.3进行修改。

清单4.3 命令更新处理

CMainFrame::CMainFrame()

{

// TODO: add member initialization code here

 

m_bWorking=FALSE;

}

void CMainFrame::OnRecordStart()

{

// TODO: Add your command handler code here

MessageBeep((UINT)(-1));

 

m_bWorking=TRUE;

}

void CMainFrame::OnRecordStop()

{

// TODO: Add your command handler code here

MessageBeep((UINT)(-1));

 

m_bWorking=FALSE;

}

void CMainFrame::OnUpdateRecordStart(CCmdUI* pCmdUI)

{

// TODO: Add your command update UI handler code here

 

pCmdUI->Enable(!m_bWorking);

}

void CMainFrame::OnUpdateRecordStop(CCmdUI* pCmdUI)

{

// TODO: Add your command update UI handler code here

 

pCmdUI->Enable(m_bWorking) ;

}

  m_bWorking的初值应是FALSE,对它的初始化工作在CMainFrame的构造函数中完成。m_bWorking的值在处理Start和Stop命令时会被更新以反映当前的状态。两个命令更新处理函数都调用了CCmdUI::Enable,该函数根据m_bWorking的值来更新命令接口对象。

  编译并运行Record,现在Start和Stop命令的逻辑功能已经实现了。

4.2.3按钮风格

  在Record程序中,用户可以选择两种采样频率来录音。用户接口对象应该能反映出当前的采样频率。普通的工具条按钮在按下后会立刻弹起来,我们希望Record程序的频率选择按钮具有单选按钮的风格,即当用户选择了一个采样频率时,该采样频率对应的按钮一直处于按下的状态,而另一个频率选择按钮应处于弹起状态。

  我们可以利用CCmdUI::SetCheck函数来实现这一功能,在命令更新函数中调用CCmdUI::SetCheck(TRUE)或CCmdUI::SetCheck(FALSE)可将用户接口对象设定为选中或不选中状态,当一个用户接口对象被选中时,相应的工具按钮会处于按下的状态,并且相应的菜单项的前面会加上一个选中标记。这里需要给CMainFrame类加一个布尔型成员变量以表明当前的采样频率。请读者在CMainFrame类内加入下面一行代码:

BOOL m_bHighQuality;

接下来请读者按清单4.4进行修改。

清单4.4

CMainFrame::CMainFrame()

{

// TODO: add member initialization code here

m_bWorking=FALSE;

 

m_bHighQuality=TRUE;

}

void CMainFrame::OnHighQuality()

{

// TODO: Add your command handler code here

 

m_bHighQuality=TRUE;

}

void CMainFrame::OnLowQuality()

{

// TODO: Add your command handler code here

 

m_bHighQuality=FALSE;

}

void CMainFrame::OnUpdateHighQuality(CCmdUI* pCmdUI)

{

// TODO: Add your command update UI handler code here

 

pCmdUI->SetCheck(m_bHighQuality);

}

void CMainFrame::OnUpdateLowQuality(CCmdUI* pCmdUI)

{

// TODO: Add your command update UI handler code here

 

pCmdUI->SetCheck(!m_bHighQuality);

}

  m_bHighQuality的初值是TRUE,即缺省时是高频采样,对它的初始化工作在CMainFrame的构造函数中完成。m_bHighQuality的值在处理High quality和Low quality命令时会被更新以反映当前的状态。两个命令更新处理函数都调用了CCmdUI::SetCheck,该函数根据m_bHighQuality的值来更新命令接口对象,从而使工具条按钮具有了单选按钮的风格。

  编译并运行Record,读者可以看到具有新风格的工具条按钮。当选择采样频率时,相应的菜单项前会出现一个选中标记,相应的工具条按钮会被按下。

4.2.4工具条的隐藏/显示

  读者可能已经试过了Record程序的View菜单的功能。通过该菜单用户可以隐藏/显示工具条和状态栏,这个功能是由AppWizard自动实现的。由于第二个工具条是手工建立的,因此它不会自动具备隐藏/显示功能。但我们可以通过编程来实现第二个工具条的隐藏/显示:

打开IDR_MAINFRAME菜单资源

在View菜单中加入一个名为Toolbar1的菜单项,指定其ID为ID_VIEW_TOOLBAR1,并在Prompt栏中输入Show or hide the toolbar1\nToggle ToolBar1。

按Ctrl+W键进入ClassWizard。在Class name栏中选择CMainFrame,在Object IDs栏中选择ID_VIEW_TOOLBAR1,并为该命令ID定义命令处理函数OnViewToolbar1和命令更新处理函数OnUpdateViewToolbar1。

按清单4.5修改程序。

 

清单4.5 显示/隐藏工具条

void CMainFrame::OnViewToolbar1()

{

// TODO: Add your command handler code here

 

m_wndToolBar1.ShowWindow(m_wndToolBar1.IsWindowVisible()?

SW_HIDE:SW_SHOW);

RecalcLayout();

}

void CMainFrame::OnUpdateViewToolbar1(CCmdUI* pCmdUI)

{

// TODO: Add your command update UI handler code here

 

pCmdUI->SetCheck(m_wndToolBar1.IsWindowVisible());

}

  调用CWnd::ShowWindow(SW_SHOW)或CWnd::ShowWindow(SW_HIDE)可以显示或隐藏窗口。由于工具条也是窗口,CToolBar是CWnd类的继承类,故该函数也是CToolBar的成员。在命令处理函数OnViewToolbar1中,我们调用CToolBar::ShowWindow来显示/隐藏工具条,在调用时会利用CWnd::IsWindowVisible函数作出判断,如果工具条是可见的,就传给ShowWindow函数SW_HIDE参数以隐藏工具条,否则,就传SW_SHOW参数显示工具条。接着要调用CMainFrame::RecalcLayout以重新调整主框架窗口的布局。

  命令更新处理函数OnUpdateViewToolbar1会根据工具条是否可见使View->Toolbar1菜单项选中或不选中。

  编译并运行Record,现在Record程序已变得很有趣了。至此,读者已经掌握了工具条的一些实用编程技术。