MFC 编程——》运行时隐藏

定义一个bool m_show;//决定是否隐藏

m_show=false;//默认隐藏

可以在对话框中重载虚函数DefWindowProc()。

LRESULT CTest2Dlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if(message==133 )
{
if(b_show==false)
    ShowWindow(SW_HIDE);
else
   ShowWindow(SW_SHOW);
}
return CDialog::DefWindowProc(message, wParam, lParam);
}

程序中想还原显示的时候可以

m_show=true;
this->ShowWindow(SW_SHOW);

 

1.基于对话框的程序
   我在论坛上看到有人说在OnInitDialog()中加上ShowWindow(SW_HIDE)对话框便不出现了,其实是不可行的。至于原因,我认为是系统是在OnInitDialog()后调用ShowWindow(SW_SHOW)让对话框显示的.可以添加下面代码:
CXXDlg::OnInitDialog()
{
...
Sleep(5000);
return TRUE;
}
可以发现5秒后对话框才显示出来.至于在何时调用的我也不清楚,

 

但是我们可以在OnPaint()中加上ShowWindow(SW_HIDE),来达到隐藏的目的.不过使用的这种方法,会有一点闪烁.

 

另外一种方法就是在OnInitDialog()中使用SetWindowPlacement()

GetWindowPlacement(&m_wp); //恢复时用
ModifyStyleEx(WS_EX_APPWINDOW,WS_EX_TOOLWINDOW);//从任务栏中去掉.

WINDOWPLACEMENT wp;      屏幕上位置的信息。
wp.length=sizeof(WINDOWPLACEMENT);
wp.flags=WPF_RESTORETOMAXIMIZED;
wp.showCmd=SW_HIDE;
SetWindowPlacement(&wp);

还有一种更简单的方法:在OnInitDialog()中调用下面代码.
SetWindowPos(&wndTop,0,0,0,0,NULL);

 

2.基于单文档的程序

我们一般采用的方法就是将InitInstance()中的:
CXXApp::InitInstance()
{
   //m_pMainWnd->ShowWindow(SW_SHOW);
}
但是这样窗体还会有闪烁。
因为MFC还要在ActiveFrame显示框架,所以我们还需要添加下面代码:
void CMainFrame::ActivateFrame(int nCmdShow)
{
   nCmdShow=SW_HIDE;
   CFrameWnd::ActivateFrame(nCmdShow);
}
或者:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   AfxGetApp()->m_nCmdShow=SW_HIDE;
}
顺便说一下,上面通过ModifyStyleEx(WS_EX_APPWINDOW,WS_EX_TOOLWINDOW)的方法来实现从任务栏去掉按钮,这样当显示时还要切换显示的模式,其实还可以通过调用TaskbarList组件直接删除和添加:
ITaskbarList的定义在shobjidl.h(vs.net)中。
也可以手动定义:
DECLARE_INTERFACE_(ITaskbarList,IUnknown)
{
   STDMETHOD(QueryInterface)(THIS_ REFIID riid,LPVOID* ppvObj) PURE;
   STDMETHOD_(ULONG,AddRef)(THIS) PURE;
   STDMETHOD_(ULONG,Release)(THIS) PURE;
   STDMETHOD(ActivateTab)(HWND) PURE;
   STDMETHOD(AddTab)(HWND) PURE;
   STDMETHOD(DeleteTab)(HWND) PURE;
   STDMETHOD(HrInit)(void) PURE;
};
BOOL CMy2App::InitInstance()
{
   CoInitialize(0);
   ITaskbarList *pObj;
   CoCreateInstance(CLSID_TaskbarList,NULL,CLSCTX_INPROC_SERVER,IID_ITaskbarList,(void**)&pObj);
   pObj->DeleteTab(m_pMainWnd->m_hWnd); //从任务栏上删除
   //pObj->AddTab(m_pMainWnd->m_hWnd); //添加

   pObj->Release();
   CoUninitialize();
}

所以我们还可以用将窗体最小化,并从任备栏上删除按钮的方法来实现隐藏.

有很多应用程序要求一起动就隐藏起来,这些程序多作为后台程序运行,希望不影响其他窗口,往往只在托盘区显示一个图标。这些程序通常都是对话框程序,而对话框在初始化的过程上与SDI、MDI的初始化是不同的,对话框只需要DoModule或者是CreateDialog等等对话框函数调用一次便可,SDI、MDI则要好几步才行。这样看来,对话框在使用方法上面是隐藏了不少细节的,其中就没有SDI、MDI所要求的ShowWindow(nCmdShow)这一步。因此对话框要想一运行就隐藏,并不是很直接的。有一些方法可以做到这一点,下面我们就来看看几种方案。

1.
定时器
最直观,又是最无奈的一个方法就是使用定时器。既然我们在对话框开始显示之前不能用ShowWindow(SW_HIDE)将其隐藏,那就给一个时间让它显示,完了我们在隐藏它

方法:

1.在OnInitDialog()函数里设置定时器:(WINDOWS API里面响应消息WM_INITDIALOG)

SetTimer(1, 1, NULL);

2.添加处理WM_TIMER的消息处理函数OnTimer,添加代码:

if(nIDEvent == 1)

{

DeleteTimer(1);

ShowWindow(SW_HIDE);

}

这种方法的缺点是显而易见的,使用定时器,使得程序的稳定性似乎打一个折扣;窗口是要先显示出来的,那么效果就是窗口闪了一下消失。

2.
改变对话框显示状况
在对话框初始化时改变其显示属性可以让它隐藏起来。方法是调用SetWindowPlacement函数


BOOL CDialogExDlg::OnInitDialog()

{

CDialog::OnInitDialog();

//DO something


WINDOWPLACEMENT wp;

wp.length=sizeof(WINDOWPLACEMENT);

wp.flags=WPF_RESTORETOMAXIMIZED;

wp.showCmd=SW_HIDE;

SetWindowPlacement(&wp);

return TRUE;

}



在需要显示时(通常是响应热键或者托盘图标的鼠标消息):


WINDOWPLACEMENT wp;

wp.length=sizeof(WINDOWPLACEMENT);

wp.flags=WPF_RESTORETOMAXIMIZED;

wp.showCmd=SW_SHOW;

SetWindowPlacement(&wp);


这样的效果很不理想:窗口显示在屏幕的左上角,并且是只有标题栏,要正常显示,还需加上如下代码:

定义一个成员变量CRect rect;

在OnInitDialog()里面:

GetWindowRect(&rect);

在需要显示的地方:

SetWindowPos(&wndNoTopMost, wndRc.left, wndRc.top, wndRc.right, wndRc.bottom, SWP_SHOWWINDOW);

CenterWindow();

即使这样,效果还是很差。

这种方法还有一个弊端是当程序开始运行并且隐藏起来后,原来激活的窗口变成了非激活状态了,而当对话框显示出来后,对话框自身也是非激活状态的。

下面是对话框的恢复代码:

WINDOWPLACEMENT wp;//定义这个结构

GetWindowPlacement(&wp);//这行代码是在隐藏的时候添加的,为了保存当前窗口信息

SetWindowPlacement(&wp);//这段代码是恢复窗口使用的代码

 

 



3.不绘制窗口
当对话框显示时将要响应消息WM_PAINT绘制客户区,相应消息WM_NCPAINT绘制窗口边框。我们在窗口第一次自绘自身时隐藏窗口,可以收到比较良好的效果。由于窗口是先画窗口边框,所以我们仅需处理WM_NCPAINT即可。代码如下:

添加WM_NCPAINT处理函数。

void CMyDialog::OnNcPaint()

{

static int i = 2;

if(i > 0)

{

i --;

ShowWindow(SW_HIDE);

}

else

CDialog::OnNcPaint();

}

这里有个问题:为什么要定义静态变量i而且设其值为2呢?

我们只要窗口隐藏第一次,所以定义这个变量可以判断是否时首次显示窗口。当程序开始运行时,系统发送(SendMessage)WM_NCPAINT消息,此时程序的窗口边框应该被显示,但是此时我们没有作任何显示的操作,而是将窗口隐藏,ShowWindow(SW_HIDE)将把窗口的WS_VISIBLE属性去掉,继续执行,程序将检查WS_VISIBLE属性,如果没有则显示窗口,所以又发送了一个WM_NCPAINT消息。所以我们要处理两次WM_NCPAINT消息。

在需要窗口显示时,调用ShowWindow(SW_SHOW)即可。

程序执行的结果是,原来处于激活状态的窗口可能会闪动两下,然后仍然处于激活状态。这种处理方式比上面的方式要优越得多。


4.
将对话框作为子窗口
这种方法是采用SDI框架,主窗口始终隐藏,对话框作为主窗口的成员变量,在CMainFrame::OnCreate()里面加入下代码:

if(!dlg.Create(IDD_MYDIALOG, this))

{

return –1;

}

dlg.ShowWindow(SW_HIDE);

在要显示对话框的地方用dlg.ShowWindow(SW_SHOW);即可。注意,主窗口一定要隐藏,否则对话框可能会闪现一下。

隐藏状态栏窗口
上面介绍了几种检查对话框的方法,大家如果试过的话可能已经注意到系统状态栏里在程序启动时会有程序的图标闪过,在隐藏对话框的时候这个也是要隐藏的,方法很简单:

在OnInitDialog()函数里面加上ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_TOOLWINDOW);即可。在要显示窗口的地方加上代码

ModifyStyleEx(WS_EX_TOOLWINDOW, WS_EX_APPWINDOW);即将窗口的扩展样式改回来

 

WS_EX_TOOLWINDOW,带有这个属性的窗口有以下特点:

1. 不在任务栏显示。

2. 不显示在Alt+Tab的切换列表中。

3. 在任务管理器的窗口管理Tab中不显示。

我们可能会出于某种目的会为我们的窗口设置这个属性,但有一个副作用,就是这个窗口被拖动时,可以被拖动到任务栏里面去。

图片中,任务栏后面那个模糊的身影,就是我那悲催的窗口

 

那么什么办法能够最简单的实现创建一个父窗口呢?CFrameWnd 就有点笨了,干脆就用自己好了。在
   BOOL CHideApp::InitInstance()中:
  CHideDlg dlgParent;
  dlgParent.Create(IDD_HIDE_DIALOG);
  dlgParent.ShowWindow(SW_HIDE);
  //dlgParent.ShowWindow(SW_SHOW);
  CHideDlg dlg(&dlgParent);
  m_pMainWnd = &dlg;
  int nResponse = dlg.DoModal();
  dlgParent.DestroyWindow();
  在BOOL CHideDlg::OnInitDialog()中:
  this-ModifyStyleEx(WS_EX_APPWINDOW, 0);
  只短短几行,就实现了对话框在任务栏的隐藏。

 

 解决闪烁问题

 

如果你使用VC6做的单文档程序,那么只需在BOOL CXXXXApp::InitInstance()方法中添加如下代码:

                    m_nCmdShow = SW_HIDE;
                    if (!ProcessShellCommand(cmdInfo))
                            return FALSE;

           即在"if (!ProcessShellCommand(cmdInfo))”在这一句的上方加一句代码"m_nCmdShow = SW_HIDE;"即可

 

      如果使用VC2008建立的单文档程序,那么需要在BOOL CXXXXApp::InitInstance()方法中添加如下代码:

                    m_nCmdShow = SW_HIDE;
                    m_bLoadWindowPlacement=FALSE;
                    if (!ProcessShellCommand(cmdInfo))
                               return FALSE;

            即在"if (!ProcessShellCommand(cmdInfo))”在这一句的上方加两行代码"m_nCmdShow = SW_HIDE;"和"m_bLoadWindowPlacement=FALSE;"

 

        大家可能觉得奇怪,为什么不一样呢?

        原因是在VC2008建立的单文档程序中,CXXXXApp默认继承的CWinAppEx类,该类中新添加了一些处理。如果你在"if (!ProcessShellCommand(cmdInfo))”处打上断点按F11,走到一个方法后继续按F11跟踪进去直到代码走到下面这个方法:

       BOOL CWinAppEx::LoadState(LPCTSTR lpszSectionName /*=NULL*/, CFrameImpl* pFrameImpl /*= NULL*/)

       而在该方法中有下面一段代码,问题就出在这里:

       if (m_bLoadWindowPlacement)
      {
               //--------------------------------------------------------
               // Set frame default(restored) size:
               //--------------------------------------------------------
               ReloadWindowPlacement(pFrameImpl->m_pFrame);
      }

我们再继续跟进BOOL CWinAppEx::ReloadWindowPlacement(CFrameWnd* pFrameWnd),其实现代码如下:

BOOL CWinAppEx::ReloadWindowPlacement(CFrameWnd* pFrameWnd)
{
     ASSERT_VALID(pFrameWnd);

     CCommandLineInfo cmdInfo;
    AfxGetApp()->ParseCommandLine(cmdInfo);
    if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)
    {
       //Don't show the main window if Application
       //was run with /Embedding or /Automation.
       return FALSE;
    }

    CRect rectNormal;
    int nFlags = 0;
    int nShowCmd = SW_SHOWNORMAL;
    BOOL bRet = FALSE;

    if (LoadWindowPlacement(rectNormal, nFlags, nShowCmd))
 {
  WINDOWPLACEMENT wp;
  wp.length = sizeof(WINDOWPLACEMENT);

  if (pFrameWnd->GetWindowPlacement(&wp))
  {
   wp.rcNormalPosition = rectNormal;
   wp.showCmd = nShowCmd;

   RECT rectDesktop;
   SystemParametersInfo(SPI_GETWORKAREA,0, (PVOID)&rectDesktop,0);
   OffsetRect(&wp.rcNormalPosition, -rectDesktop.left, -rectDesktop.top);

   pFrameWnd->SetWindowPlacement(&wp);

   bRet = TRUE;
  }
 }

 if (pFrameWnd->IsKindOf(RUNTIME_CLASS(CMDIFrameWndEx)))
 {
  CDockingManager *pDockingManager = ((CMDIFrameWndEx *)pFrameWnd)->GetDockingManager();
  pDockingManager->ShowDelayShowMiniFrames(TRUE);
 }
 else if (pFrameWnd->IsKindOf(RUNTIME_CLASS(CFrameWndEx)))
 {
  CDockingManager *pDockingManager = ((CFrameWndEx *)pFrameWnd)->GetDockingManager();
  pDockingManager->ShowDelayShowMiniFrames(TRUE);
 }

 return bRet;
}

       看我加了下划线加粗的代码行,发现我之前设置的"m_nCmdShow = SW_HIDE;"在这里被更改,所以会闪烁,那么看到这里问题就可以解决了。

      我们发现之所以会进ReloadWindowPlacement方法,是因为m_bLoadWindowPlacement=TRUE的原因,这个值在CWinAppEx的构造函数中初始化为TRUE了,那么我们就应该想办法将m_bLoadWindowPlacement置为FALSE,所以我们使用VC2008开发时就应该在调用ReloadWindowPlacement方法前把m_bLoadWindowPlacement置为FALSE,于是就有了文章开头我说的修改方法。

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值