Для начала разберемся, что к чему прилипает.
Небольшое отступление как создать равноправные дочерние окна по типу немодального диалогового окна регистрируется класс дочернего окна со своей процедурой а затем:
hwndList[0] = CreateWindow(szChild, "Child 1", WS_OVERLAPPEDWINDOW | WS_VISIBLE, W_USEDEFAULT, CW_USEDEFAULT,100, 50, hwnd, NULL, hInstance, NULL) ;
В принципе все, что нужно это обработать WM_WINDOWPOSCHANGING и скорректировать, если необходимо положение окна те осуществить проверку на близость к конкретному объекту. И еще одно нужно знать что происходит с окном двигают ли его или изменяют размер ухватив за левую правую верхнюю нижнюю кромку эту инфу можно получить из
WM_MOVING, WM_SIZING :
Узнаем какую кромку тянем -
void DockedOnSizing(WPARAM wParam, int &fMoving) { fMoving&=~15; if(wParam==9) fMoving|=15; else { if(wParam==1 || wParam==4 || wParam==7) fMoving|=1; if(wParam==2 || wParam==5 || wParam==8) fMoving|=2; if(wParam==3 || wParam==4 || wParam==5) fMoving|=4; if(wParam==6 || wParam==7 || wParam==8) fMoving|=8; } }
Изменяем, все кромки, просто тянем окно:
void DockedOnMoving(WPARAM wParam, int &fMoving) { fMoving|=15; }
Осуществляется проверка на близость к десктопу и соответственную корректировку положения
//lpwp - это lParam WM_WINDOWPOSCHANGING //fMoving - это ранее полученное значение кромки //StickAt - это контролируемое расстояние void IsCloseToDeskTop(LPWINDOWPOS lpwp, int fMoving, int StickAt) { RECT WorkArea; SystemParametersInfo(SPI_GETWORKAREA, 0, &WorkArea, 0); WorkArea.right -= lpwp->cx; WorkArea.bottom -= lpwp->cy; if( abs(WorkArea.left - lpwp->x) <= StickAt && fMoving&1) { if( !(fMoving&2) ) lpwp->cx +=lpwp->x - WorkArea.left; lpwp->x = WorkArea.left; } if( abs(WorkArea.right - lpwp->x) <= StickAt && fMoving&2) { if( !(fMoving&1) ) lpwp->cx +=WorkArea.right - lpwp->x ; else lpwp->x = WorkArea.right; } if( abs(WorkArea.top - lpwp->y) <= StickAt && fMoving&4) { if( !(fMoving&8) ) lpwp->cy += lpwp->y - WorkArea.top; lpwp->y = WorkArea.top; } if( abs(WorkArea.bottom - lpwp->y) <= StickAt && fMoving&8) { if( !(fMoving&4) ) lpwp->cy += WorkArea.bottom - lpwp->y ; else lpwp->y = WorkArea.bottom; } }
Анализ на близость двух окон
//hwndDockedTo - окно для анализа //lpwp - это lParam WM_WINDOWPOSCHANGING //fMoving - это ранее полученное значение кромки //StickAt - это контролируемое расстояние bool IsCloseToWindow(HWND hwndDockedTo, LPWINDOWPOS lpwp, int fMoving, int StickAt) { bool DockedByX=false; bool DockedByY=false; RECT WorkArea; if(hwndDockedTo) // если окно непустое { // анализ положения двух окон GetWindowRect(hwndDockedTo, &WorkArea); if( lpwp->y + lpwp->cy > WorkArea.top - StickAt && lpwp->y < WorkArea.bottom + StickAt) { if( fMoving&2 && abs(WorkArea.left - lpwp->cx - lpwp->x) <= StickAt) { if( !(fMoving&1) ) lpwp->cx = WorkArea.left-lpwp->x; else lpwp->x = WorkArea.left-lpwp->cx; DockedByY=true; } if( fMoving&1 && abs(WorkArea.right - lpwp->x) <= StickAt) { if( !(fMoving&2) ) lpwp->cx = lpwp->x - WorkArea.right + lpwp->cx; lpwp->x = WorkArea.right; DockedByY=true; } if(DockedByY) { if( WorkArea.bottom > lpwp->y + lpwp->cy/2) if( fMoving&4 && abs(WorkArea.bottom - lpwp->cy - lpwp->y) <= StickAt) lpwp->y = WorkArea.bottom - lpwp->cy;else; else if( fMoving&4 && abs(WorkArea.bottom - lpwp->y) <= StickAt) lpwp->y = WorkArea.bottom; if(WorkArea.top < lpwp->y + lpwp->cy/2) if( fMoving&8 && abs(WorkArea.top - lpwp->y) <= StickAt) lpwp->y = WorkArea.top;else; else if( fMoving&8 && abs(WorkArea.top - lpwp->y - lpwp->cy) <= StickAt) lpwp->y = WorkArea.top-lpwp->cy; } } if( lpwp->x + lpwp->cx > WorkArea.left && lpwp->x < WorkArea.right) { if( fMoving&8 && abs(WorkArea.top - lpwp->y - lpwp->cy) <= StickAt) { if( ! (fMoving&4) ) lpwp->cy = WorkArea.top - lpwp->y; else lpwp->y = WorkArea.top - lpwp->cy; DockedByX=true; } if( fMoving&4 && abs(WorkArea.bottom - lpwp->y ) <= StickAt) { if(!(fMoving&8)) lpwp->cy = lpwp->y - WorkArea.bottom + lpwp->cy; lpwp->y = WorkArea.bottom; DockedByX=true; } if(DockedByX) { if( fMoving&1 && abs(WorkArea.left - lpwp->x) <= StickAt) lpwp->x = WorkArea.left; if( fMoving&2 && abs(WorkArea.right - lpwp->x-lpwp->cx) <= StickAt) lpwp->x = WorkArea.right-lpwp->cx; } } } return (DockedByX || DockedByY); }
Комментировать все вычисления можно заморится основная идея взяли два прямоугольника и рассмотрели все варианты их взаимоположения и движения разных кромок Одна рекомендация StickAt делать не больше чем мин ширина окна/2 или мин высота/2. Теперь как использовать все это добро, в каждую оконную процедуру чье окно должно быть липнущим добавить
1 static int fSave; флажок активной кромки.
2
case WM_MOVING : DockedOnMoving(wParam, fSave); return true; case WM_SIZING : DockedOnSizing(wParam, fSave); return true; case WM_WINDOWPOSCHANGING : IsCloseToDeskTop((LPWINDOWPOS)lParam, fSave, 15); IsCloseToWindow(hwnd1, (LPWINDOWPOS)lParam, fSave, 15) ; ...// список окон подверженных тестироватию IsCloseToWindow(hwndn, (LPWINDOWPOS)lParam, fSave, 15) ; return 0;
Вроде все работает :). Пример в проекте.
Шаг прислал Куроедов Д. В.