MessageBox(hwnd, msg, "Initialization", MB_ICONEXCLAMATION | MB_OK);
return -1;
} catch (...) {
MessageBox(hwnd, "Unknown Error", "Initialization", MB_ICONEXCLAMATION | MB_OK);
return -1;
}
return 0;
case WM_SIZE:
pCtrl->Size(LOWORD(lParam), HIWORD(lParam));
return 0;
case MSG_MOVESPLITTER:
pCtrl->MoveSplitter(wParam);
return 0;
case WM_DESTROY:
SetWinLong
delete pCtrl;
return 0;
}
return ::DefWindowProc(hwnd, message, wParam, lParam);
}
Имеем обычную оконную процедуру за исключением одного сообщения: MSG_MOVESPLITTER. Это — наше собственное, определяемое пользователем сообщение, которое послано сплиттером его родительскому окну. Но сначала давайте взглянем на контроллер главного окна.
class Controller {
public:
Controller(HWND hwnd, CREATESTRUCT* pCreat);
~Controller();
void Size(int cx, int cy);
void MoveSplitter(int x);
private:
enum { splitWidth = 8 }; // width of splitter
// User Interface
HWnd _hwnd;
//Main controller window
HWnd _leftWin;
HWnd _rightWin;
HWnd _splitter;
int _splitRatio; // in per cent
int _cx;
int _cy;
};
Контроллер содержит дескриптор своего окна, двух дочерних подокон, и окна сплиттера. Он также сохраняет текущий коэффициент разбиения, в процентах.
Конструктор контроллера отвечает за создание дочерних окон.
Controller::Controller(HWND hwnd, CREATESTRUCT * pCreat) : _hwnd (hwnd), _leftWin (0), _rightWin (0), _splitter (0), _splitRatio (50) {
// Create child windows
{
ChildWinMaker leftWinMaker(IDC_PANE, _hwnd, ID_LEFT_WINDOW);
leftWinMaker.Create();
_leftWin.Init(leftWinMaker);
leftWinMaker.Show();
}
{
ChildWinMaker rightWinMaker(IDC_PANE, _hwnd, ID_RIGHT_WINDOW);
rightWinMaker.Create();
_rightWin.Init(rightWinMaker);
rightWinMaker.Show();
}
Splitter::MakeWindow(_splitter, _hwnd, ID_SPLITTER);
}
Когда пользователь перемещает полоску расщепителя, родитель получает сообщение MSG_MOVESPLITTER . Параметр wParam содержит новое расстояние полосы расщепителя от левого края родительского окна. В ответ на такое сообщение, родитель должен также изменить размеры дочерних подокон и переместить расщепитель. Он делает это, вызывая метод Size.
void Controller::MoveSplitter(int x) {
_splitRatio = x * 100 / _cx;
if (_splitRatio < 0) _splitRatio = 0;
else if (_splitRatio > 100) _splitRatio = 100;
Size(_cx, _cy);
}
Вообще же, Size вызывается всякий раз, когда пользователь изменяет размеры основного окна, но тот код который вытолько что видели, мы вызываем также тогда, когда сплиттер перемещается.
void Controller::Size(int cx, int cy) {
_cx = cx;
_cy = cy;
int xSplit = (_cx * _splitRatio) / 100;
if (xSplit < 0) xSplit = 0;
if (xSplit + splitWidth >= _cx) xSplit = _cx - splitWidth;
_splitter.MoveDelayPaint(xSplit, 0, splitWidth, cy);
_leftWin.Move(0, 0, xSplit, cy);
_rightWin.Move(xSplit+splitWidth, 0, cx-xSplit-splitWidth, cy);
_splitter.ForceRepaint ();
}
Обратите внимание на используемый здесь важный прием. Мы перемещаем расщепитель, но задерживаем его перерисовку до изменения его обоих подокон, расположенных слева и справа. Эта методика устраняет некоторое неприятное смазывание изображения.
Выше была представлена реализация клиентского кода. Теперь, я надеюсь, Вам будет интересно увидеть реализацию сплиттера.