大体思路
1.系统化托盘图标。先搞一个父窗口,把最小化图标做出来,做一个MENU绑到托盘菜单添加消息相应
2.便签的主体。再搞一个窗口,由父窗口新建或读取配置时调用弹出,运行程序时根据配置文件设置窗口
3.配置文件的读写
4.整合为完整程序
1.系统化托盘图标
(1)最小化图标
主要用到NOTIFYICONDATA和Shell_NotifyIcon。
先搞一个NOTIFYICONDATA指针,把cbSize(按字节计算的结构体)hWnd(接收通知区域消息的窗口句柄)。hIcon(将要添加/修改/删除的图标句柄)szTip(一个标准提示字符串)。uCallbackMessage 当点击图标,对图标操作时向指定窗口发送消息,等成员赋值
然后用Shell_NotifyIcon系统化托盘图标BOOL Shell_NotifyIcon(
DWORD dwMessage,
PNOTIFYICONDATA lpdata
);
dwMessage:[输入参数] 说明要执行的动作。比如:NIM_ADD 增加一个图标到托盘区
lpdata:[输入参数] 一个指向NOTIFYICONDATA结构的指针。
代码:
NOTIFYICONDATA minicon;
minicon.cbSize=sizeof(NOTIFYICONDATA);
minicon.hIcon=m_hIcon;
minicon.hWnd=m_hWnd;
lstrcpy(minicon.szTip,_T("便利贴"));
minicon.uCallbackMessage = NOTIFY1;
minicon.uFlags=NIF_ICON | NIF_MESSAGE | NIF_TIP;//
Shell_NotifyIcon(NIM_ADD,&minicon);
(2)主窗口隐藏ShowWindow(SW_HIDE);
(3)托盘添加消息响应
先在资源文件中添加一个MENU,写上自己的功能菜单名
添加消息响应函数afx_msg LRESULT CMyDlg::OnIconNotify(WPARAM wParam, LPARAM lParam)
把MENU添加进去
代码
CMenu menu;
menu.LoadMenu(IDR_MENU1);
CPoint point;
GetCursorPos(&point);
SetForegroundWindow();
menu.GetSubMenu(0)->TrackPopupMenu(
TPM_LEFTBUTTON|TPM_RIGHTBUTTON,
point.x, point.y, this, NULL);
后面添加MENU方法都一样
到此第一步已经完成,效果就是运行程序后只有系统托盘图标,点击会弹出自己画的菜单。
菜单中的功能应为要有便利贴参与,所以在做好便利贴主体后再实现。其中退出可以先给MENU添加事件PostQuitMessage(0);
2.便签主体
(更改背景,字体颜色,设置标题,删除、隐藏便签)
新建一个dialog(模态对话框)控件只有一个编辑框,放大到dialog一样。给对话框添加一个类
类比1再搞个MENU,绑在新的dialog上,并给各个菜单选项添加事件
(1)新建dialog初始化背景字体颜色,字体大小。
定义一个CBRUSH cb(画刷) CFONT cf(字体),自己先给定初始化值,编辑框添加控件变量(不是VALUE)
COLORREF backcolor;//背景颜色
COLORREF fontcolor;//字体颜色
代码
backcolor=RGB(255,255,0);
fontcolor=RGB(0,0,255);
cb.CreateSolidBrush(backcolor);
cf.CreatePointFont(200,_T("黑体"));
eedit.SetFont(&cf);
添加OnCtlColor消息,添加方法:右键点击dialog->类向导->消息,找到OnCtlColor添加消息
代码
HBRUSH dialog1::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: 在此更改 DC 的任何特性
if ( pWnd->GetDlgCtrlID() == IDC_EDIT1){
pDC->SetTextColor(fontcolor);
pDC->SetBkColor(backcolor);
pDC->SelectObject(&cb);
hbr = (HBRUSH)cb;
}
// TODO: 如果默认的不是所需画笔,则返回另一个画笔
return hbr;
}
此时新建dialog时,弹出的对话框就是已经有背景颜色且字体类型颜色也被更改。
(2)调用选色板更改背景颜色,字体颜色更改类似。主要用到MFC自带的CColorDialog
先定义变量CColorDialog myDlg1,然后GetColor()获取选择的颜色,删除画刷指向的对象,再新建画刷
代码
CColorDialog myDlg1;
if(IDOK == myDlg1.DoModal()){
backcolor = myDlg1.GetColor();
cb.DeleteObject();
cb.CreateSolidBrush(backcolor);
}
字体颜色更改类似
(2)更改标题
先画一个dialog用来填写标题名
dialog添加类,给编辑框添加VALUE变量,确定按钮双击添加处理程序。点击菜单弹出此对话框过程不再赘述。
在点击确定后,UpdateData()将控件值给变量,再通过GetParent()找到父窗口更改父窗口的值,同时SendMessage(WM_CLOSE)关闭本窗口。
代码
UpdateData(true);
this->GetParent()->SetWindowTextW(newtitle);
this->SendMessage(WM_CLOSE);
同时父窗口要把新值给标题变量。
删除和隐藏较简单,等配置文件读取做好后一起做。
3.配置文件的读写
读写主要涉及两个函数GetPrivateProfileString和WritePrivateProfileString
主要是CString类型也可以读int型
DWORD GetPrivateProfileString(
LPCTSTR lpAppName, // INI文件中的一个字段名[节名]可以有很多个节名
LPCTSTR lpKeyName, // lpAppName 下的一个键名,也就是里面具体的变量名
LPCTSTR lpDefault, // 如果lpReturnedString为空,则把个变量赋给lpReturnedString
LPTSTR lpReturnedString, // 存放键值的指针变量,用于接收INI文件中键值(数据)的接收缓冲区
DWORD nSize, // lpReturnedString的缓冲区大小
LPCTSTR lpFileName // INI文件的路径
);
UINT GetPrivateProfileInt(
LPCTSTR lpAppName, // INI文件中的一个字段名[节名]可以有很多个节名
LPCTSTR lpKeyName, // lpAppName 下的一个键名,也就是里面具体的变量名
INT nDefault, // 如果没有找到指定的数据返回,则把个变量值赋给返回值
LPCTSTR lpFileName // INI文件的路径
);
其中字段名、键名、存放键值的指针、缓冲区大小都是自己设置的。获取文件路径可能存在疑问。可以再对话框初始化时就先获取ini配置文件位置,DWORD GetModuleFileName(
HMODULE hModule,//装载程序实例句柄,可以通过AfxGetInstanceHandle()获取
LPTSTR lpFilename,//返回相应的路径
DWORD nSize
);
BOOL WritePrivateProfileString(
LPCTSTR lpAppName, // INI文件中的一个字段名[节名]可以有很多个节名
LPCTSTR lpKeyName, // lpAppName 下的一个键名,也就是里面具体的变量名
LPCTSTR lpString, // 键值,也就是数据
LPCTSTR lpFileName // INI文件的路径
)
例如保存标题(curtitle)
WritePrivateProfileString(tit,_T("标题"),curtitle,path);
tit字段名(如便签1、便签2等),curtitle标题变量,path路径
颜色的保存要先把RGB三个值分别取出来,然后format转为CString再保存
temp.Format(_T("%d %d %d"),GetRValue(backcolor),GetGValue(backcolor),GetBValue(backcolor));
WritePrivateProfileString(tit,_T("背景颜色"),temp,path);
读取是也是一样要RGB一个个读:
wchar_t *str=NULL;
str = new wchar_t[len];
GetPrivateProfileString(tit,_T("字体颜色"),_T(""),str,len,path);
temp=str;
str1=getString(temp);
a= _wtoi(str1);//CString转int
str1=getString(temp);
b=_wtoi(str1);
str1=getString(temp);
c=_wtoi(str1);
fontcolor=RGB(a,b,c);
因为读取颜色时得到的是存在空格的一串CString,自己写一个getstring()方法取得RGB三个值。
String dialog1::getString(CString &str){
CString ss=_T("");
str.Trim();//去掉两端空白字符
str+=_T(" ");
int t=str.Find(' ');//找空格,t返回第一个空格位置,没有找到返回-1
if(t!=-1){
ss=str.Left(t);//取空格左边数
str=str.Mid(t+1);//str从第二个数开始
}
return ss;
}
配置文件读取大概内容就这些,主要就两个函数,可以先另外新建一个程序来熟悉配置文件读写,没问题再迁移到本程序中。
4.整合为完整程序
(1)(完整思路)我们可以把一个个便利贴放进数组,vector<dialog1*> notelist;//搞个dialog类型的动态数组,notelist[i]就代表第i个便利贴。运行程序时,先读取配置文件,如果配置文件中已经有便利贴,读取便利贴数量n,接着循环新建dialog,读取配置中的信息,将dialog中变量更新,如:
int len=notelist.size();//数组长度代表便利贴数量
for(int i =0;i<len;i++){
dialog1 *p = new dialog1();
notelist.push_back(p);
p->Create(IDD_DIALOG1,NULL);//先创建窗口
p->fetch(i,str);//读便利贴配置
p->setnote();//更新窗口
}
如果配置文件为空,点击新建窗口后,会新建一个指针p*,先push_back进数组,然后
list->Create(dialog,this)新建一个便利贴窗口。
至于程序运行过程中进行各种操作,改变的值不会立即写入配置文件,而是在退出时用DeleteFile()先删除原先的配置文件,然后,循环保存各个便签,就是上边讲的将每个便签的各种变量值写入配置文件。
隐藏便签功能的实现,搞个变量i在新建便签时,给i赋一个默认值,在隐藏便签的处理程序中,改变i的值,然后SW_HIDE隐藏此便签,下次打开程序时。
删除便签功能的实现,调用父窗口中的删除方法,通过迭代找到数组中当前便签,然后CloseWindow(),erase(),注意每次erase都可能使迭代器失效,要在失效前指向下一个位置,代码
for (vector<dialog1*>::iterator iter=notelist.begin();iter!=notelist.end();){
if((*iter)==p){
p->CloseWindow();
delete p;
notelist.erase(iter);
break;
}
else
iter++;
}
系统托盘上的显示所有菜单功能的实现,主要就是找到隐藏变量i不是默认值的便签,方法就是循环读取每个便签i的值,如果不是默认值将它改为默认值,然后SW_SHOW显示此便签。
系统托盘上的删除所有便签,在该菜单的处理程序中,循环用WM_CLOSE关闭所有的便签,然后直接notelist.clear()清空数组即可。
设定弹出时间功能还没想好