《MFC教程》03章 初始大小、标题和图标的改变


一、直接修改现有图标

在上一章生成的基础上,打开资源视图,选择唯一的图标“IDR_MAINFRAME”双击,然后将图标“擦除”,画一个自己喜欢的图形并保存。“Ctrl+F5”重新编译生成运行,你将会和小雅一样惊奇地发现,运行出来的程序的图标并没有改变

小雅是个喜欢钻牛角尖的人,将源程序前前后后地研究了很长时间,未得其解,只好不耻下问。身边的人问了、论坛上也发贴了,仍未找到答案。不免有几句牢骚,中国许多人在招聘时个个都说自己能力很强,问他会不会VC++,他会举出很多做过VC++的项目,进了单位之后,让他编一个很简单的程序,费了九牛二虎之力生成一堆废代码。如果给他一个sample的话,倒能发挥发挥。

这种现象决不是小雅有意要攻击哪个人,在国内如此,在国外更是如此。而许多编程人员,明明技术很差,却个个自以为了不起,一点没有想学的念头。不仅如此,许多人都象穷疯了一样,都想通过一两次生意、或一两年奋斗来“暴发”一下。这种好大喜功的心理在中国很普遍,国家领导也将经济增长的数目作为政绩,而不注重实实在在的技术、生产能力,靠出卖廉价劳动带来的经济增长,这有什么好吹的。

那么原因出在哪儿呢,原因出在MFC生成的图标上,我们自己新建一个图标,无论怎样画大小也只有1K左右,而MFC生成的图标竟有22K之多。小雅将MFC生成的图标一个象素一个象素地在新建的图标上画了一遍,大小也只有1K左右,新画的图标在Windows的资源管理器里面,无论大图标还是小图标,画错了的地方都能立即显示出来,而MFC生成的图标,无论怎样修改,在Windows的资源管理器里面,无论大图标还是小图标都没有任何改变。

小雅知识有限,对图象方面了解不多,记得看过一篇文章,介绍如何通过调色板控制BMP,使其颜色不能被修改。图标也应该有类似的功能,小雅既然不会,就介绍一下更换图标的笨方法。这个笨办法对第1章和第2章生成的程序都适用。

  1. 在资源视图中“Icon”下选择“IDR_MAINFRAME”图标,然后用“Delete”键删除。
  2. 在Windows的资源管理器里面,打开当前项目下的“res”子目录,删除相应的图标文件。
  3. 在资源视图中“~.rc”下,用右键菜单的“插入资源(A)...”,然后在对话框中选择“Icon”新建一个图标。
  4. 在属性工具中,将新建的图标的ID改为“IDR_MAINFRAME”(默认为“IDI_ICON1”)。
  5. 在属性工具中,将新建的图标的Filename改为上面删除的文件名。注意应保存在“res”子目录下。

如果你事先已经有一个自己的图标,那最简单了,在VC++中什么也不需要修改,直接在资源管理器里面将那个22K的图标文件删除,然后用你自己的图标换成被删除的文件名。

二、改变窗口的标题和初始大小

在资源视图中打开“String Table”,IDR_MAINFRAME的值为“test2\n\ntest2\n\n\ntest2.Document”,将之改为:“yaer\n小雅\ntest2\n\n\ntest2.Document”,窗口的标题就改完了。

初始大小的更改,对于第2章生成的基于对话框的程序来说很简单,只要在资源视图中选择主窗口后打开,直接用鼠标改变大小就可以了。

对于第1章生成的文档类程序不忙于修改,先用类视图来看一下MFC生成的单文档程序和对话框程序的类有什么相同和不同。

单文档对话框
CAboutDlg“关于”对话框类。无须修改。CAboutDlg“关于”对话框类。无须修改。
CTest2App主应用程序类,调用框架类、文档类、视图类。无须修改。CTest3App主应用程序类,调用主对话框类。无须修改。
CMainFrame主框架类,即主窗口。很少需要修改。Ctest3Dlg主对话框类。
CTest2Doc文档类,即存放数据主要在这儿编写代码。
CTest2view视图类,即用于屏幕显示的类,文字显示和画图主要在这儿编写代码。

修改窗口的初始大小在CMainFrame类的PreCreateWindow()成员函数中,通过修改 CREATESTRUCT cs 来修改窗口类或样式。

typedef struct tagCREATESTRUCTW {
    LPVOID    lpCreateParams;     //指向创建窗口用的数据的指针
    HANDLE    hInstance;          //窗口的实例句柄
    HMENU     hMenu;              //窗口的菜单句柄
    HWND      hwndParent;         //窗口句柄
    int       cy;                 //窗口的高度
    int       cx;                 //窗口的宽度
    int       y;                  //窗口的左上角的y座标
    int       x;                  //窗口的左上角的x座标
    LONG      style;              //窗口的类型
    LPCSTR    lpszName;           //指向窗口的名称的指针
    LPCSTR    lpszClass;          //指向窗口类的名称的指针
    DWORD     dwExStyle;          //窗口的扩展类型
} CREATESTRUCTW, *PCREATESTRUCTW, *LPCREATESTRUCTW;
    

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    if( !CFrameWnd::PreCreateWindow(cs) )
        return FALSE;
    // TODO: 在此处通过修改 CREATESTRUCT cs 来修改窗口类或样式
    cs.cx = 220;
    cs.cy = 150;

    return TRUE;
}

三、框架类、文档类、视图类的关系

框架类主要处理窗口的类型、边框、位置、大小以及子窗口(对于多文档来说)等内容,工具条、状态条也是这个类的成员变量。如果要添加成员变量时,初始化要在CMainFrame()构造函数中添加代码;OnCreate()成员函数是消息WM_CREATE的处理函数,这里面根据需要修改代码(一般不需要);PreCreateWindow()成员函数是创建主窗口前的设置,调整窗口位置、大小 、类型等都是在这儿修改代码。

文档类主要用来处理数据。例如画一个图,图的各节点座标、颜色、线条的粗细等都有文档类来处理,文件的保存和打开等当然也是文档类来处理。视图类主要处理数据的显示,例如:文字在窗口中显示、键盘鼠标操作等都是由视图类来处理。视图类不断地从文档类取数据,不断地对文档类传送数据。当文档类数据较多时,视图类显示的数据只是文档类的一部分。


四、主应用程序类做什么

主应用程序类是程序的入口,其它各个类都是在这儿集成的。这个类除构造函数之外,唯一要注意的就是初始化函数。这个类一般不修改。

单文档对话框
Ctest2App()构造函数Ctest2App()构造函数
InitInstance()初始化成员函数InitInstance()初始化成员函数
OnAppAbout()由菜单发出的消息处理函数

单文档对话框
BOOL Ctest2App::InitInstance()
{
    InitCommonControls();
    CWinApp::InitInstance();
    if (!AfxOleInit())
    {
        AfxMessageBox(IDP_OLE_INIT_FAILED);
        return FALSE;
    }
    AfxEnableControlContainer();
    SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
    
    //加载标准 INI 文件选项(包括 MRU)
    LoadStdProfileSettings(4);

    //上面内容不用太关注,以下是重点
    //注册应用程序的文档模板。文档模板
    //将用作文档、框架窗口和视图之间的连接
    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate(
        IDR_MAINFRAME,
        RUNTIME_CLASS(Ctest2Doc),
        RUNTIME_CLASS(CMainFrame),
        RUNTIME_CLASS(Ctest2View));
    if (!pDocTemplate)
        return FALSE;
    AddDocTemplate(pDocTemplate);
    //分析标准外壳命令、DDE、打开文件操作的命令行
    CCommandLineInfo cmdInfo;
    ParseCommandLine(cmdInfo);
    //调度在命令行中指定的命令。如果
    //用 /RegServer、/Register、/Unregserver 
    //或 /Unregister 启动应用程序,则返回 FALSE。
    if (!ProcessShellCommand(cmdInfo))
        return FALSE;
    //唯一的一个窗口已初始化,因此显示它并对其进行更新
    m_pMainWnd->ShowWindow(SW_SHOW);
    m_pMainWnd->UpdateWindow();
    //仅当存在后缀时才调用 DragAcceptFiles,
    //在 SDI 应用程序中,这应在 ProcessShellCommand
    //之后发生
    return TRUE;
}
BOOL Ctest3App::InitInstance()
{
    InitCommonControls();
    CWinApp::InitInstance();





    AfxEnableControlContainer();
    SetRegistryKey(_T("应用程序向导生成的本地应用程序"));




    //上面内容不用太关注,以下是重点
    Ctest3Dlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    //下面的程序注意:对话框窗口已经被关闭
    if (nResponse == IDOK)
    {
        // TODO: 在此放置处理何时用“确定”来关闭
        //对话框的代码
    }
    else if (nResponse == IDCANCEL)
    {
        // TODO: 在此放置处理何时用“取消”来关闭
        //对话框的代码
    }

    // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
    // 而不是启动应用程序的消息泵。
    return FALSE;
}