Přetahování okna bez Caption baru (MFC a C++)

Pokud znáte hudební přehrávač WinAmp, určitě víte, že se dá přetahovat tak, že kamkoliv kliknete a táhnete. Dnes se naučíme naprogramovat něco podobného.

Já osobně znám dvě možnosti, jak to naprogramovat, jedna spočívá v odchycení zprávy WM_NCHITTEST a druhá v manuálním přesouvání okna. Zdrojový kód na konci článku obsahuje první možnost.

Možnost první - WM_NCHITTEST

Základ spočívá ve zprávě WM_NCHITTEST, která je zasílaná systémem ještě před zprávami WM_MOUSEMOVE, WM_LBUTTONDOWN. V parametru této zprávy je uložena pozice myši. Většinou se tato zpráva nechává standardně zpracovat, ale pro náš příklad si vytvoříme její novou verzi.

Pokud vyvoláme funkci OnNcHitTest z předchůdce naší třídy, vrácená hodnota se bude rovnat oblasti okna, pro kterou byla zpráva vyvolána. Uvedu zde pouze dvě hodnoty, které budeme používat, zbytek si můžete vyhledat v dokumentaci.

HTCLIENT - zpráva byla vyvolána z klientské části okna
HTCAPTION - zpráva byla vyvolána v titulku okna (caption)

Vše tedy vykonáme tak, že když bude funkce vracet hodnotu HTCLIENT, změníme ji na HTCAPTION. Tímto způsobem vlastně oklameme systém, který si bude myslet, že myš je nad titulkem okna (caption).

A nakonec samotný kód:

//v mapě zpráv
ON_WM_NCHITEST()

...

UINT CPresunDlg::OnNcHitTest(CPoint point)
{
  UINT result=CDialog::OnNcHitTest(point);
  
  if(result==HTCLIENT)
    result=HTCAPTION;
  
  return result;
}

Možnost druhá - manuální

Tento způsob spočívá v tom, že si naprogramujeme obsluhy zpráv WM_LBUTTONDOWN, WM_LBUTTONUP a WM_MOUSEMOVE tak, aby přesouvaly okno.

Nejprve si do třídy okna přidáme tři členské proměnné:

protected:
  int oldx,oldy; //pozice myši vzhledem k oknu
  BOOL draging; //udává, jestli je okno právě přesouváno

V konstruktoru inicializujeme proměnnou draging na FALSE, aby se okno po vytvoření nezačalo hned přesunovat. Potom si nadefinujeme obsluhy zpráv WM_LBUTTONDOWN, WM_LBUTTONUP a WM_MOUSEMOVE. Vše objasní zdrojový kód:

//mapa zpráv
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MUSEMOVE()

...

void COkno::OnLButtonDown(UINT nFlags,CPoint point)
{
  draging=TRUE;
  oldx=point.x;
  oldy=point.y;
  SetCapture();
}

void COkno::OnLButtonUp(UINT nFlags,CPoint point)
{
  draging=FALSE;
  ReleaseCapture();
}

void COkno::OnMouseMove(UINT nFlags,CPoint point)
{
  if(draging)
  {
    int x,y;
    CRect rect;
    GetWindowRect(&rect);
    x=rect.left+(point.x-oldx);
    y=rect.top+(point.y-oldy);
    
    SetWindowPos(NULL,x,y,SWP_NOSIZE);
  }
}

Z příkladu je jasné, jak vše funguje. Nová poloha okna se vypočítá tak, že se k aktuální poloze přičte nebo odečte vzdálenost, o kterou se myš posunula. Hlavně byste neměli zapomenout na funkce SetCapture a ReleaseCapture, protože jinak by se po rychlém pohybu myši okno přestalo přesouvat.

A tohle už je konec tohoto článku. Podle mě je lepší druhý způsob, protože pomocí něj můžeme okno přesunout kamkoli, což u prvního nejde, ale pokud zase nechcete psát tolik kódu vyberte si první.

Stáhnout zdrojový kód (23kb)

UPOZORNĚNÍ: Jedná se o archiv článků z let 2003 - 2005, uvedené technologie či postupy již mohou být neaktuální.