-
VC++6.0中创建动态控件是比较偏离基础的知识 也有一定的难度.它的完整功能是要动态创建控件后再动态响应控件中的事件 两者全部做到才算完整.
这里 我将展示一个完整的动态控件示例 它可以动态创建控件 然后再动态响应控件事件 并可以保存控件信息至ini配置文件 然后再根据ini文件读取出控件信息来动态创建控件.相信它能够解决你在动态控件中所遇到的许多问题.
当然 动态控件的方法有许多种 我展示的 只是给我认为较好的.
这里以VC++6.0创建对话框工程为例.添加菜单 分别添加子项按钮 文本框 标签.大家都是知道的 VC中基本上都要靠手写 所以 这里先写三个控件的创建 其它的控件 基本一致.
第一步当然是建立一个全局的类(噢 这个只是我个人喜好) 里面放上满满的全局共用数据 我会把它们都放入一个*Global.h文件中.还有一个控件类 里面包含了每个控件都需要的属性 比如控件的名称 大小 坐标 附加的什么.
除了那两个类 最重要的还是控件类 因为使用系统的控件类添加事件的响应会比较麻烦(实现不会太难 主要是不好管理).具体添加方法我想你们都清楚 就是使用系统的添加类向导生成三个类(我们现在只做三个不同类型的控件) 一个继承自按钮类(CButton)-按钮 一个继承自编辑框类(CEdit)-文本框 一个继承自静态类(CStatic)-标签 分别命名为CMyButton CMyEdit CMyLabel 不会介意吧?.如果使用手动添加的话 则强大的事件类向导将不能使用.
这下因该有2个系统类 3个控件类了吧??当然 控件类也会在工程中加入它们的头文件与程序文件.下面就是设计控件类了.我有考虑使用多继承来使这三类自定义控件类都继承控件类(第一步中加入的控件类 暂时称为控件主类为好) 不过没使用 因该更方便.现在只是在控件主类中声明了那三个自定义控件 然后加上一开始的一些共公信息 就是下面这样的了:
//控件主类
class _myControl
{
public:
//共公信息
CString caption;//标题
CRect rect;//坐标大小
int type;//类型
//动态控件
CMyButton myButton;
CMyEdit myEdit;
CMyLabel myLabel;
_myControl()
{
caption="Control";
type=0;//默认为按钮
rect=CRect(10, 80, 100, 120);//初始坐标大小
}
};
在这个类中用那三个自定义控件类成员 分别用来存放动态生成的三种不同类型的控件.如果你还想把它保存起来 并能随时读取出来的话 还要加上共公信息中的那些成员变量.
另外程序中加入了下面这些常量:
#define IDB_MYCONTROL 0x9000 //自定义按钮的句柄(ID)
#define NUM_CONTROL 128 //数目
//保存配置文件用
const CString APPINFO="appInfo";
const CString CONTROL="Control";
const CString SETTING="Setting";
#define MYBUTTON 1
#define MYEDIT 2
#define MYLABEL 3
程序里设计了一个class _globalData类 使用它的globalData对象可以访问里面的全局数据.
//全局数据
class _globalData
{
public:
CString appPath;//程序路径
CString appAllPath;//保存文件全路径
bool isDraw;//是否可以拖拽控件
//控件信息
//vector <_myControl> myControl;//考虑使用vector也是可以的
_myControl* myControl[NUM_CONTROL];//这里使用数量受到了限制
int count;//已经建立的控件总数
_globalData()
{
isDraw=false;
//初始控件
for (int i=1;i<=NUM_CONTROL-1;i++) {
globalData.myControl[i]=new _myControl;
}
count=0;
//取得当前路径
char temp[255]= _T("");//保存当前路径的变量
GetModuleFileName(NULL,appPath.GetBufferSetLength(sizeof(temp)),sizeof(temp));//取得程序所在的全目录名
int nPos=appPath.ReverseFind('//'); //取得除去文件名字符数后的总长度
appPath=appPath.Left(nPos+1); //截取得到的文件路径长度 最终得到程序所在路径
appPath.ReleaseBuffer();
appAllPath=appPath+"myControl.ini";
}
}extern globalData;
这些代码不是很难 相信都能看懂.事实上以后建立控件的话就是创建了一个_myControl* 对象 使用它来管理所有不同类型的控件.
我们已经做好了准备 现在即将开始.在工程中加入菜单(当然 随便你如何 我只是想要有三个按钮来触发新建三个不同类型控件的事件).
addContorl(this,MYBUTTON); //新建按钮
addContorl(this,MYEDIT); //新建文本框
addContorl(this,MYLABEL); //新建标签
addContorl函数当然是很重要的:
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//新增控件
//参数:[1].新建控件的父窗体[2].控件的类型[3].表示是新增控件还是读取控件(编号) 值为0则表示新增控件 编号使用最大数量;为其它值时 是读取控件的编号
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
template<class T>
void addContorl(T& object,int type,int readID=0)
{
int _index=0;//标识建立的控件编号(新增控件时为最大控件号 读取控件时 为传递过来的值)
//如果是新增
if (readID==0)
{
globalData.count++;//增加总数
globalData.myControl[globalData.count]->type=type;//确定类型
//公共数据
CString _str;
_str.Format("%d",globalData.count);
globalData.myControl[globalData.count]->caption+=_str;//名称标题
//这里设置新建的控件初始坐标为最后一个控件的坐标偏移
CRect _rect;
if (globalData.count>1)
{
_rect=globalData.myControl[globalData.count-1]->rect;
globalData.myControl[globalData.count]->rect.left=_rect.left+10;
globalData.myControl[globalData.count]->rect.top=_rect.top+10;
globalData.myControl[globalData.count]->rect.right=_rect.right+10;
globalData.myControl[globalData.count]->rect.bottom=_rect.bottom+10;
}
else
_rect=globalData.myControl[globalData.count]->rect;
_index=globalData.count;
}else
_index=readID;
//----------创建控件
//一.都是要靠消息来完成 按钮的字体是随系统的不能改变
HFONT hFont;
hFont = CreateFont(12, 0, 0, 0, 400, 0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH || FF_DONTCARE, "宋体");
//各自数据
switch(type) {
case MYBUTTON:
{
globalData.myControl[_index]->myButton.Create(globalData.myControl[_index]->caption,WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,globalData.myControl[_index]->rect,object ,IDB_MYCONTROL+_index);
SendMessage(globalData.myControl[_index]->myButton,WM_SETFONT,(DWORD)hFont,TRUE);
};
break;
case MYEDIT:
{
//两种方法使文本框具有3D风格
/*
//只有使用CreateEx才能创建具有扩展风格的文本框 否则没有3D效果
globalData.myControl[_index]->myEdit.CreateEx(WS_EX_CLIENTEDGE, // 指明窗口具有3D外观,这意味着,边框具有下沉的边界。
_T("EDIT"), "",//globalData.myControl[_index]->caption
WS_CHILD | WS_VISIBLE,
globalData.myControl[_index]->rect,object, IDB_MYCONTROL+_index);
*/
globalData.myControl[_index]->myEdit.Create(WS_VISIBLE | WS_CHILD,globalData.myControl[_index]->rect,object ,IDB_MYCONTROL+_index);
globalData.myControl[_index]->myEdit.ModifyStyleEx(0, WS_EX_CLIENTEDGE, SWP_DRAWFRAME) ;
//globalData.myControl[_index]->myEdit.HideCaret();
SendMessage(globalData.myControl[_index]->myEdit,WM_SETFONT,(DWORD)hFont,TRUE);
//这里EDIT控件要特殊处理一下 因为使用CreateEx创建了带3D的扩展风格 所以 实际大小会少去4个点用来显示3D效果 这里要加上4个点
//CRect rect;
//globalData.myControl[_index]->myEdit.GetClientRect(&rect);
globalData.myControl[_index]->myEdit.SetWindowPos(NULL,globalData.myControl[_index]->rect.left-2,globalData.myControl[_index]->rect.top-2,globalData.myControl[_index]->rect.Width()+4,globalData.myControl[_index]->rect.Height()+4,NULL);
}
break;
case MYLABEL:
globalData.myControl[_index]->myLabel.Create(globalData.myControl[_index]->caption,WS_VISIBLE | WS_CHILD | SS_NOTIFY,globalData.myControl[_index]->rect,object ,IDB_MYCONTROL+_index);
SendMessage(globalData.myControl[_index]->myLabel,WM_SETFONT,(DWORD)hFont,TRUE);
globalData.myControl[_index]->myLabel.Invalidate(TRUE);
break;
default:;
}
}
头痛....仔细阅读 或许也不是太难理解.它是真正负责在窗体上建立控件的代码.其实建立控件已经到此就完成了 函数会根据你所调用到addContorl的第二个参数的不同创建不同的控件于窗体上 你似乎已经看到了.
我们要做的不止这些 因为我说过了 只有当动态控件能响应几乎所有事件的话 整个工程才算完整.所以接下来我们将要把控件对事件的响应完成掉.
动态控件的事件响应 两种最为常用(也许只是我) 一种是在PreTranslateMessage中判断消息的ID是否是控件ID 然后再判断事件消息来操作.一种就是使用自己的控件类 在类中添加好控件对消息的事件处理.有人会使用ON_COMMAND_RANGE 但不总是太好 也不能实现大多消息功能.
因为在一开始 我们使用了系统向导来生成继承的控件类 所以 它是能够得到六个文件(三个.h三个.cpp) 也就意味着 它是能够使用ctrl+w类向导来生成事件.你试下? 呵 是的 就是这么简单 直接添加事件的响应就可以了 不如在按钮类里面来个单击事件?
void CMyButton::OnClicked()
{
AfxMessageBox("你单击了我!(BN_CLICKED)");
}
噢 真的能响应 似乎太简单了!?也许吧 任何方法都是不止你所能看到的数量 而只是你我都未发现而已.
到此 动态控件的添加与事件响应已经能够完成了 我还说过要将它能保存与读取 所以 下面的代码将完成它.
把这些代码都写到global文件中:
//
//取得控件在窗体中的坐标与大小(根据控件 窗体相对屏幕的坐标)
//
template<class T,class B>
static CRect getRect(T& myControl,B& obj)
{
CRect _rect,_rect2,_rect3;
int _right,_bottom;//用于保存控件大小
//获取控件所在父窗体坐标
obj.GetClientRect(&_rect);
obj.ClientToScreen(&_rect);
//获取自身坐标
myControl.GetClientRect(&_rect2);
_right=_rect2.right;
_bottom=_rect2.bottom;
myControl.ClientToScreen(&_rect2);
_rect3.left=_rect2.left-_rect.left;//控件left值等于自身的left减去父窗体的left
_rect3.top=_rect2.top-_rect.top;//控件top值等于自身的top减去父窗体的top
_rect3.right=_rect3.left+_right;//这是控件的right值 等于left坐标+大小
_rect3.bottom=_rect3.top+_bottom;//这是控件的bottom值 等于top坐标+大小
return _rect3;
}
//
//重新计算控件坐标
//
template<class T>
static void getGUIData(T& obj)
{
CWnd* _wnd;
//重新计算控件坐标信息
for (int i=1;i<=globalData.count;i++) {
switch(globalData.myControl[i]->type) {
case MYBUTTON:
{
_wnd=CWnd::FromHandle(globalData.myControl[i]->myButton.m_hWnd);
}
break;
case MYEDIT:
{
_wnd=CWnd::FromHandle(globalData.myControl[i]->myEdit.m_hWnd);
}
break;
case MYLABEL:
{
_wnd=CWnd::FromHandle(globalData.myControl[i]->myLabel.m_hWnd);
}break;
default:;
}
globalData.myControl[i]->rect=getRect(*_wnd,*obj);
}
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//数据保存
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
template<class T>
static void saveFile(T& object)
{
getGUIData(object);//重新计算控件坐标及大小
//共用变量
CString _str;
//清空文件
CFile _file(globalData.appAllPath,CFile::modeCreate);//清空文件先CFile::Remove
_file.Close();
//DeleteFile(globalData.appAllPath);//删除整个文件
//清除
//WritePrivateProfileString(APPINFO,NULL,NULL,globalData.appAllPath);
//保存数量
_str.Format("%d",globalData.count);
WritePrivateProfileString(APPINFO,"count",_str,globalData.appAllPath);
//保存控件信息
//清除
//WritePrivateProfileString(CONTROL,NULL,NULL,globalData.appAllPath);
CString ITEM,_temp;
for (int i=1;i<=globalData.count;i++)
{
_str.Format("%d",i);
ITEM=CONTROL+_str;//项名
//公共属性
WritePrivateProfileString(ITEM,"caption",globalData.myControl[i]->caption,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->rect.left);
WritePrivateProfileString(ITEM,"left",_temp,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->rect.top);
WritePrivateProfileString(ITEM,"top",_temp,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->rect.right);
WritePrivateProfileString(ITEM,"right",_temp,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->rect.bottom);
WritePrivateProfileString(ITEM,"bottom",_temp,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->type);
WritePrivateProfileString(ITEM,"type",_temp,globalData.appAllPath);
}
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//数据读取
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
template<class T>
void readFile(T& object)
{
//清除资源
for (int j=1;j<=globalData.count;j++) {
delete globalData.myControl[j];
globalData.myControl[j]=new _myControl;
}
CString _str,ITEM;
char _buff[255];
globalData.count=GetPrivateProfileInt(APPINFO,"count",NULL,globalData.appAllPath);
for(int i=1;i<=globalData.count;i++)
{
_str.Format("%d",i);
ITEM=CONTROL+_str;//项名
GetPrivateProfileString(ITEM,"caption",NULL,_buff,256,globalData.appAllPath);
globalData.myControl[i]->caption.Format("%s",_buff);
globalData.myControl[i]->rect.left=GetPrivateProfileInt(ITEM,"left",NULL,globalData.appAllPath);
globalData.myControl[i]->rect.top=GetPrivateProfileInt(ITEM,"top",NULL,globalData.appAllPath);
globalData.myControl[i]->rect.right=GetPrivateProfileInt(ITEM,"right",NULL,globalData.appAllPath);
globalData.myControl[i]->rect.bottom=GetPrivateProfileInt(ITEM,"bottom",NULL,globalData.appAllPath);
globalData.myControl[i]->type=GetPrivateProfileInt(ITEM,"type",NULL,globalData.appAllPath);
//调用创建控件函数
addContorl(object,globalData.myControl[i]->type,i);
}
//获取屏幕分辩率
int nFullWidth=GetSystemMetrics(SM_CXSCREEN);
int nFullHeight=GetSystemMetrics(SM_CYSCREEN);
}
在任何地方调用:
saveFile(this);//保存所有控件信息
readFile(this);//读取
我在PreTranslateMessage还加入了对控件拖拽的处理:
//使用鼠标可以随意拖动控件
if (pMsg->message==WM_LBUTTONDOWN)
{
if (globalData.isDraw)//自己增加这个变量
{
FromHandle(pMsg->hwnd)->SendMessage( WM_SYSCOMMAND,SC_MOVE+1,0);
this->Invalidate(TRUE);
return true;
}
}
文献来源: http://blog.youkuaiyun.com/smallfishff/article/details/1884671
VC++6.0中创建动态控件是比较偏离基础的知识 也有一定的难度.它的完整功能是要动态创建控件后再动态响应控件中的事件 两者全部做到才算完整.
这里 我将展示一个完整的动态控件示例 它可以动态创建控件 然后再动态响应控件事件 并可以保存控件信息至ini配置文件 然后再根据ini文件读取出控件信息来动态创建控件.相信它能够解决你在动态控件中所遇到的许多问题.
当然 动态控件的方法有许多种 我展示的 只是给我认为较好的.
这里以VC++6.0创建对话框工程为例.添加菜单 分别添加子项按钮 文本框 标签.大家都是知道的 VC中基本上都要靠手写 所以 这里先写三个控件的创建 其它的控件 基本一致.
第一步当然是建立一个全局的类(噢 这个只是我个人喜好) 里面放上满满的全局共用数据 我会把它们都放入一个*Global.h文件中.还有一个控件类 里面包含了每个控件都需要的属性 比如控件的名称 大小 坐标 附加的什么.
除了那两个类 最重要的还是控件类 因为使用系统的控件类添加事件的响应会比较麻烦(实现不会太难 主要是不好管理).具体添加方法我想你们都清楚 就是使用系统的添加类向导生成三个类(我们现在只做三个不同类型的控件) 一个继承自按钮类(CButton)-按钮 一个继承自编辑框类(CEdit)-文本框 一个继承自静态类(CStatic)-标签 分别命名为CMyButton CMyEdit CMyLabel 不会介意吧?.如果使用手动添加的话 则强大的事件类向导将不能使用.
这下因该有2个系统类 3个控件类了吧??当然 控件类也会在工程中加入它们的头文件与程序文件.下面就是设计控件类了.我有考虑使用多继承来使这三类自定义控件类都继承控件类(第一步中加入的控件类 暂时称为控件主类为好) 不过没使用 因该更方便.现在只是在控件主类中声明了那三个自定义控件 然后加上一开始的一些共公信息 就是下面这样的了:
//控件主类
class _myControl
{
public:
//共公信息
CString caption;//标题
CRect rect;//坐标大小
int type;//类型
//动态控件
CMyButton myButton;
CMyEdit myEdit;
CMyLabel myLabel;
_myControl()
{
caption="Control";
type=0;//默认为按钮
rect=CRect(10, 80, 100, 120);//初始坐标大小
}
};
在这个类中用那三个自定义控件类成员 分别用来存放动态生成的三种不同类型的控件.如果你还想把它保存起来 并能随时读取出来的话 还要加上共公信息中的那些成员变量.
另外程序中加入了下面这些常量:
#define IDB_MYCONTROL 0x9000 //自定义按钮的句柄(ID)
#define NUM_CONTROL 128 //数目
//保存配置文件用
const CString APPINFO="appInfo";
const CString CONTROL="Control";
const CString SETTING="Setting";
#define MYBUTTON 1
#define MYEDIT 2
#define MYLABEL 3
程序里设计了一个class _globalData类 使用它的globalData对象可以访问里面的全局数据.
//全局数据
class _globalData
{
public:
CString appPath;//程序路径
CString appAllPath;//保存文件全路径
bool isDraw;//是否可以拖拽控件
//控件信息
//vector <_myControl> myControl;//考虑使用vector也是可以的
_myControl* myControl[NUM_CONTROL];//这里使用数量受到了限制
int count;//已经建立的控件总数
_globalData()
{
isDraw=false;
//初始控件
for (int i=1;i<=NUM_CONTROL-1;i++) {
globalData.myControl[i]=new _myControl;
}
count=0;
//取得当前路径
char temp[255]= _T("");//保存当前路径的变量
GetModuleFileName(NULL,appPath.GetBufferSetLength(sizeof(temp)),sizeof(temp));//取得程序所在的全目录名
int nPos=appPath.ReverseFind('//'); //取得除去文件名字符数后的总长度
appPath=appPath.Left(nPos+1); //截取得到的文件路径长度 最终得到程序所在路径
appPath.ReleaseBuffer();
appAllPath=appPath+"myControl.ini";
}
}extern globalData;
这些代码不是很难 相信都能看懂.事实上以后建立控件的话就是创建了一个_myControl* 对象 使用它来管理所有不同类型的控件.
我们已经做好了准备 现在即将开始.在工程中加入菜单(当然 随便你如何 我只是想要有三个按钮来触发新建三个不同类型控件的事件).
addContorl(this,MYBUTTON); //新建按钮
addContorl(this,MYEDIT); //新建文本框
addContorl(this,MYLABEL); //新建标签
addContorl函数当然是很重要的:
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//新增控件
//参数:[1].新建控件的父窗体[2].控件的类型[3].表示是新增控件还是读取控件(编号) 值为0则表示新增控件 编号使用最大数量;为其它值时 是读取控件的编号
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
template<class T>
void addContorl(T& object,int type,int readID=0)
{
int _index=0;//标识建立的控件编号(新增控件时为最大控件号 读取控件时 为传递过来的值)
//如果是新增
if (readID==0)
{
globalData.count++;//增加总数
globalData.myControl[globalData.count]->type=type;//确定类型
//公共数据
CString _str;
_str.Format("%d",globalData.count);
globalData.myControl[globalData.count]->caption+=_str;//名称标题
//这里设置新建的控件初始坐标为最后一个控件的坐标偏移
CRect _rect;
if (globalData.count>1)
{
_rect=globalData.myControl[globalData.count-1]->rect;
globalData.myControl[globalData.count]->rect.left=_rect.left+10;
globalData.myControl[globalData.count]->rect.top=_rect.top+10;
globalData.myControl[globalData.count]->rect.right=_rect.right+10;
globalData.myControl[globalData.count]->rect.bottom=_rect.bottom+10;
}
else
_rect=globalData.myControl[globalData.count]->rect;
_index=globalData.count;
}else
_index=readID;
//----------创建控件
//一.都是要靠消息来完成 按钮的字体是随系统的不能改变
HFONT hFont;
hFont = CreateFont(12, 0, 0, 0, 400, 0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH || FF_DONTCARE, "宋体");
//各自数据
switch(type) {
case MYBUTTON:
{
globalData.myControl[_index]->myButton.Create(globalData.myControl[_index]->caption,WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,globalData.myControl[_index]->rect,object ,IDB_MYCONTROL+_index);
SendMessage(globalData.myControl[_index]->myButton,WM_SETFONT,(DWORD)hFont,TRUE);
};
break;
case MYEDIT:
{
//两种方法使文本框具有3D风格
/*
//只有使用CreateEx才能创建具有扩展风格的文本框 否则没有3D效果
globalData.myControl[_index]->myEdit.CreateEx(WS_EX_CLIENTEDGE, // 指明窗口具有3D外观,这意味着,边框具有下沉的边界。
_T("EDIT"), "",//globalData.myControl[_index]->caption
WS_CHILD | WS_VISIBLE,
globalData.myControl[_index]->rect,object, IDB_MYCONTROL+_index);
*/
globalData.myControl[_index]->myEdit.Create(WS_VISIBLE | WS_CHILD,globalData.myControl[_index]->rect,object ,IDB_MYCONTROL+_index);
globalData.myControl[_index]->myEdit.ModifyStyleEx(0, WS_EX_CLIENTEDGE, SWP_DRAWFRAME) ;
//globalData.myControl[_index]->myEdit.HideCaret();
SendMessage(globalData.myControl[_index]->myEdit,WM_SETFONT,(DWORD)hFont,TRUE);
//这里EDIT控件要特殊处理一下 因为使用CreateEx创建了带3D的扩展风格 所以 实际大小会少去4个点用来显示3D效果 这里要加上4个点
//CRect rect;
//globalData.myControl[_index]->myEdit.GetClientRect(&rect);
globalData.myControl[_index]->myEdit.SetWindowPos(NULL,globalData.myControl[_index]->rect.left-2,globalData.myControl[_index]->rect.top-2,globalData.myControl[_index]->rect.Width()+4,globalData.myControl[_index]->rect.Height()+4,NULL);
}
break;
case MYLABEL:
globalData.myControl[_index]->myLabel.Create(globalData.myControl[_index]->caption,WS_VISIBLE | WS_CHILD | SS_NOTIFY,globalData.myControl[_index]->rect,object ,IDB_MYCONTROL+_index);
SendMessage(globalData.myControl[_index]->myLabel,WM_SETFONT,(DWORD)hFont,TRUE);
globalData.myControl[_index]->myLabel.Invalidate(TRUE);
break;
default:;
}
}
头痛....仔细阅读 或许也不是太难理解.它是真正负责在窗体上建立控件的代码.其实建立控件已经到此就完成了 函数会根据你所调用到addContorl的第二个参数的不同创建不同的控件于窗体上 你似乎已经看到了.
我们要做的不止这些 因为我说过了 只有当动态控件能响应几乎所有事件的话 整个工程才算完整.所以接下来我们将要把控件对事件的响应完成掉.
动态控件的事件响应 两种最为常用(也许只是我) 一种是在PreTranslateMessage中判断消息的ID是否是控件ID 然后再判断事件消息来操作.一种就是使用自己的控件类 在类中添加好控件对消息的事件处理.有人会使用ON_COMMAND_RANGE 但不总是太好 也不能实现大多消息功能.
因为在一开始 我们使用了系统向导来生成继承的控件类 所以 它是能够得到六个文件(三个.h三个.cpp) 也就意味着 它是能够使用ctrl+w类向导来生成事件.你试下? 呵 是的 就是这么简单 直接添加事件的响应就可以了 不如在按钮类里面来个单击事件?
void CMyButton::OnClicked()
{
AfxMessageBox("你单击了我!(BN_CLICKED)");
}
噢 真的能响应 似乎太简单了!?也许吧 任何方法都是不止你所能看到的数量 而只是你我都未发现而已.
到此 动态控件的添加与事件响应已经能够完成了 我还说过要将它能保存与读取 所以 下面的代码将完成它.
把这些代码都写到global文件中:
//
//取得控件在窗体中的坐标与大小(根据控件 窗体相对屏幕的坐标)
//
template<class T,class B>
static CRect getRect(T& myControl,B& obj)
{
CRect _rect,_rect2,_rect3;
int _right,_bottom;//用于保存控件大小
//获取控件所在父窗体坐标
obj.GetClientRect(&_rect);
obj.ClientToScreen(&_rect);
//获取自身坐标
myControl.GetClientRect(&_rect2);
_right=_rect2.right;
_bottom=_rect2.bottom;
myControl.ClientToScreen(&_rect2);
_rect3.left=_rect2.left-_rect.left;//控件left值等于自身的left减去父窗体的left
_rect3.top=_rect2.top-_rect.top;//控件top值等于自身的top减去父窗体的top
_rect3.right=_rect3.left+_right;//这是控件的right值 等于left坐标+大小
_rect3.bottom=_rect3.top+_bottom;//这是控件的bottom值 等于top坐标+大小
return _rect3;
}
//
//重新计算控件坐标
//
template<class T>
static void getGUIData(T& obj)
{
CWnd* _wnd;
//重新计算控件坐标信息
for (int i=1;i<=globalData.count;i++) {
switch(globalData.myControl[i]->type) {
case MYBUTTON:
{
_wnd=CWnd::FromHandle(globalData.myControl[i]->myButton.m_hWnd);
}
break;
case MYEDIT:
{
_wnd=CWnd::FromHandle(globalData.myControl[i]->myEdit.m_hWnd);
}
break;
case MYLABEL:
{
_wnd=CWnd::FromHandle(globalData.myControl[i]->myLabel.m_hWnd);
}break;
default:;
}
globalData.myControl[i]->rect=getRect(*_wnd,*obj);
}
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//数据保存
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
template<class T>
static void saveFile(T& object)
{
getGUIData(object);//重新计算控件坐标及大小
//共用变量
CString _str;
//清空文件
CFile _file(globalData.appAllPath,CFile::modeCreate);//清空文件先CFile::Remove
_file.Close();
//DeleteFile(globalData.appAllPath);//删除整个文件
//清除
//WritePrivateProfileString(APPINFO,NULL,NULL,globalData.appAllPath);
//保存数量
_str.Format("%d",globalData.count);
WritePrivateProfileString(APPINFO,"count",_str,globalData.appAllPath);
//保存控件信息
//清除
//WritePrivateProfileString(CONTROL,NULL,NULL,globalData.appAllPath);
CString ITEM,_temp;
for (int i=1;i<=globalData.count;i++)
{
_str.Format("%d",i);
ITEM=CONTROL+_str;//项名
//公共属性
WritePrivateProfileString(ITEM,"caption",globalData.myControl[i]->caption,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->rect.left);
WritePrivateProfileString(ITEM,"left",_temp,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->rect.top);
WritePrivateProfileString(ITEM,"top",_temp,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->rect.right);
WritePrivateProfileString(ITEM,"right",_temp,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->rect.bottom);
WritePrivateProfileString(ITEM,"bottom",_temp,globalData.appAllPath);
_temp.Format("%d",globalData.myControl[i]->type);
WritePrivateProfileString(ITEM,"type",_temp,globalData.appAllPath);
}
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//数据读取
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
template<class T>
void readFile(T& object)
{
//清除资源
for (int j=1;j<=globalData.count;j++) {
delete globalData.myControl[j];
globalData.myControl[j]=new _myControl;
}
CString _str,ITEM;
char _buff[255];
globalData.count=GetPrivateProfileInt(APPINFO,"count",NULL,globalData.appAllPath);
for(int i=1;i<=globalData.count;i++)
{
_str.Format("%d",i);
ITEM=CONTROL+_str;//项名
GetPrivateProfileString(ITEM,"caption",NULL,_buff,256,globalData.appAllPath);
globalData.myControl[i]->caption.Format("%s",_buff);
globalData.myControl[i]->rect.left=GetPrivateProfileInt(ITEM,"left",NULL,globalData.appAllPath);
globalData.myControl[i]->rect.top=GetPrivateProfileInt(ITEM,"top",NULL,globalData.appAllPath);
globalData.myControl[i]->rect.right=GetPrivateProfileInt(ITEM,"right",NULL,globalData.appAllPath);
globalData.myControl[i]->rect.bottom=GetPrivateProfileInt(ITEM,"bottom",NULL,globalData.appAllPath);
globalData.myControl[i]->type=GetPrivateProfileInt(ITEM,"type",NULL,globalData.appAllPath);
//调用创建控件函数
addContorl(object,globalData.myControl[i]->type,i);
}
//获取屏幕分辩率
int nFullWidth=GetSystemMetrics(SM_CXSCREEN);
int nFullHeight=GetSystemMetrics(SM_CYSCREEN);
}
在任何地方调用:
saveFile(this);//保存所有控件信息
readFile(this);//读取
我在PreTranslateMessage还加入了对控件拖拽的处理:
//使用鼠标可以随意拖动控件
if (pMsg->message==WM_LBUTTONDOWN)
{
if (globalData.isDraw)//自己增加这个变量
{
FromHandle(pMsg->hwnd)->SendMessage( WM_SYSCOMMAND,SC_MOVE+1,0);
this->Invalidate(TRUE);
return true;
}
}
文献来源: http://blog.youkuaiyun.com/smallfishff/article/details/1884671