Scroll Window Into View
A Win32 technique that animates a window sliding up into view from below the desktop edge, driven by a worker thread.
The pre-DWM way to make a window's first appearance feel smooth. The trick: park the window just below the visible work area (positioned at desktopHeight + 1), then run a worker thread that calls MoveWindow in small steps - three pixels every 10 ms here - until it reaches its final bottom-right position. Because the UI thread keeps pumping messages while the animation runs, the rest of the app stays responsive. A good reference for hand-rolled window entrance effects when you cannot use DWM transitions or an animation library.
struct _ScrollWindowIntoView{
HWND xHandle;
int nScreenWidth;
int nScreenHeight;
int nDesktopWidth;
int nDesktopHeight;
int nWindowHeight;
int nWindowWidth;
int NewPos;
int DestPos;
}; _ScrollWindowIntoView gSWIV;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void InitScrollWindowIntoView(HWND xHandle, _ScrollWindowIntoView *SWIV)
{
RECT rcDesktop;
RECT rcWindow;
SWIV->xHandle = xHandle;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0);
GetWindowRect(SWIV->xHandle, &rcWindow);
SWIV->nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
SWIV->nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
SWIV->nDesktopWidth = rcDesktop.right - rcDesktop.left;
SWIV->nDesktopHeight = rcDesktop.bottom - rcDesktop.top;
SWIV->nWindowHeight = rcWindow.bottom - rcWindow.top;
SWIV->nWindowWidth = rcWindow.right - rcWindow.left;
SWIV->NewPos = (SWIV->nDesktopHeight) + 1;
SWIV->DestPos = (SWIV->nDesktopHeight - SWIV->nWindowHeight);
MoveWindow(SWIV->xHandle, (SWIV->nDesktopWidth - SWIV->nWindowWidth) - 1, SWIV->NewPos, SWIV->nWindowWidth, SWIV->nWindowHeight, TRUE);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI Worker_Thread(LPVOID pvData)
{
_ScrollWindowIntoView *SWIV = (_ScrollWindowIntoView *)pvData;
while(SWIV->NewPos > SWIV->DestPos)
{
MoveWindow(SWIV->xHandle, (SWIV->nDesktopWidth - SWIV->nWindowWidth) - 1, SWIV->NewPos, SWIV->nWindowWidth, SWIV->nWindowHeight, TRUE);
SWIV->NewPos = (SWIV->NewPos - 3);
Sleep(10);
}
MoveWindow(SWIV->xHandle, (SWIV->nDesktopWidth - SWIV->nWindowWidth) - 1, SWIV->DestPos, SWIV->nWindowWidth, SWIV->nWindowHeight, TRUE);
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void DoScrollWindowIntoView(_ScrollWindowIntoView *SWIV)
{
Worker_Thread_Handle = CreateThread(NULL, 0, Worker_Thread, (LPVOID)SWIV, 0, &Worker_Thread_ID);
}
In the dialog procedure you call the two functions from WM_INITDIALOG:
if(xMessage == WM_INITDIALOG)
{
// ... set title, icon, wire up controls ...
InitScrollWindowIntoView(xHandle, &gSWIV);
DoScrollWindowIntoView(&gSWIV);
return TRUE;
}
Tune the feel by changing the step size (- 3) and the Sleep(10) interval; note SPI_GETWORKAREA is what makes it respect the taskbar, since raw screen metrics would slide the window underneath it.