最近在做一个音频传输的项目,现在已经到了界面的部分了,决定使用MFC来写一个界面,通信必然离不了IP,今天写的是MFC中IP控件的使用。这里模仿VS的界面做一个静态IP配置的界面,如下所示:
注意:对话框下面的按钮有两个,分别是确定和取消按钮,后面隐藏了确认按钮,并将按钮取消改成完成
该对话框的效果是:
1.点第一个Radio控件,后面Group控件内的所有IP框变灰,不接受输入;
2.点第二个Radio控件,后面三个IP控件接受输入;
3.当焦点移到子网掩码控件时,根据输入的IP自动生成;
4.在IP框输入时,按键盘Enter键,焦点依次下移;
5.点击完成按钮,弹出确认对话框。
查看MSDN上该类的成员函数,如下:
void ClearAddress();
清除IP地址控件中的内容。
BOOL IsBlank();
如果IP地址控件的所有域均为空,返回非0值;否则返回0。
void SetAddress(BYTE nField0,BYTE nField1,BYTEnField2,BYTE nField3);
void SetAddress(DWORD dwAddress);
设置IP地址控件中的地址值。
第一种形式是用4个0~255的整数分别设置IP地址各个域的值。
第二种形式是用1个长整数设置IP地址值。
int GetAddress(BYTE& nField0,BYTE&nField1,BYTE& nField2,BYTE& nField3);
int GetAddress(DWORD& dwAddress);
获取IP地址控件中的地址值。
第一种形式是把IP地址的4个域填充到用4个引用中。
第二种形式是把IP地址填充到1个长整数的引用中。
返回值:IP地址控件中非空域的数量。
void SetFieldFocus(WORD nField);
把焦点设置在指定的域中。nField取值为0~3,如果大于3,则焦点设置到第一个空域中,若所有域均非空,则 焦点设置在第一个域中。
设置指定域中数值的取值范围。
nField:域索引,取值0~3;
nLower:域的下限;
nUpper:域的上限。
1, 首先创建基于对话框的MFC应用程序,名为:IPControl,然后按上图,排好控件后,分别为三个IP框添加三个控制变量,m_Addr1,m_Addr2,m_Addr3,分别依次对应IP,Netmask,Dns。
2,现在来完成效果1和效果2;
在对话框上右击鼠标,添加BOOL变量IP_From,这里IP_From = FALSE表示IP地址是自动获取的,IP_From = TRUE,则表示手动配置;
然后添加函数EnableIp(),用来控制三个IP框是否变灰,代码如下:
void CIPControlDlg::EnableIp()
{
m_Addr1.EnableWindow( IP_From );
m_Addr2.EnableWindow( IP_From );
m_Addr3.EnableWindow( IP_From );
}
在资源页面,分别双击两个Radio控件,添加控件处理函数,其中点击第一个Radio控件时,让三个IP控件中的内容清空,用成员函数ClearAddress();当点击第二个Radio控件时,让焦点默认停在第一个IP控件上。具体代码如下:
void CIPControlDlg::OnBnClickedRadio1()
{
// TODO: 在此添加控件通知处理程序代码
IP_From = FALSE;
m_Addr1.ClearAddress();
m_Addr2.ClearAddress();
m_Addr3.ClearAddress();
EnableIp();
}
void CIPControlDlg::OnBnClickedRadio2()
{
// TODO: 在此添加控件通知处理程序代码
IP_From = TRUE;
EnableIp();
m_Addr1.SetFieldFocus(0);
}
在OnInitialDialog()中添加如下代码来设置默认效果:
<span style="font-size:14px;">CheckDlgButton(IDC_RADIO1,TRUE);
CheckDlgButton(IDC_RADIO2,FALSE);
EnableIp();//这里因为IP_From 初始化为False了,所以默认三个IP控件为灰色</span>
点击运行,效果如下:
3,实现第三个效果:对于子网掩码,我们都知道,只需要知道IP第一个域的值就能得以确定
这里采用消息传递的形式,根据函数OnSetfocus()来设置这样的效果,由于在VS2010中没有看到EN_SETFOCUS这个消息,因此这里选择手动添加。
1)点击对话框资源,右击添加类CSubNetMask;并在头文件中添加IP控件指针CIPAddressCtrl *m_IPAddrCtrl用来获取输入的IP地址,同时在构造函数中初始化该指针为NULL;并在OnInitialDialog()中添加如下代码,让该指针指向IP地址的输入框:
m_Addr2.m_IPAddrCtrl = &m_Addr1;
2)在SubNetMask.h中添加消息处理函数afx_msg void OnSetfocus(),并在在SubNetMask.cpp中添加消息处理函数如下:
<span style="font-size:14px;">BEGIN_MESSAGE_MAP(CSubNetMask, CIPAddressCtrl)
ON_CONTROL_REFLECT(EN_SETFOCUS,&CSubNetMask::OnSetfocus)// 手动添加消息处理函数
END_MESSAGE_MAP();
void CSubNetMask::OnSetfocus()// 手动添加消息处理函数
{
// TODO: 在此添加控件通知处理程序代码
if (m_IPAddrCtrl && IsBlank())
{
BYTE field1,field2,field3,field4;
m_IPAddrCtrl->GetAddress(field1,field2,field3,field4);
if (field1 >= 0 && field1<= 127)
{
SetAddress(255,0,0,0);
}
else if (field1 >= 128 && field1 <=191)
{
SetAddress(255,255,0,0);
}
else if (field1 >= 192 && field1 <=223)
{
SetAddress(255,255,255,0);
}
}
}</span>
这里根据
m_IPAddrCtrl指针和GetAddress()获取输入的IP地址,并根据输入IP的第一个域的值来设置子网掩码。
3)将IPControl.h中m_Addr2的类型改成CSubNetMask;
4,实现第四个效果,这就涉及到修改MFC控件的窗口过程了,这在孙鑫老师的视频教程第7课也讲到了。主要是根据缺省的控件来响应键盘按下的消息。步骤如下:
1)首先资源界面双击确定按钮进入OnBnClickedOk()函数代码区,并注释掉CDialogEx::OnOK();语句;
2)添加窗口过程的步骤如下代码所示:在IPControlDlg.cpp中添加如下代码,注意不是在任何函数中添加,这里面添加的是全局的,所以要使用WINAPI函数来编程;
<pre name="code" class="cpp">WNDPROC prevProc; //回调函数
static LRESULT CALLBACK WinMyProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
if (uMsg == WM_CHAR && wParam == 0x0d)
{
//::SetFocus(::GetNextWindow(hWnd,GW_HWNDNEXT));
::SetFocus(GetNextDlgTabItem(::GetParent(hWnd),hWnd,FALSE));//当键盘按下Enter键时,将焦点转移到下个tab按钮处
//这里的tab按钮可以在对话框资源界面按Ctrl+D来设置顺序
return 1;
}
else
{
return prevProc(hWnd,uMsg,wParam,lParam);
}
}
本来这里面是要响应WM_INITIALDLG消息的,由于VS2010取消了这个消息,而以函数OnInitialDialog()直接来实现,所以这边就不需要再添加消息处理函数了,只需要在OnInitialDialog()中添加如下代码即可:
<span style="font-size:14px;">prevProc = (WNDPROC)SetWindowLong(GetDlgItem(IDC_IPADDRESS1)->m_hWnd, GWL_WNDPROC, (LONG)WinMyProc);</span>
3)资源界面双击确定按钮进入OnBnClickedOk()函数代码区,添加如下代码:
<span style="font-size:14px;">GetNextDlgTabItem(GetFocus())->SetFocus();</span><span style="font-size:18px; color: rgb(204, 51, 204); "> </span>
5,实现效果5:这个效果就比较简单了,首先资源界面双击取消按钮,进入代码区,设置效果5,如下代码所示:
<span style="font-size:14px;">void CIPControlDlg::OnBnClickedCancel()
{
<pre name="code" class="cpp"> if (IDYES== MessageBox("是否确认修改?","确认",MB_YESNO))
{
CDialogEx::OnCancel();
}
}</span>
然后将资源界面右击属性按钮,在属性中隐藏此按钮。
至此,该对话框的效果已经全部实现了,下面附上主要代码段
<span style="font-size:14px;">// IPControlDlg.h : 头文件
//
#pragma once
#include "afxcmn.h"
#include "SubNetMask.h"
// CIPControlDlg 对话框
class CIPControlDlg : public CDialogEx
{
// 构造
public:
CIPControlDlg(CWnd* pParent = NULL); // 标准构造函数
// 对话框数据
enum { IDD = IDD_IPCONTROL_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void EnableIp();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedRadio1();
afx_msg void OnBnClickedRadio2();
afx_msg void OnIpnFieldchangedIpaddress2(NMHDR *pNMHDR, LRESULT *pResult);
public:
CIPAddressCtrl m_Addr1;
CSubNetMask m_Addr2;
CIPAddressCtrl m_Addr3;
BOOL IP_From; //单选按钮的位置,为0表示自动获取
DWORD m_IP;
afx_msg void OnBnClickedOk();
afx_msg void OnBnClickedCancel();
};</span><span style="color: rgb(204, 51, 204); font-size: 18px; ">
</span>
// IPControlDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "IPControl.h"
#include "IPControlDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
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()
// CIPControlDlg 对话框
CIPControlDlg::CIPControlDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CIPControlDlg::IDD, pParent)
, IP_From(FALSE)
, m_IP(0)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CIPControlDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_IPADDRESS1, m_Addr1);
DDX_Control(pDX, IDC_IPADDRESS2, m_Addr2);
DDX_Control(pDX, IDC_IPADDRESS3, m_Addr3);
}
BEGIN_MESSAGE_MAP(CIPControlDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_RADIO1, &CIPControlDlg::OnBnClickedRadio1)
ON_BN_CLICKED(IDC_RADIO2, &CIPControlDlg::OnBnClickedRadio2)
ON_BN_CLICKED(IDOK, &CIPControlDlg::OnBnClickedOk)
ON_BN_CLICKED(IDCANCEL, &CIPControlDlg::OnBnClickedCancel)
END_MESSAGE_MAP()
// CIPControlDlg 消息处理程序
WNDPROC prevProc; //回调函数
static LRESULT CALLBACK WinMyProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
if (uMsg == WM_CHAR && wParam == 0x0d)
{
//::SetFocus(::GetNextWindow(hWnd,GW_HWNDNEXT));
::SetFocus(GetNextDlgTabItem(::GetParent(hWnd),hWnd,FALSE));//当键盘按下Enter键时,将焦点转移到下个tab按钮处
//这里的tab按钮可以在对话框资源界面按Ctrl+D来设置顺序
return 1;
}
else
{
return prevProc(hWnd,uMsg,wParam,lParam);
}
}
BOOL CIPControlDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
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); // 设置小图标
// TODO: 在此添加额外的初始化代码
CheckDlgButton(IDC_RADIO1,TRUE);
CheckDlgButton(IDC_RADIO2,FALSE);
EnableIp();
m_Addr2.m_IPAddrCtrl = &m_Addr1;
prevProc = (WNDPROC)SetWindowLong(GetDlgItem(IDC_IPADDRESS1)->m_hWnd, GWL_WNDPROC, (LONG)WinMyProc);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
void CIPControlDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CIPControlDlg::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 CIPControlDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CIPControlDlg::EnableIp()
{
m_Addr1.EnableWindow( IP_From );
m_Addr2.EnableWindow( IP_From );
m_Addr3.EnableWindow( IP_From );
}
void CIPControlDlg::OnBnClickedRadio1()
{
// TODO: 在此添加控件通知处理程序代码
IP_From = FALSE;
m_Addr1.ClearAddress();
m_Addr2.ClearAddress();
m_Addr3.ClearAddress();
EnableIp();
}
void CIPControlDlg::OnBnClickedRadio2()
{
// TODO: 在此添加控件通知处理程序代码
IP_From = TRUE;
EnableIp();
m_Addr1.SetFieldFocus(0);
}
void CIPControlDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
GetNextDlgTabItem(GetFocus())->SetFocus();
//CDialogEx::OnOK();
}
void CIPControlDlg::OnBnClickedCancel()
{
<pre name="code" class="cpp"> if (IDYES== MessageBox("是否确认修改?","确认",MB_YESNO))
{
CDialogEx::OnCancel();
}
}
#pragma once
#include "afxcmn.h"
class CSubNetMask :
public CIPAddressCtrl
{
public:
CSubNetMask(void);
~CSubNetMask(void);
public:
public:
CIPAddressCtrl *m_IPAddrCtrl;
protected:
afx_msg void OnSetfocus();
DECLARE_MESSAGE_MAP();
};
#include "StdAfx.h"
#include "SubNetMask.h"
CSubNetMask::CSubNetMask(void)
{
m_IPAddrCtrl =NULL;
}
CSubNetMask::~CSubNetMask(void)
{
}
BEGIN_MESSAGE_MAP(CSubNetMask, CIPAddressCtrl)
ON_CONTROL_REFLECT(EN_SETFOCUS,&CSubNetMask::OnSetfocus)// 手动添加消息处理函数
END_MESSAGE_MAP();
void CSubNetMask::OnSetfocus()// 手动添加消息处理函数
{
// TODO: 在此添加控件通知处理程序代码
if (m_IPAddrCtrl && IsBlank())
{
BYTE field1,field2,field3,field4;
m_IPAddrCtrl->GetAddress(field1,field2,field3,field4);
if (field1 >= 0 && field1<= 127)
{
SetAddress(255,0,0,0);
}
else if (field1 >= 128 && field1 <=191)
{
SetAddress(255,255,0,0);
}
else if (field1 >= 192 && field1 <=223)
{
SetAddress(255,255,255,0);
}
}
}