読者です 読者をやめる 読者になる 読者になる

きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

STATICコントロールのリサイズ

Windowsプログラミング C++
  • 分割ウィンドウの表示例
    • f:id:staryoshi:20140319024536p:plain
  • 今遊んでいるダイアログ
    • f:id:staryoshi:20140319025110p:plain

CreateWindowでウィンドウを生成するときにスタイルでWS_SIZEBOXを指定すればリサイズできる枠がつきます。その枠をドラッグしたりすると通常、ウィンドウのサイズが変更されます。ところが、STATICコントロールではできませんでした。

どうやらスタティックコントロールは外枠、即ちクライアントの外をクリックした際に生じるメッセージWM_NCHITTESTに対し、HTTRANSPARENTを返すみたいです。これでは何も動作しません。そこで、これらのメッセージの処理をDefWindowProcに任せました。

これでもリサイズはできません。というわけで、強引に全てをDefWindowProcに任せ、スタティックコントロールとして使うメッセージだけを元のスタティックコントロールのプロシージャに任せることにしました。

  • divided_window.h
#pragma once

#include <Windows.h>

class DividedWindows {
public:
	DividedWindows(HWND h_wnd);
	~DividedWindows();

	static void Register();
	void show();

private:
	static LRESULT CALLBACK WindowProc(HWND h_wnd, UINT message, WPARAM wp, LPARAM lp);
	static LRESULT CALLBACK Child1Proc(HWND h_wnd, UINT message, WPARAM wp, LPARAM lp);

	void onCreate();
	void onLButtonUp();
	LRESULT mainProc(HWND h_wnd, UINT message, WPARAM wp, LPARAM lp);

	SHORT x, y;
	SHORT width, height;
	SHORT rx, ry, lx, ly;
	SHORT r_width, l_width;
	SHORT r_height, l_height;
	
	HWND h_wnd;
	HWND h_child1;
	HWND h_child2;
	WNDPROC child1_proc;
};
#include "divided_windows.h"
#include "resource.h"
#include "project.h"
#include <exception>

DividedWindows::DividedWindows(HWND h_wnd) {
	x = y = 0;
	width = height = 200;

	CreateWindow(
		L"DividedWindows", L"title",
		WS_OVERLAPPEDWINDOW | WS_CHILD | WS_CLIPCHILDREN,
		x, y, width, height, h_wnd, NULL,
		GetModuleHandle(NULL), this);
	
	// ここに来るのはWM_CREATEメッセージの処理の後
	if(this->h_wnd == NULL) {
		throw std::exception();
	}
}

void DividedWindows::show() {
	ShowWindow(h_wnd, SW_SHOW);
}

void DividedWindows::Register() {
	WNDCLASS wndc;

	wndc.style = CS_HREDRAW | CS_VREDRAW;
	wndc.lpfnWndProc = WindowProc;
	wndc.cbClsExtra = wndc.cbWndExtra = 0;
	wndc.hInstance = NULL;
	wndc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndc.lpszMenuName = NULL;
	wndc.lpszClassName = L"DividedWindows";

	if(!RegisterClass(&wndc)) {
		throw std::exception();
	}
}

LRESULT CALLBACK DividedWindows::WindowProc(HWND h_wnd, UINT message, WPARAM wp, LPARAM lp) {
	DividedWindows* self = (DividedWindows*)GetWindowLongPtr(h_wnd, GWLP_USERDATA);

	if(self == NULL) {
		if(message == WM_CREATE) {
			self = (DividedWindows*)((LPCREATESTRUCT)lp)->lpCreateParams;

			SetWindowLongPtr(h_wnd, GWLP_USERDATA, (LPARAM)self);
			self->h_wnd = h_wnd;
			self->onCreate();
		}

		return DefWindowProc(h_wnd, message, wp, lp);
	}

	else {
		return self->mainProc(h_wnd, message, wp, lp);
	}
}

LRESULT DividedWindows::mainProc(HWND h_wnd, UINT message, WPARAM wp, LPARAM lp) {
	switch(message) {
	case WM_LBUTTONUP:
		onLButtonUp();
		break;
	case WM_SIZE:
		SCOPE {
			int frame_size = GetSystemMetrics(SM_CXSIZEFRAME) * 2;
			RECT rc;
			GetClientRect(h_wnd, &rc);
			r_width = rc.right - l_width + frame_size + 2;
			r_height = rc.bottom + 2;
			l_height = rc.bottom + frame_size * 2 + 2;
			lx = -frame_size - 1;
			ly = -frame_size - 1;
			rx = l_width - frame_size - 1;
			ry = -1;

			MoveWindow(h_child1, lx, ly, l_width, l_height, TRUE);
			MoveWindow(h_child2, rx, ry, r_width, r_height, TRUE);
		}
		break;
	}

	return DefWindowProc(h_wnd, message, wp, lp);
}

DividedWindows::~DividedWindows() {
	DestroyWindow(h_wnd);
}

void DividedWindows::onCreate() {
	int frame_size = GetSystemMetrics(SM_CXSIZEFRAME) * 2;
	RECT rc;
	GetClientRect(h_wnd, &rc);
	
	l_width = rc.right / 2;
	l_height = rc.bottom + frame_size * 2;

	h_child1 = CreateWindowEx(WS_EX_CLIENTEDGE,
		L"STATIC", L"static1",
		WS_CHILD | WS_VISIBLE | WS_SIZEBOX,
		0, 0, 0, 0, h_wnd, NULL,
		GetModuleHandle(NULL), this);
	h_child2 = CreateWindow(
		L"STATIC", L"static2",
		WS_CHILD | WS_VISIBLE | WS_BORDER | SS_NOTIFY,
		0, 0, 0, 0, h_wnd, NULL,
		GetModuleHandle(NULL), this);

	//h_child1のウィンドウプロシージャを取得
	SetWindowLongPtr(h_child1, GWLP_USERDATA, (LONG_PTR)this);
	child1_proc = (WNDPROC)GetWindowLongPtr(h_child1, GWLP_WNDPROC);
	SetWindowLongPtr(h_child1, GWLP_WNDPROC, (LONG_PTR)Child1Proc);
}

void DividedWindows::onLButtonUp() {
	HDC hdc;
	hdc = GetDC(h_child1);
	TextOut(hdc, 10, 10, L"TESTA", lstrlen(L"TESTA"));
	ReleaseDC(h_child1, hdc);
	
	hdc = GetDC(h_child2);
	TextOut(hdc, 10, 10, L"TESTB", lstrlen(L"TESTB"));
	ReleaseDC(h_child2, hdc);
}

LRESULT CALLBACK DividedWindows::Child1Proc(HWND h_wnd, UINT message, WPARAM wp, LPARAM lp) {
	DividedWindows* self = (DividedWindows*)GetWindowLongPtr(h_wnd, GWLP_USERDATA);
	
	assert(self != NULL);
	if(self != NULL) {
		switch(message) {
		case WM_SIZE:
			SCOPE {
				RECT rc;
				GetWindowRect(h_wnd, &rc);
				self->l_width = rc.right - rc.left;
				InvalidateRect(h_wnd, NULL, TRUE);
				SendMessage(self->h_wnd, WM_SIZE, 0, 0);
			}
			break;
		case WM_PAINT:
			return CallWindowProc(self->child1_proc, h_wnd, message, wp, lp);
		}
		
		return DefWindowProc(h_wnd, message, wp, lp); 
	}
}

サブクラス化したChild1Procの処理でほとんど全ての処理をreturn DefWindowProc(h_wnd, message, wp, lp);としてデフォルト動作させているところがポイントです。スタティックコントロールの詳細の動作については、Win32API-スタティック コントロールの概要が大変参考になります。