动态创建控件支持事件响应并可保存与读取

豆豆网   技术应用频道   2007年12月30日  【字号: 收藏本文

内容摘要:动态控件是比较繁杂的一个工作 这里演示了从在一个窗体上分别动态创建按钮、文本框、标签框的例子,当然,你还可以直接再添加其它的控件是很容易的.并且它是可以响应动态控件的事件的,你还可以用类向导生成事件,不必再手动添加消息。我还使这些控件的信息保存为文件并可以随时读取。

  本文示例源代码或素材下载

  VC++6.0中创建动态控件是比较偏离基础的知识,也有一定的难度。它的完整功能是要动态创建控件后再动态响应控件中的事件,两者全部做到才算完整。

  这里我将展示一个完整的动态控件示例,它可以动态创建控件,然后再动态响应控件事件,并可以保存控件信息至ini配置文件,然后再根据ini文件读取出控件信息来动态创建控件。相信它能够解决你在动态控件中所遇到的许多问题。

  当然,动态控件的方法有许多种,我展示的只是给我认为较好的。

  这里以VC++6.0创建对话框工程为例,添加菜单,分别添加子项按钮,文本框,标签。大家知道,VC中基本上都要靠手写,所以,这里先写三个控件的创建,其它的控件基本一致。

  第一步当然是建立一个全局的类(噢 这个只是我个人喜好) 里面放上满满的全局共用数据,我会把它们都放入一个*Global.h文件中。还有一个控件类,里面包含了每个控件都需要的属性,比如控件的名称、大小、坐标以及附加。

  除了那两个类,最重要的还是控件类,因为使用系统的控件类添加事件的响应会比较麻烦(实现不会太难,主要是不好管理)。具体添加方法我想大家都明白,就是使用系统的添加类向导生成三个类(我们现在只做三个不同类型的控件) 一个继承自按钮类(CButton)-按钮一个继承自编辑框类(CEdit)-文本框,一个继承自静态类(CStatic)-标签,分别命名为CMyButton,CMyEdit,CMyLabel,不会介意吧?。如果使用手动添加的话,则强大的事件类向导将不能使用。

  这下应该有二个系统类,三个控件类了吧?当然控件类也会在工程中加入它们的头文件与程序文件。下面就是设计控件类了。我有考虑使用多继承来使这三类自定义控件类都继承控件类(第一步中加入的控件类暂时称为控件主类为好) 不过没使用 因该更方便.现在只是在控件主类中声明了那三个自定义控件 然后加上一开始的一些共公信息 就是下面这样的了:////////////////
//控件主类
////////////////
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 
程序里设计了一个_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 调用的第二个参数的不同在窗体上创建不同的控件,

来源:vckbase    作者:天枫十一郎    责编:豆豆技术应用

正在加载评论...