周一晚上十点整!! 完工了, 从上周五晚上做到现在。
先上一个最终效果图:
12-19周二下午更新(修复了两个bug) 一个是修改的时候队列位置的错位问题, 一个是备注信息显示错乱的问题。
另外做了一个应用的图标。
首先说说一下总结, 虽然整个程序做完了, 但是还是有很多缺陷, 比如写了很多没有用的代码。 因为很多地方都不会, 直接去百度, 找到一个解决的办法就用了, 实际上会有更优的办法。 还有就是对于类的理解不够深刻, 很多想实现的东西还是用竞赛代码的思想去实现, 真正的工程代码肯定不会是我这种代码的格式。
之前从来没接触过工程,遇到了很多问题, 所以边做边记录。 做的过程中遇到的问题我基本都写在了里面并附有解决方法和参考的博客链接, 我想许多第一次写mfc的人应该也会遇到同样的问题, 所以应该会有一些帮助吧。 仅供参考, 没办法作为教程, 写的很散乱, 步骤可能也很跳跃。 毕竟是两者同时进行的时候, 写代码为主, 写博客为辅。
工程是周五晚开始做的, 这个记录是周六上午开始写的, 所以刚开始就没什么记录。 之后都是解决一个问题写一点记录一点, 会比较详细。
周五晚上弄了一下基本框架。 我用的是纯对话框做的。 我也不知道正确的顺序是啥, 一点点来吧。 首先是框架。
添加联系人框架
然后开始搞主框架, 首先解决的左边信息的显示问题, 刚开始我用的是列边框, 也就是listbox
, 其实是错的, 应该用列表控件 listctrl。
这才是我想要的, 能够显示信息。
紧接着是解决列表控件的初始化问题, 就是想要实现打开程序后, 通讯录的自动加载以及上面的分栏。
- 解决分栏问题:在OnInitDialog()函数中添加代码:
CListCtrl* pmyListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_TOT); //获取列表控件
DWORD dwStyle = GetWindowLong(pmyListCtrl->m_hWnd, GWL_STYLE);
SetWindowLong( pmyListCtrl->m_hWnd, GWL_STYLE, dwStyle | LVS_REPORT);
DWORD styles = pmyListCtrl->GetExtendedStyle();
pmyListCtrl->SetExtendedStyle(styles|LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);//设置listctrl可以整行选择和网格条纹
CRect rect;
pmyListCtrl->GetWindowRect(&rect);
m_list.InsertColumn(0,"姓名",LVCFMT_CENTER,rect.Width()/6);//设置标题
m_list.InsertColumn(1,"电话",LVCFMT_CENTER,rect.Width()/6*2);
m_list.InsertColumn(2,"邮箱",LVCFMT_CENTER,rect.Width()/6*2);
m_list.InsertColumn(3,"分组",LVCFMT_CENTER,rect.Width()/6);
z这样列表的设置问题就解决了, 发现了一个博客, 里面写的很详细, listctrl总结。 顺便搞了一下在控制窗口的输出。最后用读文件应该也是用这种方法显示。
int nRow = m_list.InsertItem(0, "严嘉豪");// 插入行
m_list.InsertItem(1, "谭瑞");// 插入行
m_list.InsertItem(2, "马博");
m_list.SetItemText(nRow, 1, "110");// 设置其它列数据
m_list.SetItemText(nRow, 2, "87*****@qq.om");
m_list.SetItemText(nRow, 3, "无");
刚刚发现自己新建的窗口不对, 昨天创建的是非模式对话框(多个窗口是活动的), 但是我是想创建模式对话框(只有当前窗口是活动的)。
完全删除类的方法:
MFC Wizard中创建的类用类向导删除后并不能删除源文件,可以通过以下简单的几步完全删除:
1:从workspace中的fileview中删除对应的.h和.cpp文件。
2.再关闭项目,从实际的文件夹中删除对应的.h和.cpp文件。
3.再删除.clw文件。
再打开重新编译即可!
重新解决了添加按钮的对话框模式, 首先在资源视图的Dialog右键添加Dialog, 新建的窗口自己命名并且设置ID, 利用class wizard 自动添加类, 在主窗口#include”ADD.h” 包含文件, 在添加按钮中加入代码
void CZbookDlg::OnButtonAdd()
{
ADD dlg;
dlg.DoModal();
// TODO: Add your control notification handler code here
}
实现点击添加联系人按钮打开新的添加窗口。忙活了半天, 基本上是把昨晚上做的翻工了一遍。。。
半个小时又过去了, 基本的框架都已经想好了。主界面大概是这个样子:
有两个子窗口, 一个是添加联系人窗口, 一个是修改联系人窗口, 并且这两个窗口的布局基本相同。
机房吹空调好干啊。。 出去买点喝的透透气,回来继续写。
解决了分组下拉栏问题。 在里面添加了5个分组:全部、无、家人、同事、朋友。
方法是右键打开组合框(下拉栏)属性, 在,在数据栏添加初始数据, 用ctrl +enter换行, 取消分类, 不然系统会自动排序。 然后在initDialog() 函数初始化默认选项, 因为我把全部排在第一个, 所以下标是0。 添加代码
m_Flist.SetCurSel(0);//初始化分组选择下拉栏的默认选项。
效果如下
同理, 给添加联系人窗口和修改联系人窗口的下拉栏也初始化, 当然要注意的是, 没有了全部分组, 因为全部分组是查找的时候用的。
对于添加对话框的初始化函数, 需要用class wizard 来添加。 详见这里
因为删除函数需要读取选中的信息,现在我还没有初始化, 先不动修改和删除这两个模块。
CString strtemp;
//strtemp.Format("单击的是第%d行第%d列", lvinfo.iItem, lvinfo.iSubItem);
strtemp.Format("bababalalal");
用来测试程序用的弹窗代码。
实现:在主窗口, 当没有选择联系人的时候, 无法点击删除和修改按钮, 当选中联系人后, 按钮可以点击。
效果:
。
添加方法:
首先给listctrl添加NM_CLICK消息函数(用class wizard) 在对应的函数里编辑:
DWORD dwPos = GetMessagePos();//通过查看点击位置 确定修改和删除按钮是否可点。
CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
m_list.ScreenToClient(&point);
LVHITTESTINFO lvinfo;
lvinfo.pt = point;
lvinfo.flags = LVHT_ABOVE;
int nItem = m_list.SubItemHitTest(&lvinfo);
if(nItem != -1)// 选择了联系人
{
GetDlgItem(IDC_BUTTON_DEL)-> EnableWindow(TRUE);// 删除窗口可以点击 (传入的是按钮控件的ID)
GetDlgItem(IDC_BUTTON_CHANGE)-> EnableWindow(TRUE); 修改按钮
}
else{// 没有选择联系人, 两个按钮变灰(不可按)
GetDlgItem(IDC_BUTTON_DEL)-> EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_CHANGE)-> EnableWindow(FALSE);
}
listctrl 总结网站: CListCtrl控件使用方法总结
今天到这了, 晚上要打cf, 估计写不了了。
晚上一直在想数据到底该如何存放以及如何访问, 第一个问题是在add子窗口添加完信息后, 主窗口如何得到子窗口的变量以及如何在listctrl上显示出来, 发现一个人写的程序跟我的思路极其相似并且遇到了同样的问题, 如何实现MFC主对话框和子对话框之间数据的传递。 明天去机房试试。
还有一个博客, 写到很多方法, 全局变量啊什么的, 没有上一个理解的透彻, 先存着。 MFC不同对话框使用公共数据的几种方法。
小知识点:
UpdateData(TRUE)是将控件的状态传给其关联的变量,当然你要为控件关联上变量才行。
UpdateData(FALSE)是将控件的关联变量的值传给控件并改变控件状态。
UpdateData刷新的是当前对话框。
UpdateData(true);//用于将屏幕上控件中的数据交换到变量中。
UpdateData(false);//用于将数据在屏幕中对应控件中显示出来。
刚刚看了一下数据传输, 突然懂了点什么, 感觉解决了添加和修改的问题。 看的是这个博客mfc对话框传递数据。
数据传递分两类。
1. 父窗口传到子窗口。
2. 子窗口传到父窗口。
对于我的程序来说, 添加按钮需要的是子窗口传到父窗口。 修改按钮两个都需要, (首先通过父传子获得需要修改的信息方便显示, 然后再将修改之后的传回来)。 应该这样。
每一次修改/删除/添加 都需要更新listctrl 并且保存到文件?? 应该是这样吧, 这一部分还没想好, 具体的明天再百度一下。
睡觉, 明天白天继续赶工。
今天是周天。
早晨解决了add子窗口向主窗口传值的问题, 首先是在主窗口的添加按钮中添加函数。
void CZbookDlg::OnButtonAdd()
{
ADD dlg;
if(IDOK == dlg.DoModal()){
Myclass new_people;// 如果激活子窗口, 获取相关信息
new_people.Name = dlg.m_Aname;
new_people.Number = dlg.m_Atel;
new_people.E_mail = dlg.m_Amail;
new_people.Information = dlg.m_Ainfo;
new_people.group_index = dlg.addgroup_index;
/*
CString check; //检查数据获取
check.Format("%d", new_people.group_index);
MessageBox(check);
*/
}
// TODO: Add your control notification handler code here
}
如果子窗口正常关闭, 则获取子窗口中输入的信息。 然后是在子窗口的却定按钮中添加函数。
void ADD::OnAdd()
{
UpdateData(TRUE);
Myclass st;
if(m_Aname.IsEmpty()||m_Atel.IsEmpty()||m_Amail.IsEmpty()){
MessageBox("添加联系人信息不能为空");
}
else{
addgroup_index = m_addgroup.GetCurSel();
CDialog::OnOK();
}
// TODO: Add your control notification handler code here
}
判断联系人是否为空, 如果不为空 正常关闭对话框。 我新建了一个变量addgroup_index
是为了获取分组的下标, 我想以下标的形式在数据间传递,而不是字符串, 但是直接在主窗口的部分好像没有办法获取, 所以我直接在子窗口中添加了一个变量, 然后间接地获取这个下标。
通过调用listctrl的函数, 现在已经能够在列表显示添加内容了。为了方便数据的共享, 我开了一个全局数组用来存放所有人的信息。 具体方法是在 stdAfx.h
中声明为 extern
类型, 然后在 stdAfx.cpp
中初始化。 就成为全局变量了。 当然别忘了挂上 #include "Myclass.h"
stdAfx.h:
extern int m_tot;// 总人数
extern Myclass People[1005]; 每个人的信息
stdAfx.cpp:
int m_tot;
Myclass People[1005];
现在我的 添加按钮函数是这样的:
void CZbookDlg::OnButtonAdd()
{
ADD dlg;
if(IDOK == dlg.DoModal()){
m_tot++;
People[m_tot].Name = dlg.m_Aname;
People[m_tot].Number = dlg.m_Atel;
People[m_tot].E_mail = dlg.m_Amail;
People[m_tot].Information = dlg.m_Ainfo;
People[m_tot].group_index = dlg.addgroup_index;
m_Flist.GetLBText(People[m_tot].group_index+1, People[m_tot].Group); // 为了显示分组
/*
CString check; //检查数据获取
check.Format("%d", new_people.group_index);
MessageBox(check);
*/
int nHeadNum = m_list.GetItemCount();//获取当前行数
m_list.InsertItem(nHeadNum, People[m_tot].Name);
m_list.SetItemText(nHeadNum, 1, People[m_tot].Number);
m_list.SetItemText(nHeadNum, 2, People[m_tot].E_mail);
m_list.SetItemText(nHeadNum, 3, People[m_tot].Group);
//m_Info.SetWindowText(new_people.Information);
}
// TODO: Add your control notification handler code here
}
目前效果是这样:
接下来想要实现的是在主窗口选中一个人后, 下面备注会显示相关信息,并且是只读属性, 无法修改。
首先修改只读属性, 在编辑框直接右键 属性, 只读。
然后在listctrl的 click
函数中添加两行代码
m_Info.SetWindowText(People[lvinfo.iItem+1].Information);// 在备注栏显示相关信息
UpdateData(FALSE); // 更新编辑框
刚刚突然发现一个问题, 就是下拉栏的类型应该设置为下拉列表, 才能保证是选择框, 不然的话会变成可选择的编辑框了。
12点了 一上午又过去了。
发现一个需要修改的地方, 就是备注的编辑框, 需要设置成自动换行 方法:
1.新建一个编辑框控件(Edit Control),将其多行(Multiline)前面打勾(属性设置为True),Auto HScroll前面的勾去掉(属性设置False),这样就可以实现每一行填满后自动换行了。
2.再将垂直滚动条(Vetrical Scroll)前面打勾(属性设置为True),当输入或显示超过编辑框的大小后就会出现垂直滚动条。
改了一些字符串的东西: 定义的全局变量要用char 数组, 不能用CString 不然的话后面存到文件里很难弄, 改成char之后, 就要处理Cstring 转char 和读入空格的问题, 用
a.TrimLeft();a.TrimRight(); 可以消除CString类型 左右的空格, 中间的消不掉。
首先重新修改了添加联系人判空的方法: 在ADD窗口的add按键中, 先取出每个编辑框的字符串, 然后去除左右的空格, 如果为空则为非法输入, 需要弹窗提示。
void ADD::OnAdd()
{
UpdateData(TRUE);
bool flag = true;
CString str;
str = m_Aname; // 处理空信息
str.TrimLeft(); str.TrimRight();
if(str.IsEmpty()) flag = false;
str = m_Atel;
str.TrimLeft(); str.TrimRight();
if(str.IsEmpty()) flag = false;
str= m_Amail;
str.TrimLeft(); str.TrimRight();
if(str.IsEmpty()) flag = false;
if(!flag) { MessageBox("添加联系人信息不能为空"); }
else{
addgroup_index = m_addgroup.GetCurSel();
CDialog::OnOK();
}
// TODO: Add your control notification handler code here
}
如果输入合法, 则需要把相关信息放到数组中, 具体方法是在主窗口的添加 按钮处理。
void CZbookDlg::OnButtonAdd()
{
ADD dlg;
if(IDOK == dlg.DoModal()){
m_tot++;
int i;
CString str;
int nHeadNum = m_list.GetItemCount();//获取当前行数 添加联系人到列表
str = dlg.m_Aname;//姓名
str.TrimLeft(); str.TrimRight();
m_list.InsertItem(nHeadNum, str);
for(i = 0; i < str.GetLength(); i++) People[m_tot].Name[i] = str[i];
People[m_tot].Name[str.GetLength()] = '\0';
str = dlg.m_Atel;//电话
str.TrimLeft(); str.TrimRight();
m_list.SetItemText(nHeadNum, 1, str);
for(i = 0; i < str.GetLength(); i++) People[m_tot].Number[i] = str[i];
People[m_tot].Number[str.GetLength()] = '\0';
str = dlg.m_Amail;//邮箱
str.TrimLeft(); str.TrimRight();
m_list.SetItemText(nHeadNum, 2, str);
for(i = 0; i < str.GetLength(); i++) People[m_tot].E_mail[i] = str[i];
People[m_tot].E_mail[str.GetLength()] = '\0';
str = dlg.m_Ainfo;//备注
for(i = 0; i < str.GetLength(); i++) People[m_tot].Information[i] = str[i];
People[m_tot].Information[str.GetLength()] = '\0';
People[m_tot].group_index = dlg.addgroup_index;// 分组
m_Flist.GetLBText(People[m_tot].group_index+1, People[m_tot].Group); // 为了显示分组
m_list.SetItemText(nHeadNum, 3, People[m_tot].Group);
/*
CString check; //检查数据获取
check.Format("%d", new_people.group_index);
MessageBox(check);
*/
}
// TODO: Add your control notification handler code here
}
暂时添加模块没有问题了, 开始搞删除模块。
首先还是用 class wizard
给del按钮添加click消息函数,
思路: 首先要选中一个列表联系人, 然后删除框才可以点击, 把电机的行数传给删除窗口, 再改变数组。
百度了一下, 发现一个函数可以很智能的删除列表选中的选项, 并且返回行数, 这样即解决了列表的删除, 也能解决数组中删除。
参考的博客: CListCtrl删除选中行。 我用的是方法三。
代码如下:
void CZbookDlg::OnButtonDel()
{
CString check;
int nItem;
// TODO: Add your control notification handler code here
while(m_list.GetNextItem(-1,LVNI_ALL | LVNI_SELECTED) != -1){
nItem = m_list.GetNextItem(-1,LVNI_ALL | LVNI_SELECTED);
m_list.DeleteItem(nItem);
int pos = nItem + 1;
for(int i = pos; i < m_tot; i++){
strcpy(People[i].Name, People[i+1].Name);
strcpy(People[i].Number, People[i+1].Number);
strcpy(People[i].E_mail, People[i+1].E_mail);
strcpy(People[i].Information, People[i+1].Information);
People[i].group_index = People[i+1].group_index;
}
m_t
}
/*
check.Format("%d", nItem);
MessageBox(check);
*/
ot--;
//因为点击了一下修改按钮之后, 列表的选择就没了, 我想让修改和查询按钮再次不可点。 所以再加两句代码。
}
这样删除功能基本实现了, 开始搞修改功能。
修改功能首先要做的就是选中列表栏信息后, 点击删除, 会把相应的信息读入到删除窗口中, 这个问题弄了好一会。
首先是在主窗口的删除按钮添加代码:
void CZbookDlg::OnButtonChange()
{
Mod dlg;
int nlist = m_list.GetNextItem(-1, LVNI_SELECTED)+1; //获得列表框选择的位置
dlg.m_Mname.Format("%s", People[nlist].Name);
dlg.m_Mtel.Format("%s", People[nlist].Number);
dlg.m_Mmail.Format("%s", People[nlist].E_mail);
dlg.m_Minfo.Format("%s", People[nlist].Information);
dlg.m_Mgroup_index = People[nlist].group_index;
dlg.DoModal();
GetDlgItem(IDC_BUTTON_DEL)-> EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_CHANGE)-> EnableWindow(FALSE);
}
这样就把除了分组之外的信息都传到了删除窗口里, 为什么是除了分组信息呢, 因为分组是个下拉栏, 我在删除类里新建了一个变量 m_Mgroup_index
记录对应的标号, 在删除模块中添加init预处理函数, 在预处理函数中才能修改下拉栏默认输出项, 否则会一直程序出错。
BOOL Mod::OnInitDialog()
{
CDialog::OnInitDialog();
m_Mgroup.SetCurSel(m_Mgroup_index);//初始化分组选择下拉栏的默认选项。 一定要写下主窗口初始化的下面。
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
实现了修改功能, 基本上和添加很相似, 唯一不一样的就是要在列表框先删除当前这一行的信息, 然后再重新写入。
和添加的函数基本雷同
void CZbookDlg::OnButtonChange()
{
Mod dlg;
int nlist = m_list.GetNextItem(-1, LVNI_SELECTED); //获得列表框选择的位置
dlg.m_Mname.Format("%s", People[nlist].Name);
dlg.m_Mtel.Format("%s", People[nlist].Number);
dlg.m_Mmail.Format("%s", People[nlist].E_mail);
dlg.m_Minfo.Format("%s", People[nlist].Information);
dlg.m_Mgroup_index = People[nlist].group_index;
if(IDOK == dlg.DoModal()){
m_list.DeleteItem(nlist);
CString str; int i;
str = dlg.m_Mname;//姓名
str.TrimLeft(); str.TrimRight();
m_list.InsertItem(nlist, str);
for(i = 0; i < str.GetLength(); i++) People[nlist].Name[i] = str[i];
People[nlist].Name[str.GetLength()] = '\0';
str = dlg.m_Mtel;// 电话
str.TrimLeft(); str.TrimRight();
m_list.SetItemText(nlist, 1, str);
for(i = 0; i < str.GetLength(); i++) People[nlist].Number[i] = str[i];
People[nlist].Number[str.GetLength()] = '\0';
str = dlg.m_Mmail;// 电话
str.TrimLeft(); str.TrimRight();
m_list.SetItemText(nlist, 2, str);
for(i = 0; i < str.GetLength(); i++) People[nlist].E_mail[i] = str[i];
People[nlist].E_mail[str.GetLength()] = '\0';
str = dlg.m_Minfo;// 备注
str.TrimLeft(); str.TrimRight();
for(i = 0; i < str.GetLength(); i++) People[nlist].Information[i] = str[i];
People[nlist].Information[str.GetLength()] = '\0';
People[nlist].group_index = dlg.m_Mgroup_index;// 分组
if(dlg.m_Mgroup_index == 0) str = "无";
else if(dlg.m_Mgroup_index == 1) str = "家人";
else if(dlg.m_Mgroup_index == 2) str = "同事";
else if(dlg.m_Mgroup_index == 3) str = "朋友";
for(i = 0; i < str.GetLength(); i++) People[nlist].Group[i] = str[i];
People[nlist].Group[str.GetLength()] = '\0';
m_list.SetItemText(nlist, 3, str);
}
GetDlgItem(IDC_BUTTON_DEL)-> EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_CHANGE)-> EnableWindow(FALSE);
}
Demo:
现在基本完成了三大操作, 添加 修改 删除。
还没有实现的:
1. 新建的时候判断当前电话是否已经存在, 若存在, 则不能添加。
2. 文件的读写保存。
3. 查询功能。
1问题解决, 一个check函数的问题, O(n)检查一遍就可以。 我在Myclass累里写了一个checkIn函数, 然后调用People[0].checkIn。
我决定先搞查询。。 最后在弄文件。
就在我决定去解决查询问题的时候,忽然发现一个细节(bug) 就是如果在修改联系人的时候修改出一个之前已经存在的电话, 也需要check一下。 所以在修改部分也需要添加checkIn函数。
花了好长时间, 解决了查找的第一部分问题, 最简单的查找, 首先是允许查找的情况 只要不是姓名空&& 电话空&& (分组==全部)都可以查找, 查找的时候, 就是一些判断, 我开了一个全局的数组模拟队列把符合的下标都存起来并记录总个数。
代码:
void CZbookDlg::OnButtonFind()
{
UpdateData(TRUE);
Myclass Sfind;
CString str; int i;
bool ok1 = false, ok2 = false, ok3 = false;
str = m_Fname;
str.TrimLeft(); str.TrimRight();
for(i = 0; i < str.GetLength(); i++) Sfind.Name[i] = str[i];
Sfind.Name[str.GetLength()] = '\0';
if(str.IsEmpty()) ok1 = true;
str = m_Ftel;
str.TrimLeft(); str.TrimRight();
for(i = 0; i < str.GetLength(); i++) Sfind.Number[i] = str[i];
Sfind.Number[str.GetLength()] = '\0';
if(str.IsEmpty()) ok2 = true;
Sfind.group_index = m_Flist.GetCurSel();
if(Sfind.group_index == 0) ok3 = true;
if(ok1 && ok2 && ok3){
MessageBox("请填写查找信息");
}
else{
que[0] = 0;
CString s1, s2;
CString check;
for(int i = 0; i <= m_tot; i++){
if(Sfind.group_index != 0 && Sfind.group_index != (People[i].group_index+1)) continue;
s1.Format("%s", Sfind.Name); s2.Format("%s", People[i].Name);
check = s1 + " " + s2;
if(!s1.IsEmpty() && s2.Find(s1) == -1) continue;
s1.Format("%s", Sfind.Number); s2.Format("%s", People[i].Number);
if(!s1.IsEmpty() && s2.Find(s1) == -1) continue;
que[++que[0]] = i;
}
check.Format("共查到%d条联系人", que[0]);
MessageBox(check);
}
// TODO: Add your control notification handler code here
}
void CZbookDlg::OnButtonPre()
{
if(m_nowpos == 1) {
MessageBox("toutou");
}else{
m_list.SetItemState(que[m_nowpos--], 0, LVIS_SELECTED|LVIS_FOCUSED);
m_list.SetItemState(que[m_nowpos], LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
}
// TODO: Add your control notification handler code here
}
void CZbookDlg::OnButtonNxt()
{
if(m_nowpos == que[0]){
MessageBox("weiwei");
}
else{
m_list.SetItemState(que[m_nowpos++], 0, LVIS_SELECTED|LVIS_FOCUSED);
m_list.SetItemState(que[m_nowpos], LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
}
// TODO: Add your control notification handler code here
}
跟计划的差了一些, 计划是周天晚上就搞定的, 果然还是有许多问题。 到目前, 基本功能都已经完成了, 但还是有许多细节没有处理。
目前最大的一个问题就是 Clistctrl失焦问题,什么事失焦, 就是当鼠标点击clistctrl列表时, 被选中的信息有高亮, 当点到外边(或者别的按钮)的时候, 就没有高亮了。 上网找了很多解决办法, 都很难, 不是很容易实现。 又要拖到明天了。。
还有文件的读写问题。。 惨
又搜了几个帖子, 发现一个可行的正解, 收一下 如何让CListCtrl选中行恒保持其蓝色高亮状态?。 或者我再想想有没有更好的查询交互方法。
今天就只能完成这么多了
周一早晨
决定改一下查找的方式, 变成根据关键字查找之后把所有符合条件的都显示在列表框。
这要的话就要解决这几个问题:
1. 添加联系人的时候, 如果当前列表框是全部列表框, 则直接添加, 否则就会清空当前的筛选然后直接列出全部联系人, 在添加最新的。 或者根本不考虑是不是全部列表,直接暴力全部删除再全部添加。
2. 去掉上一个 下一个按钮。
3. 优化修改和删除按钮可点击情况。
今晚填补了之前挖的坑, 先说一下解决方案把, 修改了查询的显示结果, 直接让结果显示在列表栏里, 这样的话就方便的做到根据姓名+电话+分组查询的同时修改+查询。
方法是用之前的队列始终维护列表里有那些人(存实际数组中的下标)。
当添加联系人的时候, 直接刷新显示全部联系人, 其他情况没有影响。
查询的时候, 队列清空, 重新查询, 满足的放到队列里, 然后输出到列表上。
删除和修改的时候, 获得的是列表中的下标, 通过队列能够查询到在People数组中的实际下标, 进而实现修改和删除。
大的bug也调完了, 小的bug发现了再调。
上一个效果图:
最后, 要解决的问题就是数据的文件写入保存问题。
我是用CStdioFile 形式实现读写的文件。
具体实现方法: 保存: 首先多加了一个保存按钮:
void CZbookDlg::OnButtonSave()
{
CStdioFile file;
file.Open("test.txt",CFile::modeCreate|CFile::modeWrite);
CString str;
for(int i = 0; i <= m_tot; i++){
str.Format("%s\n", People[i].Name);
file.WriteString(str);
str.Format("%s\n", People[i].Number);
file.WriteString(str);
str.Format("%s\n", People[i].E_mail);
file.WriteString(str);
str.Format("%d\n", People[i].group_index);
file.WriteString(str);
str.Format("%s\n", People[i].Information);
file.WriteString(str);
}
MessageBox("保存成功");
// TODO: Add your control notification handler code here
}
然后在Myclass 写了一个初始读入的函数
void Myclass::Readbook()
{
CStdioFile file;
CString str;
file.Open("test.txt", CFile::modeRead);
int now = 0;
while (file.ReadString(str) != NULL)
{
int i;
if(now == 0) {
m_tot++;
for(i = 0; i < str.GetLength(); i++) People[m_tot].Name[i] = str[i];
People[m_tot].Name[str.GetLength()] = '\0';
}
else if(now == 1){
for(i = 0; i < str.GetLength(); i++) People[m_tot].Number[i] = str[i];
People[m_tot].Number[str.GetLength()] = '\0';
}
else if(now == 2){
for(i = 0; i < str.GetLength(); i++) People[m_tot].E_mail[i] = str[i];
People[m_tot].E_mail[str.GetLength()] = '\0';
}
else if(now == 3){
int x = People[m_tot].group_index = _ttoi(str);
if(x == 0) strcpy(People[m_tot].Group, "无");
else if(x == 1) strcpy(People[m_tot].Group, "家人");
else if(x == 2) strcpy(People[m_tot].Group, "同事");
else if(x == 3) strcpy(People[m_tot].Group, "朋友");
}
else if(now == 4){
for(i = 0; i < str.GetLength(); i++) People[m_tot].Information[i] = str[i];
People[m_tot].Information[str.GetLength()] = '\0';
}
now++; now %= 5;
}
}
在主窗口初始化的时候调用这个函数就行了。
感受, 用CString 和 char* 转来转去的感觉自己做麻烦了, 如果直接用CString储存的话代码肯定会短很多, 但是不知道会不会有错。
其他地方基本满意, 特别是查询模块换了思路之后, 可以支持修改和删除, 更符合真正意义上的程序。
之后还会再修复bug, 可能还会做一些美化,(如果有时间的话), 毕竟期末了 其他科也需要复习。