MFC——7.定制应用程序外观

Lesson7:定制应用程序外观

 

应用程序外观是用户体验中一个重要因素,优美的程序外观可以提升程序逼格。本文主要讲解了MFC中应用程序外观的修改,主要包括任务栏、工具栏、启动画面等。

 

1.      程序窗口的图标、背景、光标修改

1.1  窗口创建之前修改

因为应用程序的外观主要是在程序的框架窗口上,通过框架类体现的,所以在定制程序外观的时候,主要是在框架类进行修改。如果要修改应用程序框架外观大小,通常在窗口创建之前。可以在CMainFrame类里的PreCreateWindow()函数中实现。如果我们要修改窗口的图标、背景、光标,应该怎么修改?窗口的类型,大小是在创建窗口设定的,但前面Windows程序运行机制我们了解了如何创建一个窗口,它是通过一个窗口类,在这个类里设计窗口的时候,就设计好了图标、背景、光标,现有的窗口类的设计是MFC帮我们在底层已经设计好了,虽然我们不能改变这个底层代码,但我们可以重新设计一个窗口类,然后用自己的这个窗口类进行修改图标、背景、光标。

  

  BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&cs) //窗口创建之前修改应用程序框架大小
{
if( !CFrameWnd::PreCreateWindow(cs) )
        return FALSE;
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
/*********************    窗口创建之前修改应用程序框架大小  *****************/
cs.cx = 300;          //修改窗口大小(300*400大小)
cs.cy = 400;
//cs.style = ~FWS_ADDTOTITLE;  //修改标题栏名字,因为会选用默认的名字,所以将默认的选项取反
//cs.style = cs.style & ~FWS_ADDTOTITLE; //这种方式和上面方式的效果一样,选一种即可
cs.style = WS_OVERLAPPEDWINDOW;  //这种方式和上面方式的效果一样,选一种即可
cs.lpszName = "http"; //窗口名称
 
/**********************    自定义窗口类,然后注册使用  *****************/
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;          //类的额外内存不需要
wndcls.cbWndExtra = 0;                    //窗口的类的额外内存不需要
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);  //窗口背景
wndcls.hCursor=LoadCursor(NULL,IDC_HELP);  //窗口光标
wndcls.hIcon = LoadIcon(NULL,IDI_ERROR);    //窗口tubiao
wndcls.hInstance = AfxGetInstanceHandle();     //获得当前应用程序的句柄
wndcls.lpfnWndProc = ::DefWindowProc;        //窗口过程函数
wndcls.lpszClassName ="SUNXING";           //类的名称
wndcls.lpszMenuName = NULL; //菜单的名称,虽然设置为NULL,但会用到MFC自带的菜单
wndcls.style = CS_HREDRAW | CS_VREDRAW; //窗口类的类型,水平和垂直重绘
 
RegisterClass(&wndcls);     //注册窗口
 
cs.lpszClass = "SUNXING";   //修改cs结构体成员变量的类名,用它来重新设置窗口样式
 
//cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW| CS_VREDRAW, 0, 0, LoadIcon(NULL, IDI_WARNING));  //不用注册窗口类,直接修改窗口样式
       return TRUE;
}      

上面我们通过窗口类重新设计与注册使用后发现,只有图标发生了改变,而光标和背景没有改变,这是因为,程序在外观上是view类覆盖在frame类之上的,光标和背景消息首先被view类在获得响应,所以我们在frame类里修改后看不到效果。所以我们在view类里的PreCreateWindow()函数里添加一句代码,修改窗口类为我们以设计的类,就可以看到效果。 cs.lpszClass = "SUNXING";  

      

如果我们仅仅需要修改图标、光标、背景而去重新设计窗口类,那就太麻烦了,MFC提供了取件函数进行直接修改。

LPCTSTR AFXAPIAfxRegisterWndClass( UINT nClassStyle, HCURSORhCursor= 0, HBRUSH hbrBackground = 0,HICON hIcon = 0 );

 上面注释起来的就是这个函数的应用,同理这个函数可以在view类里使用。

1.2    窗口创建之后修改

如果我们已经创建了窗口,那么在窗口创建之后如果要修改窗口外观,需要在CMainFrame类和View类里的OnCreate()里通过SetWindowLong()函数进行操作,下面是两个例子的具体代码。

 

SetWindowLong(m_hWnd,GWL_STYLE, WS_OVERLAPPEDWINDOW); //取消了窗口名称

 

SetWindowLong(m_hWnd,GWL_STYLE, GetWindowLong(m_hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX);    //取消了最大化

 

1.3  循环显示窗口的图标

如果我们需要在窗口显示中有几幅图标轮流显示,该怎么做呢?首先设计我们要加载的图标,然后加载图标到图标数组中。假设我们添加了三幅图标,这里需要添加含有三个元素的图标数组,

HICON m_hIcons[3]。并通过添加timer函数定时地不断地切换图标。循环显示图标显然是在窗口建立了以后进行的,所以理所当然的在CMainFrame类的OnCreate()函数中添加代码。

int CMainFrame::OnCreate(LPCREATESTRUCTlpCreateStruct)
{
//三种方式加载图标,三选一就行
       m_hIcons[0] = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON1));
       m_hIcons[1] = LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDI_ICON2));//这里需要将另一个源文件定义的theApp变量声明一下,延伸作用域。
       m_hIcons[2] = LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDI_ICON3));
 
       SetClassLong(m_hWnd, GCL_HICON, (LONG)m_hIcons[0]);//先将第一幅图标加载上
 
       SetTimer(1,1000,NULL);  //设置定时器  调用定时器函数timer()
       return 0;
}

添加timer函数。

void CMainFrame::OnTimer(UINT_PTRnIDEvent)
{
       // TODO:  在此添加消息处理程序代码和/或调用默认值
       static  int index = 2;       //index定义为静态变量
       SetClassLong(m_hWnd, GCL_HICON, (LONG)m_hIcons[index]);
       index = (++index) % 3;
CFrameWnd::OnTimer(nIDEvent);
}

     

2.      状态栏中显示鼠标位置和系统时间

2.1   显示鼠标位置

View类窗口覆盖在frame类窗口之上,鼠标是在view类窗口里动作的,所以这里捕获鼠标消息首先要在view类里添加一个鼠标移动的消息,来获得鼠标的位置。

void CStyleView::OnMouseMove(UINTnFlags, CPoint point)
{
       // TODO:  在此添加消息处理程序代码和/或调用默认值
       CString str;
       str.Format("x=%d,y=%d",point.x,point.y);
 
       //几种在状态栏里获得放置鼠标位置的方式
       //((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);//通过获取框架类窗口的指针,调用框架类定义的状态栏对象,用对象的方法设定文本
       //((CMainFrame*)GetParent())->SetMessageText(str);
       //((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
       GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);
       CView::OnMouseMove(nFlags, point);
}

2.2   显示系统时间

显示系统时间同样是在窗口建立之后的动作,所以需要在OnCreate()函数里不断的调用timer()函数,这里只需要在OnCreate()函数里添加一句调用定时器的代码,其余代码都是向导自动生成的。SetTimer(1, 1000, NULL);  //设置定时器,  调用定时器函数timer()

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
         if(CFrameWnd::OnCreate(lpCreateStruct) == -1)
                   return-1;
 
         if(!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER| CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
                   !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
         {
                   TRACE0("未能创建工具栏\n");
                   return-1;      // 未能创建
         }
 
         if(!m_wndStatusBar.Create(this))
         {
                   TRACE0("未能创建状态栏\n");
                   return-1;      // 未能创建
         }
         m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT));
 
         //TODO:  如果不需要可停靠工具栏,则删除这三行
         m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
         EnableDocking(CBRS_ALIGN_ANY);
         DockControlBar(&m_wndToolBar);
 
         SetTimer(1,1000, NULL);  //设置定时器,  调用定时器函数timer()
         return0;
}

添加定时器函数,在这个函数里进行响应

void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
         //TODO:  在此添加消息处理程序代码和/或调用默认值
         CTimet = CTime::GetCurrentTime();
         CStringstr = t.Format("%H:%M:%S");
 
         CClientDCdc(this); //定义一个dc对象,用来获得str的尺寸,调整状态栏里时钟显示框的大小
         CSizesz = dc.GetTextExtent(str);
         //intindex = m_wndStatusBar.CommandToIndex(IDS_TIMER);    //如果不知道时钟在状态栏的第几个窗格,可以这样找到
         m_wndStatusBar.SetPaneInfo(1,IDS_TIMER, SBPS_NORMAL, sz.cx);
         m_wndStatusBar.SetPaneText(1,str);    //1就是前面的可以用index代替的数,通过状态栏的对象调用函数,其中函数有缺省的参数值
         CFrameWnd::OnTimer(nIDEvent);
}

 


3.      添加启动画面

添加启动画面时,需要准备一幅位图资源,当我们操作资源的时候,一般是和类相关联的。

①添加一个类CWzdSplash,基类是CWnd,并在类里定义一个位图变量CBitmapm_bitmap;

②通过向导添加消息响应函数OnPaint()和 手动添加自己的函数Create()。

 

准备工作做好后我们来实现启动画面三部曲。

①在frame类的OnCreate()函数里添加代码

int CMainFrame::OnCreate(LPCREATESTRUCTlpCreateStruct)
{
         CWzdSplashwndSplash;    //创建启动窗口类的实例
         wndSplash.Create();      //CWzdSplash类里定义的函数
         wndSplash.CenterWindow();     //启动画面出现在屏幕中间
         wndSplash.UpdateWindow();   //send WM_PAINT
         Sleep(2000);                   //画面停留时间2s
         wndSplash.DestroyWindow();      //销毁初始画面窗口
}
 

②在CWzdSplash的OnPaint()函数中添加代码

void CWzdSplash::OnPaint()
{
         CPaintDCdc(this); // device context for painting
         //TODO:  在此处添加消息处理程序代码
         //不为绘图消息调用 CWnd::OnPaint()
 
         BITMAPbitmap;
         m_bitmap.GetBitmap(&bitmap);
 
         CDCdcComp;
         dcComp.CreateCompatibleDC(&dc);
         dcComp.SelectObject(&m_bitmap);
         //draw bitmap
         dc.BitBlt(0,0, bitmap.bmWidth, bitmap.bmHeight, &dcComp, 0, 0, SRCCOPY);
}


③在CWzdSplash的Create()函数中添加代码

void CWzdSplash::Create()
{
         m_bitmap.LoadBitmap(IDB_BITMAP1);
         BITMAPbitmap;
         m_bitmap.GetBitmap(&bitmap);
        //CreateEx(0,AfxRegisterWndClass(0),"",WS_POPUP|WS_VISIBLE|WS_BORDER,0,0,bitmap.bmWidth,bitmap.bmHeight,NULL,0);
        CreateEx(0,AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
                   NULL,WS_POPUP | WS_VISIBLE, 0, 0, bitmap.bmWidth, bitmap.bmHeight, NULL, NULL);
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章