第三课、
一、客户端界面的设计和编写
1.3 列表的列宽度支持伸缩
1.CListCtrl SetColumnWidth 查看MSDN
BOOL SetColumnWidth(
int nCol, //列索引
int cx //列宽度
);
2.在Dlg的CPP文件开始声明列总宽度变量:
int g_Column_Online_Width=0; //初始化上线列总宽度
int g_Column_Message_Width=0; //初始化日志列总宽度
3. 在initlist函数中计算列的总宽度(红色部分):
int CVeryEvilDlg::InitList(void)
{
//初始化上线列表控件
for (int i = 0; i < g_Column_Count_Online; i++)
{
m_CList_Online.InsertColumn(i,g_Column_Data_Online[i].title,LVCFMT_CENTER,g_Column_Data_Online[i].nWidth);
g_Column_Online_Width+=g_Column_Data_Online[i].nWidth; //得到总宽度
}
//初始化日志列表控件
for (int i = 0; i < g_Column_Count_Message; i++)
{
m_CList_Message.InsertColumn(i,g_Column_Data_Message[i].title,LVCFMT_CENTER,g_Column_Data_Message[i].nWidth);
g_Column_Message_Width+=g_Column_Data_Message[i].nWidth; //得到总宽度
}
return 0;
}
4.在OnSize函数中添加安百分比调整当前列的宽度代码(红色部分):
void CVeryEvilDlg::OnSize(UINT nType, int cx, int cy)
{//cx代表改变大小后的整个界面的宽度,cy代表改变大小后的整个界面的高度
CDialogEx::OnSize(nType, cx, cy);
double dcx=cx; //保存对话框的总宽度
if (m_CList_Online.m_hWnd!=NULL)
{//判断控件变量是否和一个窗口句柄发生关联(如果关联则说明控件已经正确显示),避免出错
CRect rc;
rc.left=1; //列表的左上角的X坐标
rc.top=80; //列表的左上角的Y坐标
rc.right=cx-1; //列表的右下角的X坐标
rc.bottom=cy-160; //列表的右下角的Y坐标
m_CList_Online.MoveWindow(rc);//移动控件窗口到新位置
//按照以前的百分比重新计算每列的宽度并设置新的宽度
for(int i=0;i<g_Column_Count_Online;i++){ //遍历每一个列
double dd=g_Column_Data_Online[i].nWidth; //得到当前列的宽度
dd/=g_Column_Online_Width; //看一看当前宽度占总长度的几分之几
dd*=dcx; //用原来的长度乘以所占的几分之几得到当前的宽度
int lenth=(int)dd; //转换为int 类型
m_CList_Online.SetColumnWidth(i,(lenth)); //设置当前的宽度
}
}
if (m_CList_Message.m_hWnd!=NULL)
{//判断控件变量是否和一个窗口句柄发生关联(如果关联则说明控件已经正确显示),避免出错
CRect rc;
rc.left=1; //列表的左坐标
rc.top=cy-156; //列表的上坐标
rc.right=cx-1; //列表的右坐标
rc.bottom=cy-6; //列表的下坐标
m_CList_Message.MoveWindow(rc);
//按照以前的百分比重新计算每列的宽度并设置新的宽度
for(int i=0;i<g_Column_Count_Message;i++){ //遍历每一个列
double dd=g_Column_Data_Message[i].nWidth; //得到当前列的宽度
dd/=g_Column_Message_Width; //看一看当前宽度占总长度的几分之几
dd*=dcx; //用原来的长度乘以所占的几分之几得到当前的宽度
int lenth=(int)dd; //转换为int 类型
m_CList_Message.SetColumnWidth(i,(lenth)); //设置当前的宽度
}
}
}
5.注意这句
double dcx=cx; //保存对话框的总宽度
是为了保证精确度最少的丢失
注意:手动调整对话框界面的大小,使所有列的宽度和等于对话框的长度
第四课、客户端界面的设计和编写
1.4 列表中添加条目
用到的函数参数说明:
1.CListCtrl InsertItem 插入条目
int InsertItem(
int nItem, //插入哪一行
LPCTSTR lpszItem //该行0列显示的字符
);
SetItemText 设置哪个列的字符
BOOL SetItemText(
int nItem, //改动那个行
int nSubItem, //该行中那个子列
LPCTSTR lpszText //要设置的字符
);
2.列表设计思路:
(1)服务端上线后要显示在列表中,这样有一个统一的函数来处理会使代码更加简洁。
(2)消息显示分为成功失败两种,还要在其中显示消息产生的时间,这样也应该有一个统一的函数来处理。
3.上线列表添加处理:
void CVeryEvilDlg::AddList(CString strIP, CString strAddr, CString strPCName, CString strOS, CString strCPU, CString strVideo, CString strPing)
{//当有主机上线是调用此函数
m_CList_Online.InsertItem(0,strIP); //默认为0行 这样所有插入的新列都在最上面
m_CList_Online.SetItemText(0,ONLINELIST_ADDR,strAddr); //设置列的显示字符 这里 ONLINELIST_ADDR等 为第二节课中的枚举类型 用这样的方法
m_CList_Online.SetItemText(0,ONLINELIST_COMPUTER_NAME,strPCName); //解决问题会避免以后扩展时的冲突
m_CList_Online.SetItemText(0,ONLINELIST_OS,strOS);
m_CList_Online.SetItemText(0,ONLINELIST_CPU,strCPU);
m_CList_Online.SetItemText(0,ONLINELIST_VIDEO,strVideo);
m_CList_Online.SetItemText(0,ONLINELIST_PING,strPing);
}
4.添加日志消息的处理:
void CVeryEvilDlg::ShowMessage(bool bIsOK, CString strMsg)
{//当有消息需要在日志控件列表显示时是调用
CString strIsOK,strTime;
CTime t=CTime::GetCurrentTime();
strTime=t.Format("%H:%M:%S");
if (bIsOK)
{
strIsOK="执行成功";
}else{
strIsOK="执行失败";
}
m_CList_Message.InsertItem(0,strIsOK);
m_CList_Message.SetItemText(0,1,strTime);
m_CList_Message.SetItemText(0,2,strMsg);
}
5.添加伪上线,和日志的测试代码,在没有加入gh0st传输内核之前是要自己测试的,所以要加入一个用于测试的函数:
void CVeryEvilDlg::Test(void)
{
AddList("192.168.0.1","本机局域网","Lang","Windows7","2.2GHZ","有","123232");
ShowMessage(true,"软件初始化成功...");
}
然后在OnInitDialog 中添加调用:
Test();
6.设置两个列表空件点击时整个列都是选中状态样式
在InitList()函数中加入代码:
m_CList_Online.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); //上线控件列表额外设置网格样式
m_CList_Message.SetExtendedStyle(LVS_EX_FULLROWSELECT);
第五课、客户端界面的设计和编写
1.5 列表中显示弹出菜单
1.添加菜单资源
2.功能菜单项并写入相应的菜单ID
3.显示菜单
(1)参考gh0st代码,看一下gh0st是怎样显示菜单的
a.找到主菜单
b.查看主菜单的ID
c.搜索IDR_LIST
d.分析代码:
CMenu popup; //声明一个菜单变量
popup.LoadMenu(IDR_LIST); //载入菜单资源
CMenu* pM = popup.GetSubMenu(0); //得到菜单项
CPoint p;
GetCursorPos(&p); //得到鼠标指针的位置
int count = pM->GetMenuItemCount(); //得到菜单的个数
if (m_pListCtrl->GetSelectedCount() == 0) //如果没有选中列表中的条目
{
for (int i = 0; i < count - 2; i++) //遍历每一个菜单
{
pM->EnableMenuItem(i, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); //该项变灰
}
pM->EnableMenuItem(count - 1, MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
}
// 全选
if (m_pListCtrl->GetItemCount() > 0) //列表中的条目项大于0
pM->EnableMenuItem(count - 2, MF_BYPOSITION | MF_ENABLED); //激活倒数第二个菜单
else
pM->EnableMenuItem(count - 2, MF_BYPOSITION | MF_DISABLED | MF_GRAYED); //否则变灰
pM->TrackPopupMenu(TPM_LEFTALIGN, p.x, p.y, this); //在指定位置显示菜单
}
(2)添加我们的代码
为线上控件列表添加NM_CLICK消息响应函数,并添加如下处理代码
void CVeryEvilDlg::OnNMRClickOnline(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
CMenu popup;
popup.LoadMenu(IDR_MENU_ONLINE);
CMenu* pM = popup.GetSubMenu(0);//获取菜单项
CPoint p;
GetCursorPos(&p);//获取当前鼠标坐标
int count = pM->GetMenuItemCount();//获取菜单项目条数
if (m_CList_Online.GetSelectedCount() == 0) //如果没有选中项目(空白区域)
{
for (int i = 0; i < count; i++)
{//遍历菜单项目逐个变灰
pM->EnableMenuItem(i, MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
}
}
pM->TrackPopupMenu(TPM_LEFTALIGN, p.x, p.y, this);
*pResult = 0;
}