Перейти к содержанию

Мерцающий текстовый контрол на WinAPI


Antonshka

Рекомендуемые сообщения

Привет, как можно избавиться от мерцания текстового контрола при очень частом его обновлении?

 

Использую обычный Static Control, созданный на главном окне вызовом функции CreateWindowExW. В родительской оконной процедуре возвращаю HOLLOW_BRUSH, в сообщении WM_CTLCOLORSTATIC, чтобы фон теста был прозрачным, то есть чтобы отображались только черные символы. ExStyle для Static Control устанавливаю на WS_EX_TRANSPARENT. А Style на WS_CLIPSIBLINGS. Для родительского окна Style ставлю на WS_CLIPCHILDREN.

 

Затем начинаю для родительского окна часто вызывать функцию

RedrawWindow(m_Owner.GetHWND(), NULL, NULL,  RDW_INVALIDATE | RDW_ALLCHILDREN);

и Static Text Control жутко мерцает.

Изменено пользователем Antonshka
Ссылка на комментарий
Поделиться на другие сайты

Пример кода мерцающего контрола. Если быстро перемещать ползунок прокрутки вверх-вниз, то будет видно как текстовый контрол мигает.

Спойлер

#pragma comment(linker,"\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#include <windows.h>
#include <windowsx.h>
#include <string>
#include <stdio.h>
#include "commctrl.h"
#pragma comment(lib, "comctl32")


HWND hMainWnd { NULL };
HWND hStaticTextCtrl{ NULL };
HBRUSH hHollowBrush{ NULL };
SCROLLINFO scrollInfo{ 0 };
int scrollInc = 10;
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Scrolling(int yInc);


int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
	MSG msg;
	WNDCLASSEXW m_WndClass{ 0 };
	m_WndClass.cbSize = sizeof(m_WndClass);
	m_WndClass.style = CS_HREDRAW | CS_VREDRAW;
	m_WndClass.lpfnWndProc = WndProc;
	m_WndClass.cbClsExtra = 0;
	m_WndClass.cbWndExtra = 0;
	m_WndClass.hInstance = hInstance;
	m_WndClass.hIcon = LoadIconW(NULL, IDI_APPLICATION);
	m_WndClass.hCursor = LoadCursorW(NULL, IDC_ARROW);
	m_WndClass.hbrBackground = CreateSolidBrush(RGB(200, 230, 200));
	m_WndClass.lpszMenuName = NULL;
	m_WndClass.lpszClassName = L"MainWndClass";
	m_WndClass.hIconSm = LoadIconW(NULL, IDI_APPLICATION);
	RegisterClassExW(&m_WndClass);

	hMainWnd = CreateWindowExW(0, L"MainWndClass", L"MainWindow",
		WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
		100, 100, 400, 400, NULL, NULL, hInstance, NULL);
	ShowWindow(hMainWnd, SW_SHOW);

	hStaticTextCtrl = CreateWindowExW(WS_EX_TRANSPARENT, L"Static", L"SomeTextThere",
		WS_VISIBLE | WS_CHILD | SS_LEFT | WS_CLIPSIBLINGS,
		30, 130, 100, 20, hMainWnd, NULL, hInstance, NULL);
	ShowWindow(hMainWnd, SW_SHOW);
	hHollowBrush = reinterpret_cast<HBRUSH>(GetStockObject(HOLLOW_BRUSH));

	while (GetMessageW(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}
	return 0;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
		scrollInfo.cbSize = sizeof(SCROLLINFO);
		scrollInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
		scrollInfo.nMax = 100;
		scrollInfo.nPage = 10;
		ShowScrollBar(hWnd, SB_VERT, TRUE);
		SetScrollInfo(hWnd, SB_VERT, &scrollInfo, TRUE);
		break;
	case WM_CTLCOLORSTATIC:
		SetBkMode(reinterpret_cast<HDC>(wParam), TRANSPARENT);
		SetTextColor(reinterpret_cast<HDC>(wParam), RGB(200, 50, 100));
		return reinterpret_cast<INT_PTR>(hHollowBrush);
	//case WM_ERASEBKGND:
		//	return TRUE;
	case WM_PAINT:
		{
			PAINTSTRUCT ps{ 0 };
			HDC hDC = NULL;
			hDC = BeginPaint(hWnd, &ps);
			SetBkMode(hDC, TRANSPARENT);
			TextOutW(hDC, 100, 100, L"This is a Main Window", wcslen(L"This is a Main Window"));
			EndPaint(hWnd, &ps);
		}
	break;
	case WM_LBUTTONUP: //move the static text control down by 7 pixels when clicking on the client area of the main window
		{
			RECT rect{ 0 };
			POINT point{ 0 };
			GetWindowRect(hStaticTextCtrl, &rect);
			point.x = rect.left;
			point.y = rect.top;
			ScreenToClient(hMainWnd, &point);
			point.y += 7;
			SetWindowPos(hStaticTextCtrl, NULL, point.x, point.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
			RedrawWindow(hMainWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
		}
		break;
	case WM_VSCROLL:
		if (!lParam) //the message is from the standard scrollbar, not from the scrollbar control.
		{
			switch (LOWORD(wParam))
			{
			case SB_LINEUP:
				Scrolling(-scrollInc); break;
			case SB_LINEDOWN:
				Scrolling(scrollInc); break;
			case SB_PAGEUP:
				Scrolling(-static_cast<int>(scrollInfo.nPage)); break;
			case SB_PAGEDOWN:
				Scrolling(static_cast<int>(scrollInfo.nPage)); break;
			case SB_THUMBTRACK:
				Scrolling(HIWORD(wParam) - scrollInfo.nPos); break;
			case SB_TOP:
				Scrolling(-scrollInfo.nMax); break;
			case SB_BOTTOM:
				Scrolling(scrollInfo.nMax); break;
			default: Scrolling(0);
			}
		}
		break;
	case WM_MOUSEWHEEL:
		Scrolling(-scrollInc * static_cast<short>(HIWORD(wParam)) / WHEEL_DELTA);
		break;
	case WM_CLOSE:
		DestroyWindow(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	return 0;
}


void Scrolling(int yInc)
{
	if (scrollInfo.nPos + yInc < 0)
	{
		yInc = scrollInfo.nPos;
		scrollInfo.nPos = 0;
		::ScrollWindow(hMainWnd, 0, yInc, NULL, NULL);
		SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
	}
	else if (scrollInfo.nPos + yInc > scrollInfo.nMax - static_cast<int>(scrollInfo.nPage))
	{
		yInc = (scrollInfo.nMax - static_cast<int>(scrollInfo.nPage)) - scrollInfo.nPos;
		scrollInfo.nPos = scrollInfo.nMax - static_cast<int>(scrollInfo.nPage);
		::ScrollWindow(hMainWnd, 0, -yInc, NULL, NULL);
		SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
	}
	else
	{
		scrollInfo.nPos += yInc;
		::ScrollWindow(hMainWnd, 0, -yInc, NULL, NULL);
		SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
	}
	RedrawWindow(hMainWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
	//RedrawWindow(hMainWnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
	//InvalidateRect(m_Owner.GetHWND(), NULL, TRUE);
	//RedrawWindow(m_Owner.GetHWND(), NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
	//RedrawWindow(m_Owner.GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
	//RedrawWindow(m_Owner.GetHWND(), NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT );
	//SetWindowPos(m_Owner.GetHWND(), NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
	//InvalidateRect(hMainWnd, NULL, TRUE);
	//InvalidateRect(hStaticTextCtrl, NULL, TRUE);
	//UpdateWindow(hMainWnd);
	//UpdateWindow(hStaticTextCtrl);
}

 

 

Ссылка на комментарий
Поделиться на другие сайты

BS_OWNERDRAW для кнопки и обработка отрисовки в оконной процедуре (сообщение WM_PAINT) родительского окна. И для RedrawWindow достаточно RDW_INVALIDATE. RDW_ERASE делает стирание перед перерисовкой, как и RDW_ERASENOW. Возможно, они являются причиной мерцания.

Ссылка на комментарий
Поделиться на другие сайты

16 минут назад, Xipho сказал:

BS_OWNERDRAW для кнопки и обработка отрисовки в оконной процедуре (сообщение WM_PAINT) родительского окна. И для RedrawWindow достаточно RDW_INVALIDATE. RDW_ERASE делает стирание перед перерисовкой, как и RDW_ERASENOW. Возможно, они являются причиной мерцания.

Попробовал убрать RDW_INVALIDATE. RDW_ERASE, - все равно мерцает.

А возможно с BS_OWNERDRAW для кнопки добиться отображения только текста, без фона самой кнопки? Если да, то как сделать фон прозрачным? Через TransparentBlt?

Например у меня под текстовым контролом будет какая-нибудь картинка.

Ссылка на комментарий
Поделиться на другие сайты

11 часов назад, Antonshka сказал:

Попробовал убрать RDW_INVALIDATE. RDW_ERASE, - все равно мерцает.

RDW_INVALIDATE убирать не надо. Ну и самый лучший вариант избавиться от мерцания - двойная буферизация. Суть - форму ты отрисовываешь на фоновый контекст, а по событию WM_PAINT фоновый контекст копируешь блиттингом на основной. Правда, с виндовыми контролами я так не делал, там я слегка по-другому делал. Поищу у себя в закромах, может, найду исходник одного из древних трейнеров, на котором я делал замену фона при нажатии на кнопку, но не факт, что оно у меня сохранилось. А по двойной буферизации можно посмотреть у меня на канале уроки по графике для винды.

Ссылка на комментарий
Поделиться на другие сайты

9 минут назад, Xipho сказал:

RDW_INVALIDATE убирать не надо.

Ага, я не то скопи-пастил. Хотел RDW_ERASENOW | RDW_ERASE. То есть я пробовал без RDW_ERASENOW | RDW_ERASE.

 

10 минут назад, Xipho сказал:

А по двойной буферизации можно посмотреть у меня на канале уроки по графике для винды.

Я смотрел все серии. Давно еще. Про буферизацию пересматривал на днях.

Как я заметил на видео, ты не обрабатываешь сообщение WM_ERASEBKGND, возвращая TRUE. Для отмены стирания фона.

case WM_ERASEBKGND:
	return TRUE;

И в функции DrawFrame у тебя не установлен флаг RDW_ERASE, при вызове функции RedrawWindow. Наверно поэтому у тебя изображение не мерцает.

Просто я подумал, как в твоем примере будет вести себя отображение, если начать изменять размер главного окна, потянув его за уголок. Ведь будет послано сообщение WM_ERASE и WM_PAINT для всех элементов окна, включая его самого. И по идее вначале  будет стерт весь фон, а потом выполнен case WM_PAINT.

 

В WM_PAINT у тебя используется глобальный backDC, и так как он всегда имеет в себе изображение, то BitBlt в WM_PAINT  отработает корректно. Но перед этим будет стерт весь фон. И по идее должно произойти мерцание.

Это меня немного сбило с толку, когда я пытался вникнуть в твою реализацию двойной буферизации.

 

Я пробовал уже делать двойную буферизацию для самого главного окна, и параллельно для текстового контрола, но увы, он все равно мерцал.

Я даже пробовал просто создавать дочернее окно, со стилем WS_EX_TRANSPARENT, и используя двойную буферизацию для него, функцией TextOut выводить строку. И все равно, даже и так мерцало.

Буду пробовать сейчас через BS_OWNERDRAW . А потом, если не выйдет, через CustomDraw.

 

Самое интересное, что ни один контрол не мерцает вообще, никогда. Только вот этот, текстовый.

Ссылка на комментарий
Поделиться на другие сайты

26 минут назад, Antonshka сказал:

то BitBlt в WM_PAINT  отработает корректно. Но перед этим будет стерт весь фон

Нет, при блиттинге фон не стирается, поскольку блиттинг может быть разного типа. С помощью блиттинга, например, можно накладывать картинки по маске

28 минут назад, Antonshka сказал:

Просто я подумал, как в твоем примере будет вести себя отображение, если начать изменять размер главного окна, потянув его за уголок. Ведь будет послано сообщение WM_ERASE и WM_PAINT для всех элементов окна, включая его самого. И по идее вначале  будет стерт весь фон, а потом выполнен case WM_PAINT.

Возьми исходник из урока, да протестируй, проблемы-то нет ) 

28 минут назад, Antonshka сказал:

Только вот этот, текстовый.

Возможно, как раз потому, что для него срабатывает WM_ERASEBKGND

Ссылка на комментарий
Поделиться на другие сайты

40 минут назад, Xipho сказал:

Нет, при блиттинге фон не стирается, поскольку блиттинг может быть разного типа.

Да, BitBlt в твоем примере просто копирует биты из одного DC в другой DC, без предварительного стирания фона этим же BitBlt.

Я говорил про то, что если потянуть за уголок главного окна, то и ему, и всем его дочерним окнам, будут посланы сообщения WM_ERASE, потом WM_PAINT.

То есть я говорил про то что фон будет стерт собщением WM_ERASE, но не самим BitBlt.

 

46 минут назад, Xipho сказал:

Возьми исходник из урока, да протестируй, проблемы-то нет )

Обязательно, если ничего не выйдет. Сперва потренируюсь на SS_OWNERDRAW.

 

Ссылка на комментарий
Поделиться на другие сайты

Отладочные тесты показали, что для ПРОЗРАЧНОГО текстового контрола, 100%-ое устранение мерцания невозможно. Вся загвоздка в стиле этого самого текстового контрола.

Чтобы фон текстового контрола был прозрачным, нужно обязательно использовать стиль WS_EX_TRANSPARENT. При нем фон текстового контрола стираться всякий раз когда обновляется нижележащий элемент. Вот последовательность:

1) обновляется фон нижележащего элемента (допустим это главное окно приложения). Клиентская область текстового контрола соответственно тоже вся обновилась, в ней стало отображаться то, что находится под ней, но сам текст при этом теперь не виден, так как абсолютно вся область текстового контрола была обновленна

2) начинает рисоваться текст в текстовом контролле (пусть даже и с двойной буферизацией)

 

Между первым и вторым пунктами и происходит исчезновение самого текста, то есть происходит мерцание. И это никак не пофиксить.

 

Изменено пользователем Antonshka
Ссылка на комментарий
Поделиться на другие сайты

Проверил сейчас прозрачный текстовый контрол в Cheat Engine на перемещение (инициирование обновления его прозрачного фона)

function UDF1_FormClick(sender)
UDF1.CELabel1.Top = UDF1.CELabel1.Top + 5
end

И знаете что? Он тоже мерцает! Это просто немыслимо...

 

Ссылка на комментарий
Поделиться на другие сайты

Всё же есть решение - Layered Windows

 

Чтобы проект Visual Studio поддерживал Layered Windows, необходимо в файле манифеста указать следующее

Спойлер

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> 
    <application>
      <!--The ID below indicates app support for Windows 8 -->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
    </application>
  </compatibility>
  <dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="*"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
  </dependency>
</assembly>

 

 

Главное окно и все его контролы нужно создать со стилем WS_EX_LAYERED. Далее для всех них нужно вызвать API функцию SetLayeredWindowAttributes.

О всех подробностях реализации можно почитать на MSDN, ссылка вверху.

 

Вот тестовый пример. Абсолютно никаких мерцаний. Ни у главного окна (оно использует здесь между прочим двойную буферизацию), ни у самих контролов.

Спойлер

#include <windows.h>
#include <windowsx.h>
#include <string>
#include <stdio.h>
#include "commctrl.h"
#pragma comment(lib, "comctl32")


HWND hMainWnd { NULL };
HDC hMainWndDC{ NULL };
HDC hMainWndCompDC{ NULL };
HBITMAP hMainWndCompBitmap{ NULL };
HBITMAP hMainWndOldCompBitmap{ NULL };
RECT rectMainWnd{ 0 };

HWND hStaticTextCtrl{ NULL };
HWND hTrackBarCtrl{ NULL };

SCROLLINFO scrollInfo{ 0 };
int scrollInc = 10;
PAINTSTRUCT ps{ 0 };

HBRUSH hHollowBrush{ NULL };

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK StaticTextProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
LRESULT CALLBACK TrackBarProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
void Scrolling(int yInc);


int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
	MSG msg;
	//////////////////////////////////////////////// Main Window ///////////////////////////////////////////
	WNDCLASSEXW m_WndClass{ 0 };
	m_WndClass.cbSize = sizeof(m_WndClass);
	m_WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	m_WndClass.lpfnWndProc = WndProc;
	m_WndClass.cbClsExtra = 0;
	m_WndClass.cbWndExtra = 0;
	m_WndClass.hInstance = hInstance;
	m_WndClass.hIcon = LoadIconW(NULL, IDI_APPLICATION);
	m_WndClass.hCursor = LoadCursorW(NULL, IDC_ARROW);
	m_WndClass.hbrBackground = CreateSolidBrush(RGB(200, 230, 200));
	m_WndClass.lpszMenuName = NULL;
	m_WndClass.lpszClassName = L"MainWndClass";
	m_WndClass.hIconSm = LoadIconW(NULL, IDI_APPLICATION);
	RegisterClassExW(&m_WndClass);

	hHollowBrush = CreateSolidBrush(RGB(5, 5, 5));

	hMainWnd = CreateWindowExW(WS_EX_LAYERED, L"MainWndClass", L"MainWindow",
		WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
		100, 100, 400, 400, NULL, NULL, hInstance, NULL);
	hMainWndDC = GetDC(hMainWnd);
	hMainWndCompDC = CreateCompatibleDC(hMainWndDC);
	SetBkMode(hMainWndCompDC, TRANSPARENT);
	SetLayeredWindowAttributes(hMainWnd, 0, 255, LWA_ALPHA);

	scrollInfo.cbSize = sizeof(SCROLLINFO);
	scrollInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
	scrollInfo.nMax = 100;
	scrollInfo.nPage = 10;
	ShowScrollBar(hMainWnd, SB_VERT, TRUE);
	SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
	ShowWindow(hMainWnd, SW_SHOW);

	//////////////////////////////////////////////// Static Control ///////////////////////////////////////////
	hStaticTextCtrl = CreateWindowExW(WS_EX_LAYERED, L"Static", L"SomeTextThere",
		WS_CHILD | SS_LEFT | WS_CLIPSIBLINGS | SS_NOTIFY,
		30, 130, 100, 50, hMainWnd, reinterpret_cast<HMENU>(1), hInstance, NULL);
	SetWindowSubclass(hStaticTextCtrl, StaticTextProc, 1, 0);
	SetLayeredWindowAttributes(hStaticTextCtrl, RGB(5, 5, 5), 0, LWA_COLORKEY);
	ShowWindow(hStaticTextCtrl, SW_SHOW);

	/////////////////////////////////////////////// TrackBar Control ///////////////////////////////////////////
	hTrackBarCtrl = CreateWindowExW(WS_EX_LAYERED, TRACKBAR_CLASSW, L"SimpleTrackBar",
		WS_CHILD | TBS_AUTOTICKS | TBS_ENABLESELRANGE | WS_CLIPSIBLINGS | SS_NOTIFY,
		100, 100, 80, 40, hMainWnd, reinterpret_cast<HMENU>(2), hInstance, NULL);
	SetWindowSubclass(hTrackBarCtrl, TrackBarProc, 1, 0);
	SetLayeredWindowAttributes(hTrackBarCtrl, RGB(5, 5, 5), 0, LWA_COLORKEY);
	ShowWindow(hTrackBarCtrl, SW_SHOW);

	while (GetMessageW(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}
	return 0;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CREATE:
		break;
	case WM_CTLCOLORSTATIC:
		SetBkMode(reinterpret_cast<HDC>(wParam), TRANSPARENT);
		if (reinterpret_cast<HWND>(lParam) == hStaticTextCtrl)
			SetTextColor(reinterpret_cast<HDC>(wParam), RGB(200, 50, 100));
		return reinterpret_cast<INT_PTR>(hHollowBrush);
	case WM_SIZE:
		if (hMainWndOldCompBitmap)
			DeleteObject(SelectObject(hMainWndCompDC, hMainWndOldCompBitmap));
		GetClientRect(hMainWnd, &rectMainWnd);
		hMainWndCompBitmap = CreateCompatibleBitmap(hMainWndDC, rectMainWnd.right - rectMainWnd.left, rectMainWnd.bottom - rectMainWnd.top);
		hMainWndOldCompBitmap = static_cast<HBITMAP>(SelectObject(hMainWndCompDC, hMainWndCompBitmap));
		FillRect(hMainWndCompDC, &rectMainWnd, reinterpret_cast<HBRUSH>(GetClassLongPtrW(hMainWnd, GCLP_HBRBACKGROUND)));
		TextOutW(hMainWndCompDC, 100, 100, L"This is a Main Window", wcslen(L"This is a Main Window"));
		break;
	case WM_ERASEBKGND:
		return TRUE;
	case WM_PAINT:
		BeginPaint(hWnd, &ps);
		BitBlt(hMainWndDC, 0, 0, rectMainWnd.right - rectMainWnd.left, rectMainWnd.bottom - rectMainWnd.top, hMainWndCompDC, 0, 0, SRCCOPY);
		EndPaint(hWnd, &ps);
		break;
	case WM_VSCROLL:
		if (!lParam) //the message is from the standard scrollbar, not from the scrollbar control.
		{
			switch (LOWORD(wParam))
			{
			case SB_LINEUP:
				Scrolling(-scrollInc); break;
			case SB_LINEDOWN:
				Scrolling(scrollInc); break;
			case SB_PAGEUP:
				Scrolling(-static_cast<int>(scrollInfo.nPage)); break;
			case SB_PAGEDOWN:
				Scrolling(static_cast<int>(scrollInfo.nPage)); break;
			case SB_THUMBTRACK:
				Scrolling(HIWORD(wParam) - scrollInfo.nPos); break;
			case SB_TOP:
				Scrolling(-scrollInfo.nMax); break;
			case SB_BOTTOM:
				Scrolling(scrollInfo.nMax); break;
			default: Scrolling(0);
			}
		}
		break;
	case WM_MOUSEWHEEL:
		Scrolling(-scrollInc * static_cast<short>(HIWORD(wParam)) / WHEEL_DELTA);
		break;
	case WM_LBUTTONUP:
		SetWindowPos(hWnd, NULL, 5, 5, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
		RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
		break;
	case WM_CLOSE:
		ReleaseDC(hMainWnd, hMainWndDC);
		DestroyWindow(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	return 0;
}


LRESULT CALLBACK StaticTextProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
	switch (uMsg)
	{
	case WM_COMMAND:
		break;
	case WM_LBUTTONUP:
		SetWindowPos(hWnd, NULL, 17, 23, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
		RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
		break;
	}
	return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}


LRESULT CALLBACK TrackBarProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
	switch (uMsg)
	{
	case WM_COMMAND:
		break;
	case WM_LBUTTONUP:
		SetWindowPos(hWnd, NULL, 150, 25, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
		RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
		break;
	}
	return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}


void Scrolling(int yInc)
{
	if (scrollInfo.nPos + yInc < 0)
	{
		yInc = scrollInfo.nPos;
		scrollInfo.nPos = 0;
		::ScrollWindow(hMainWnd, 0, yInc, NULL, NULL);
		SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
	}
	else if (scrollInfo.nPos + yInc > scrollInfo.nMax - static_cast<int>(scrollInfo.nPage))
	{
		yInc = (scrollInfo.nMax - static_cast<int>(scrollInfo.nPage)) - scrollInfo.nPos;
		scrollInfo.nPos = scrollInfo.nMax - static_cast<int>(scrollInfo.nPage);
		::ScrollWindow(hMainWnd, 0, -yInc, NULL, NULL);
		SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
	}
	else
	{
		scrollInfo.nPos += yInc;
		::ScrollWindow(hMainWnd, 0, -yInc, NULL, NULL);
		SetScrollInfo(hMainWnd, SB_VERT, &scrollInfo, TRUE);
	}
	RedrawWindow(hMainWnd, NULL, NULL, RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_ALLCHILDREN);
}

 

 

 

Ссылка на комментарий
Поделиться на другие сайты

3 часа назад, Antonshka сказал:

Всё же есть решение - Layered Windows

А, точно, я про это забыл

 

3 часа назад, Antonshka сказал:

Чтобы проект Visual Studio поддерживал Layered Windows, необходимо в файле манифеста указать следующее

Необязательно, можно в мейне на старте приложения вызывать InitCommonControls или InitCommonControlsEx. Будет тот же эффект.

 

3 часа назад, Antonshka сказал:

Далее для всех них нужно вызвать API функцию SetLayeredWindowAttributes.

С помощью этой функции, примененной к главному окну, можно установить его прозрачность. Возможно, исчезновение мерцания - это побочный эффект.

Ссылка на комментарий
Поделиться на другие сайты

2 часа назад, Xipho сказал:

Необязательно, можно в мейне на старте приложения вызывать InitCommonControls или InitCommonControlsEx. Будет тот же эффект.

InitCommonControlsEx как я понял нужно вызывать для загрузки Comctl32.dll. Чтобы была возможность использовать Tooltip, Toolbar, и им подобные.

 

Без записи в манифесте

<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>

контролы со стилем WS_EX_LAYERED не будут отображаться совсем. Только лишь главное родительское окно, для которого по видимо эта возможность установлена по умолчанию.

Спойлер

In order to use layered child windows, the application has to declare itself Windows 8-aware in the manifest.

https://docs.microsoft.com/en-us/windows/win32/winmsg/using-windows#using-layered-windows

 

2 часа назад, Xipho сказал:

С помощью этой функции, примененной к главному окну, можно установить его прозрачность. Возможно, исчезновение мерцания - это побочный эффект.

Ага, также при стиле WS_EX_LAYERED можно и у дочернего элемента изменять его собственную прозрачность.

Исчезновение мерцания, как я понял из MSDN, связано с тем что система использует для компоновки финального изображения слои.

Спойлер

Using a layered window can significantly improve performance and visual effects for a window that has a complex shape, animates its shape, or wishes to use alpha blending effects. The system automatically composes and repaints layered windows and the windows of underlying applications. As a result, layered windows are rendered smoothly, without the flickering typical of complex window regions. In addition, layered windows can be partially translucent, that is, alpha-blended.

https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#layered-windows

 

Спасибо за помощь. Хорошо что все так хорошо закончилось.

Ссылка на комментарий
Поделиться на другие сайты

13 часов назад, Antonshka сказал:

контролы со стилем WS_EX_LAYERED не будут отображаться совсем. Только лишь главное родительское окно, для которого по видимо эта возможность установлена по умолчанию.

Да, вполне возможно, я этот стиль использовал только для главного окна.

 

13 часов назад, Antonshka сказал:

InitCommonControlsEx как я понял нужно вызывать для загрузки Comctl32.dll. Чтобы была возможность использовать Tooltip, Toolbar, и им подобные.

Не только. Я точно помню, что работы лейеринга и расширенных стилей добивался именно с помощью вызова этой функции, а не манифеста.

Ссылка на комментарий
Поделиться на другие сайты

32 минуты назад, Xipho сказал:

Да, вполне возможно, я этот стиль использовал только для главного окна.

 

Не только. Я точно помню, что работы лейеринга и расширенных стилей добивался именно с помощью вызова этой функции, а не манифеста.

 

Может Microsoft теперь сменила схему. Я вчера попробовал по твоему совету WS_EX_LAYERED для дочерних элементов без записи в манифесте

<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>

, но с вызовом

INITCOMMONCONTROLSEX icc;
icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
icc.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&icc);

- ничего не вышло, дочерние не отображались. Я на Windows 11 сейчас.

Я бы рад использовать только InitCommonControlsEx, без манифеста.

 

Для расширенных стилей я так понял в манифесте нужно прописать шестую версию

Спойлер

        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="*"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />

 

 

 

 

Изменено пользователем Antonshka
Ссылка на комментарий
Поделиться на другие сайты

4 часа назад, Antonshka сказал:

Я вчера попробовал по твоему совету WS_EX_LAYERED для дочерних элементов без записи в манифесте

Я ж говорю, я использовал его только для главного окна, потому не знаю, как работает для дочерних.

4 часа назад, Antonshka сказал:

icc.dwICC = ICC_WIN95_CLASSES;

Ну и тут классы версии винды 95 всё же несколько устарели, как мне кажется. Да и слои они явно не поддерживают. Может, тут надо было константу с более свежей версией поставить, не?

А, нет, это просто константы для набора классов. Да, значит, для дочерних обязательно нужен манифест, если нет другого способа нужную версию общих контролов затащить.

Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.