how to scroll a painted region? - c++

I want to scroll a painted region. I use the CS_OWNDC style with WS_HSCROLL | WS_VSCROLL.My WM_PAINT block draws a line outgoing from the visible region (I suspect that I need to increase the buffer somehow).How can I make this?Please give me an example
PS. Block of code WM_HSCROLL incorrect working i don't know how to fix this.
My code:
// C++ WINAPI LEARN
#include <Windows.h>
WNDCLASS windowClass;
HDC hdc;
PAINTSTRUCT ps;
int hScroll, vScroll;
bool first = true;
LRESULT CALLBACK WndProc(HWND hWindow, UINT typeMessage, WPARAM wParam, LPARAM lParam)
{
switch (typeMessage)
{
case WM_PAINT:
hdc = BeginPaint(hWindow, &ps);
//HERE ANYTHING DRAW CODE (words, line any figure)
//(draw line out visible region)
if (first == true)
{
first = false;
MoveToEx(hdc, 5, 5, NULL);
LineTo(hdc, 900, 500);
}
EndPaint(hWindow, &ps);
break;
case WM_HSCROLL:
//SCOLLBAR NOT JOB!
if (LOWORD(wParam) == SB_LINERIGHT) // this block not job!!!
{
hScroll++;
}
else if (LOWORD(wParam) == SB_LINELEFT) // this block not job!!!
{
hScroll--;
}
else if (LOWORD(wParam) == SB_THUMBTRACK)
{
hScroll=HIWORD(wParam);
}
SetScrollPos(hWindow, SB_HORZ, hScroll, FALSE);
ScrollWindow(hWindow, hScroll, vScroll, NULL, NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefWindowProc(hWindow, typeMessage, wParam, lParam);
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstanse, LPSTR LpszCmdParam, int nCmdShow)
{
//CREATE WINDOW
windowClass = { 0 };
windowClass.lpszClassName = "ScrollTest";
windowClass.hInstance = hInstance;
windowClass.lpfnWndProc = WndProc;
windowClass.style = CS_OWNDC;
//REGISTER CLASS
RegisterClass(&windowClass);
HWND myWindow = ::CreateWindowEx(
0,
"ScrollTest",
"Scroll Paint",
WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
100, 100,
500, 500,
NULL,
0,
hInstance,
0
);
//TRANSLATE MESSAGE
for (MSG windowMessage; GetMessage(&windowMessage, NULL, 0, 0);)
{
DispatchMessage(&windowMessage); //TRANSLATE MESSAGES
TranslateMessage(&windowMessage); //TRANSLATE KEYS
}
}

Based on the demo, I modified part of the code and it works normally.
Some code:
LRESULT CALLBACK MyTextWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
SCROLLINFO si;
// These variables are required to display text.
static int xChar; // horizontal scrolling unit
static int yChar; // vertical scrolling unit
static int xPos; // current horizontal scrolling position
static int yPos; // current vertical scrolling position
int x, y; // horizontal and vertical coordinates
// Create an array of lines to display.
switch (uMsg)
{
case WM_CREATE:
// Get the handle to the client area's device context.
hdc = GetDC(hwnd);
// Extract font dimensions from the text metrics.
GetTextMetrics(hdc, &tm);
xChar = tm.tmAveCharWidth;
yChar = tm.tmHeight + tm.tmExternalLeading;
// Free the device context.
ReleaseDC(hwnd, hdc);
return 0;
case WM_HSCROLL:
// Get all the vertial scroll bar information.
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
// Save the position for comparison later on.
GetScrollInfo(hwnd, SB_HORZ, &si);
xPos = si.nPos;
switch (LOWORD(wParam))
{
// User clicked the left arrow.
case SB_LINELEFT:
si.nPos -= 1;
break;
// User clicked the right arrow.
case SB_LINERIGHT:
si.nPos += 1;
break;
// User clicked the scroll bar shaft left of the scroll box.
case SB_PAGELEFT:
si.nPos -= si.nPage;
break;
// User clicked the scroll bar shaft right of the scroll box.
case SB_PAGERIGHT:
si.nPos += si.nPage;
break;
// User dragged the scroll box.
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default:
break;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
GetScrollInfo(hwnd, SB_HORZ, &si);
// If the position has changed, scroll the window.
if (si.nPos != xPos)
{
ScrollWindow(hwnd, xChar * (xPos - si.nPos), 0, NULL, NULL);
}
return 0;
case WM_VSCROLL:
// Get all the vertial scroll bar information.
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_VERT, &si);
// Save the position for comparison later on.
yPos = si.nPos;
switch (LOWORD(wParam))
{
// User clicked the HOME keyboard key.
case SB_TOP:
si.nPos = si.nMin;
break;
// User clicked the END keyboard key.
case SB_BOTTOM:
si.nPos = si.nMax;
break;
// User clicked the top arrow.
case SB_LINEUP:
si.nPos -= 1;
break;
// User clicked the bottom arrow.
case SB_LINEDOWN:
si.nPos += 1;
break;
// User clicked the scroll bar shaft above the scroll box.
case SB_PAGEUP:
si.nPos -= si.nPage;
break;
// User clicked the scroll bar shaft below the scroll box.
case SB_PAGEDOWN:
si.nPos += si.nPage;
break;
// User dragged the scroll box.
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default:
break;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
// If the position has changed, scroll window and update it.
if (si.nPos != yPos)
{
ScrollWindow(hwnd, 0, yChar * (yPos - si.nPos), NULL, NULL);
UpdateWindow(hwnd);
}
return 0;
case WM_PAINT:
// Prepare the window for painting.
hdc = BeginPaint(hwnd, &ps);
// Get vertical scroll bar position.
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
GetScrollInfo(hwnd, SB_VERT, &si);
yPos = si.nPos;
// Get horizontal scroll bar position.
GetScrollInfo(hwnd, SB_HORZ, &si);
xPos = si.nPos;
x = xChar * (1 - xPos);
y = yChar * (1 - yPos);
MoveToEx(hdc, 5 + x, 5 + y, NULL);
LineTo(hdc, 900 + x, 500 + y);
// Indicate that painting is finished.
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Debug:

Related

Scrollbar duplicate winapi c++

I am making a scrollbar for the first time and don't understand why I have a duplicate of my scrollbar when content is scrollable.
Here is how it looks, I don't know if you can see it good enough but here's a screenshot; one scrollbar is the thick gray one (the one a want to keep, looks more like the classic scrollbar), and one is a little gray rectangle to the left of it. Both of these scrollbars work and scroll my program.
:
But when content is not scrollable then it looks like this:
Here is my ScrollBar.cpp if this will help:
#include "ScrollBar.h"
ScrollBar::ScrollBar(){}
ScrollBar::~ScrollBar(){
RemoveWindowSubclass(hWnd, scrollProc, IDC_STATIC);
delete con;
}
ScrollBar::ScrollBar(HWND hWnd, HINSTANCE hInst) : hWnd(hWnd),hInst(hInst) {
this->hWndParent = GetParent(hWnd);
con = new Controls(hWndParent);
}
void ScrollBar::createScroll(int x, int y, int w, int h, bool stickRight, bool stickBottom) {
this->x = x;
this->y = y;
this->width = w;
this->height = h;
hScroll = con->create("scrollbar", WS_VISIBLE | WS_CHILD | SBS_VERT, x, y, width, height,"scrollBar","",0, stickRight, stickBottom);
//Creates like this
/// CreateWindowEx(0, type, text, styles, cData.x, cData.y, cData.width, cData.height, hWnd, (HMENU)id, NULL, (LPVOID)lParam);
SetWindowSubclass(hScroll, scrollProc, IDC_STATIC, (DWORD_PTR)this);
SetWindowSubclass(hWndParent, scrollProc, IDC_STATIC, (DWORD_PTR)this);
con->setEventListener();
}
void ScrollBar::setScrollRange(int scrollMaxHeight) {
scrollMaxH = scrollMaxHeight == -1 ? scrollMaxH : scrollMaxHeight;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE;
si.nMin = 0;
si.nMax = scrollMaxH;
SetScrollInfo(hScroll, SB_VERT, &si, false);
}
void ScrollBar::setScrollSize() {
SCROLLINFO oSi = { 0 };
oSi.cbSize = sizeof(oSi);
oSi.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
GetScrollInfo(hScroll, SB_VERT, &oSi);
RECT rect = GetLocalCoordinates(hWnd);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_PAGE;
si.nPage = height;
SetScrollInfo(hScroll, SB_VERT, &si, TRUE);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
GetScrollInfo(hScroll, SB_VERT, &si);
int res = oSi.nPos - si.nPos;
if (res > 0) {
setChildScrollOffset(hWnd, res);
}
}
void ScrollBar::handleVScroll(int action,int thum) {
int nPos;
int nOldPos;
SCROLLINFO si = { 0 };
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
GetScrollInfo(hScroll, SB_VERT, &si);
nOldPos = si.nPos;
switch (action)
{
case SB_TOP:
nPos = si.nMin;
break;
case SB_BOTTOM:
nPos = si.nMax;
break;
case SB_LINEUP:
nPos = si.nPos - 30;
break;
case SB_LINEDOWN:
nPos = si.nPos + 30;
break;
case SB_PAGEUP:
nPos = si.nPos - 30;
break;
case SB_PAGEDOWN:
nPos = si.nPos + 30;
break;
case SB_THUMBTRACK:
nPos = thum;
break;
default:
nPos = si.nPos;
break;
}
SetScrollPos(hScroll, SB_VERT, nPos, TRUE);
nPos = GetScrollPos(hScroll, SB_VERT);
if (nOldPos != nPos) {
ScrollWindowEx(hWnd, 0, nOldPos - nPos, NULL, NULL, NULL, NULL,
SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN);
UpdateWindow(hWnd);
setChildScrollOffset(hWnd, nOldPos - nPos);
}
}
void ScrollBar::setChildScrollOffset(HWND h, int offset) {
Controls* childsCon = reinterpret_cast<Controls*>(GetWindowLongPtr(h, GWLP_USERDATA));
for (auto it : childsCon->sControls)
{
childsCon->getControlItem(it.first)->y += offset;
}
}
LRESULT CALLBACK ScrollBar::scrollProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
ScrollBar* lpData = (ScrollBar*)dwRefData;
HDC hdc = (HDC)wParam;
PAINTSTRUCT ps;
HBRUSH hbrBackground;
switch (uMsg) {
case WM_SIZE:
{
lpData->setScrollSize();
break;
}
case WM_VSCROLL:
{
lpData->handleVScroll(LOWORD(wParam), HIWORD(wParam));
return true;
}
case WM_MOUSEWHEEL:
{
short zDelta = (short)(0xFFFF & (wParam >> 16));
if (zDelta > 0)
PostMessage(lpData->hScroll, WM_VSCROLL, SB_LINEUP, NULL);
else
PostMessage(lpData->hScroll, WM_VSCROLL, SB_LINEDOWN, NULL);
return 0;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
I am blind and so can't see your screen shots but does your parent window have WS_VSCROLL? (you don't show the styles associated with that window). If so remove it, the WS_VSCROLL adds a scrollbar that is not control-based to the window's non-client area.
You need to distinguish between Standard Scroll Bars and Scroll Bar Controls. A scroll bar control is a control window that belongs to the SCROLLBAR window class.
For more details about how to Create Scroll Bar, I suggest you could refer to the Doc: How to Create Scroll Bars
According to the code, you want to create a Scroll Bar Controls. So you should creates a window without standard horizontal and vertical scroll bars and then use the CreateWindowEx function to create a scroll bar by specifying the SCROLLBAR window class.
Could you please show a minimal, reproducible sample?

Link SCROLLINFO to my scrollbar — WINAPI C++

I have a window with a working default scrollbar.
Now I'd like to link all the code I have to the custom scrollbar created via CreateWindow
Here is the procedure that
LRESULT CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) {
switch (msg) {
case WM_COMMAND:
if(wParam == ID_CLOSECROSS){
if(IsWindowVisible(aboutPanel)) SwitchToWindow(aboutPanel); else SwitchToWindow(howToPanel);
}
break;
case WM_MOUSEWHEEL:{
if((short)HIWORD(wParam) > 0){
SendMessage(hwnd, WM_VSCROLL, VK_UP, 0);
}else{
SendMessage(hwnd, WM_VSCROLL, VK_DOWN, 0);
}
break;
}
case WM_VSCROLL:{
RECT hrc; GetClientRect(hwnd, &hrc);
SCROLLINFO si;
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
GetScrollInfo (hwnd, SB_VERT, &si);
int pos;
yPos = si.nPos;
switch (LOWORD (wParam)){
case SB_TOP: si.nPos = si.nMin; break;
case SB_BOTTOM: si.nPos = si.nMax; break;
case SB_LINEUP: si.nPos -= 1; break;
case SB_LINEDOWN: si.nPos += 1; break;
case VK_UP: si.nPos -= 20; break;
case VK_DOWN: si.nPos += 20; break;
case SB_PAGEUP: si.nPos -= si.nPage; break;
case SB_PAGEDOWN: si.nPos += si.nPage; break;
case SB_THUMBTRACK: si.nPos = si.nTrackPos; pos = HIWORD(wParam); break;
}
si.fMask = SIF_POS;
SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
GetScrollInfo (hwnd, SB_VERT, &si);
if (si.nPos != yPos){
ScrollWindow(centerHowToPanel, 0, scrollSpeed*(yPos-si.nPos), NULL, &hrc);
// here
UpdateWindow (centerHowToPanel);
}
return 0;
}
case WM_SIZE:{
RECT hrc = { 0 };
GetClientRect(hwnd, &hrc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = 30 * 99 + 21;
si.nPage = (hrc.bottom - hrc.top);
si.nPos = yPos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
// here
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Here is my scrollbar I create in WinMain:
howToScroll = CreateWindow(L"SCROLLBAR", NULL, WS_VISIBLE|WS_CHILD|SBS_VERT, 0, 0, 0, 0, howToPanel, NULL, NULL, NULL);
If I replace each (Get|Set)ScrollInfo(hwnd, ...) with (Get|Set)ScrollInfo(howToScroll, ...) in the procedure it almost works. I can use scrollbar's arrows, but can't drag it. Also mouse wheel doesn't seem to change custom scrollbar's position.
I tried to use SetScrollPos(howToScroll, SB_CTL, yPos, 1); in the procedure at places where is commented 'here' word but it doesn't help either.
Also when resizing my custom scrollbar doesn't change it's size as default one does.
Just a quick demostration how it works with the default scrollbar.
                                                             
And here how it works with the custom one.
                                                             
Edit 1
I managed to make it scroll!
LRESULT CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) {
switch (msg) {
case WM_COMMAND:
if(wParam == ID_CLOSECROSS){
if(IsWindowVisible(aboutPanel)) SwitchToWindow(aboutPanel); else SwitchToWindow(howToPanel);
}
break;
case WM_MOUSEWHEEL:{
if((short)HIWORD(wParam) > 0){
SendMessage(hwnd, WM_VSCROLL, VK_UP, 0);
}else{
SendMessage(hwnd, WM_VSCROLL, VK_DOWN, 0);
}
break;
}
case WM_VSCROLL:{
RECT hrc; GetClientRect(hwnd, &hrc);
SCROLLINFO si;
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
GetScrollInfo (howToScroll, SB_VERT, &si);
int pos;
yPos = si.nPos;
switch (LOWORD (wParam)){
case SB_TOP: si.nPos = si.nMin; break;
case SB_BOTTOM: si.nPos = si.nMax; break;
case SB_LINEUP: si.nPos -= 1; break;
case SB_LINEDOWN: si.nPos += 1; break;
case VK_UP: si.nPos -= 20; break;
case VK_DOWN: si.nPos += 20; break;
case SB_PAGEUP: si.nPos -= si.nPage; break;
case SB_PAGEDOWN: si.nPos += si.nPage; break;
case SB_THUMBTRACK: si.nPos = HIWORD(wParam); break;
}
SetScrollPos(howToScroll, SB_CTL, yPos, true);
si.fMask = SIF_POS;
SetScrollInfo (howToScroll, SB_VERT, &si, TRUE);
GetScrollInfo (howToScroll, SB_VERT, &si);
if (si.nPos != yPos){
ScrollWindow(centerHowToPanel, 0, scrollSpeed*(yPos-si.nPos), NULL, &hrc);
UpdateWindow (centerHowToPanel);
}
return 0;
}
case WM_SIZE:{
RECT hrc = { 0 };
GetClientRect(hwnd, &hrc);
SCROLLINFO si = { 0 };
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = hrc.bottom+40;
SetScrollRange(howToScroll, SB_CTL, 0, hrc.bottom+40, TRUE);
si.nPage = (hrc.bottom - hrc.top);
si.nPos = yPos;
si.nTrackPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, true);
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Now it seems like the head of the scroll is not big enough so it doesn't goes to the end of the bar.
                                                             
The only thing I need to manage -- the size of the scroll.
Edit 2
If I replace every single SB_VERT with SB_CTL it works as should! Thanks #JonathanPotter for that.

MSDN example for scrolling large image malfunctions

INTRODUCTION AND RELEVANT INFORMATION:
I am trying to paint the image in my main window in its natural size. Currently I need to render EMF.
After browsing through Internet, I found this MSDN example.
PROBLEM:
I have given it a try, and found out it doesn't work well.
After user right clicks, as instructions say, desktop screenshot is properly painted in the main window.
However, when user resizes the window, paint artifacts occur like the ones on the image below. The below effect also occurs when user scrolls a little and then resizes the window.
Here is the image:
This is my first time trying to scroll an image, and using scrollbars, so I really have a hard time figuring out a solution.
QUESTIONS:
How to fix article code to remove this visual artifact?
Can you recommend another example, like tutorial/code example/etc that
I can study?
Can you provide "verbal" instructions/guidelines that could help me?
To make your task even easier, here is the smallest code possible that reproduces the problem. Take note that I have simply copy/pasted the window procedure and added minimal code for program to create working demonstration:
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
SCROLLINFO si;
// These variables are required by BitBlt.
static HDC hdcWin; // window DC
static HDC hdcScreen; // DC for entire screen
static HDC hdcScreenCompat; // memory DC for screen
static HBITMAP hbmpCompat; // bitmap handle to old DC
static BITMAP bmp; // bitmap data structure
static BOOL fBlt; // TRUE if BitBlt occurred
static BOOL fScroll; // TRUE if scrolling occurred
static BOOL fSize; // TRUE if fBlt & WM_SIZE
// These variables are required for horizontal scrolling.
static int xMinScroll; // minimum horizontal scroll value
static int xCurrentScroll; // current horizontal scroll value
static int xMaxScroll; // maximum horizontal scroll value
// These variables are required for vertical scrolling.
static int yMinScroll; // minimum vertical scroll value
static int yCurrentScroll; // current vertical scroll value
static int yMaxScroll; // maximum vertical scroll value
switch (uMsg)
{
case WM_CREATE:
// Create a normal DC and a memory DC for the entire
// screen. The normal DC provides a snapshot of the
// screen contents. The memory DC keeps a copy of this
// snapshot in the associated bitmap.
hdcScreen = CreateDC(L"DISPLAY", (PCTSTR)NULL,
(PCTSTR)NULL, (CONST DEVMODE *) NULL);
hdcScreenCompat = CreateCompatibleDC(hdcScreen);
// Retrieve the metrics for the bitmap associated with the
// regular device context.
bmp.bmBitsPixel =
(BYTE)GetDeviceCaps(hdcScreen, BITSPIXEL);
bmp.bmPlanes = (BYTE)GetDeviceCaps(hdcScreen, PLANES);
bmp.bmWidth = GetDeviceCaps(hdcScreen, HORZRES);
bmp.bmHeight = GetDeviceCaps(hdcScreen, VERTRES);
// The width must be byte-aligned.
bmp.bmWidthBytes = ((bmp.bmWidth + 15) &~15) / 8;
// Create a bitmap for the compatible DC.
hbmpCompat = CreateBitmap(bmp.bmWidth, bmp.bmHeight,
bmp.bmPlanes, bmp.bmBitsPixel, (CONST VOID *) NULL);
// Select the bitmap for the compatible DC.
SelectObject(hdcScreenCompat, hbmpCompat);
// Initialize the flags.
fBlt = FALSE;
fScroll = FALSE;
fSize = FALSE;
// Initialize the horizontal scrolling variables.
xMinScroll = 0;
xCurrentScroll = 0;
xMaxScroll = 0;
// Initialize the vertical scrolling variables.
yMinScroll = 0;
yCurrentScroll = 0;
yMaxScroll = 0;
break;
case WM_SIZE:
{
int xNewSize;
int yNewSize;
xNewSize = LOWORD(lParam);
yNewSize = HIWORD(lParam);
if (fBlt)
fSize = TRUE;
// The horizontal scrolling range is defined by
// (bitmap_width) - (client_width). The current horizontal
// scroll value remains within the horizontal scrolling range.
xMaxScroll = max(bmp.bmWidth - xNewSize, 0);
xCurrentScroll = min(xCurrentScroll, xMaxScroll);
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = xMinScroll;
si.nMax = bmp.bmWidth;
si.nPage = xNewSize;
si.nPos = xCurrentScroll;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
// The vertical scrolling range is defined by
// (bitmap_height) - (client_height). The current vertical
// scroll value remains within the vertical scrolling range.
yMaxScroll = max(bmp.bmHeight - yNewSize, 0);
yCurrentScroll = min(yCurrentScroll, yMaxScroll);
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = yMinScroll;
si.nMax = bmp.bmHeight;
si.nPage = yNewSize;
si.nPos = yCurrentScroll;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
break;
}
case WM_PAINT:
{
PRECT prect;
hdc = BeginPaint(hwnd, &ps);
// If the window has been resized and the user has
// captured the screen, use the following call to
// BitBlt to paint the window's client area.
if (fSize)
{
BitBlt(ps.hdc,
0, 0,
bmp.bmWidth, bmp.bmHeight,
hdcScreenCompat,
xCurrentScroll, yCurrentScroll,
SRCCOPY);
fSize = FALSE;
}
// If scrolling has occurred, use the following call to
// BitBlt to paint the invalid rectangle.
//
// The coordinates of this rectangle are specified in the
// RECT structure to which prect points.
//
// Note that it is necessary to increment the seventh
// argument (prect->left) by xCurrentScroll and the
// eighth argument (prect->top) by yCurrentScroll in
// order to map the correct pixels from the source bitmap.
if (fScroll)
{
prect = &ps.rcPaint;
BitBlt(ps.hdc,
prect->left, prect->top,
(prect->right - prect->left),
(prect->bottom - prect->top),
hdcScreenCompat,
prect->left + xCurrentScroll,
prect->top + yCurrentScroll,
SRCCOPY);
fScroll = FALSE;
}
EndPaint(hwnd, &ps);
break;
}
case WM_HSCROLL:
{
int xDelta; // xDelta = new_pos - current_pos
int xNewPos; // new position
int yDelta = 0;
switch (LOWORD(wParam))
{
// User clicked the scroll bar shaft left of the scroll box.
case SB_PAGEUP:
xNewPos = xCurrentScroll - 50;
break;
// User clicked the scroll bar shaft right of the scroll box.
case SB_PAGEDOWN:
xNewPos = xCurrentScroll + 50;
break;
// User clicked the left arrow.
case SB_LINEUP:
xNewPos = xCurrentScroll - 5;
break;
// User clicked the right arrow.
case SB_LINEDOWN:
xNewPos = xCurrentScroll + 5;
break;
// User dragged the scroll box.
case SB_THUMBPOSITION:
xNewPos = HIWORD(wParam);
break;
default:
xNewPos = xCurrentScroll;
}
// New position must be between 0 and the screen width.
xNewPos = max(0, xNewPos);
xNewPos = min(xMaxScroll, xNewPos);
// If the current position does not change, do not scroll.
if (xNewPos == xCurrentScroll)
break;
// Set the scroll flag to TRUE.
fScroll = TRUE;
// Determine the amount scrolled (in pixels).
xDelta = xNewPos - xCurrentScroll;
// Reset the current scroll position.
xCurrentScroll = xNewPos;
// Scroll the window. (The system repaints most of the
// client area when ScrollWindowEx is called; however, it is
// necessary to call UpdateWindow in order to repaint the
// rectangle of pixels that were invalidated.)
ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
(CONST RECT *) NULL, (HRGN)NULL, (PRECT)NULL,
SW_INVALIDATE);
UpdateWindow(hwnd);
// Reset the scroll bar.
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
si.nPos = xCurrentScroll;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
break;
}
case WM_VSCROLL:
{
int xDelta = 0;
int yDelta; // yDelta = new_pos - current_pos
int yNewPos; // new position
switch (LOWORD(wParam))
{
// User clicked the scroll bar shaft above the scroll box.
case SB_PAGEUP:
yNewPos = yCurrentScroll - 50;
break;
// User clicked the scroll bar shaft below the scroll box.
case SB_PAGEDOWN:
yNewPos = yCurrentScroll + 50;
break;
// User clicked the top arrow.
case SB_LINEUP:
yNewPos = yCurrentScroll - 5;
break;
// User clicked the bottom arrow.
case SB_LINEDOWN:
yNewPos = yCurrentScroll + 5;
break;
// User dragged the scroll box.
case SB_THUMBPOSITION:
yNewPos = HIWORD(wParam);
break;
default:
yNewPos = yCurrentScroll;
}
// New position must be between 0 and the screen height.
yNewPos = max(0, yNewPos);
yNewPos = min(yMaxScroll, yNewPos);
// If the current position does not change, do not scroll.
if (yNewPos == yCurrentScroll)
break;
// Set the scroll flag to TRUE.
fScroll = TRUE;
// Determine the amount scrolled (in pixels).
yDelta = yNewPos - yCurrentScroll;
// Reset the current scroll position.
yCurrentScroll = yNewPos;
// Scroll the window. (The system repaints most of the
// client area when ScrollWindowEx is called; however, it is
// necessary to call UpdateWindow in order to repaint the
// rectangle of pixels that were invalidated.)
ScrollWindowEx(hwnd, -xDelta, -yDelta, (CONST RECT *) NULL,
(CONST RECT *) NULL, (HRGN)NULL, (PRECT)NULL,
SW_INVALIDATE);
UpdateWindow(hwnd);
// Reset the scroll bar.
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
si.nPos = yCurrentScroll;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
break;
}
case WM_RBUTTONDOWN:
{
// Get the compatible DC of the client area.
hdcWin = GetDC(hwnd);
// Fill the client area to remove any existing contents.
RECT rect;
GetClientRect(hwnd, &rect);
FillRect(hdcWin, &rect, (HBRUSH)(COLOR_WINDOW + 1));
// Copy the contents of the current screen
// into the compatible DC.
BitBlt(hdcScreenCompat, 0, 0, bmp.bmWidth,
bmp.bmHeight, hdcScreen, 0, 0, SRCCOPY);
// Copy the compatible DC to the client area.
BitBlt(hdcWin, 0, 0, bmp.bmWidth, bmp.bmHeight,
hdcScreenCompat, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hdcWin);
fBlt = TRUE;
break;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
// register main window class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName = NULL;
wc.lpszClassName = L"Main_Window";
wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// create main window
hwnd = CreateWindowEx(0, L"Main_Window", L"Scrollable map",
WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
50, 50, 400, 400, NULL, NULL, hInstance, 0);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
From your description the problem seems to be simply that your window isn't being repainted when it's resized.
Either use InvalidateRect to force a repaint of the newly uncovered areas when your window is enlarged, or set the CS_VREDRAW and CS_HREDRAW styles on your window class to have the client area repainted automatically.

Win32 Draw a Dragging Rectangle

I am trying to draw a rectangle as you left click on a point and then drag across , while your dragging across the rectangle gets form to show you a preview of how the rectangle will look . This works fine except when I drag back into a smaller rectangle the old rectangles still remain.
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
if(painting)
{
HPEN greenPen=CreatePen(PS_SOLID, 1, RGB(0,255,0));
SelectObject(hdc,greenPen);
Rectangle(hdc, x1, y1, x2, y2);
}
EndPaint(hWnd, &ps);
break;
case WM_LBUTTONDOWN:
painting=true;
x1=LOWORD(lParam);
y1=HIWORD(lParam);
InvalidateRect(hWnd, NULL ,false);
break;
case WM_MOUSEMOVE:
x2=LOWORD(lParam);
y2=HIWORD(lParam);
InvalidateRect(hWnd, NULL ,false);
break;
case WM_LBUTTONUP:
painting=false;
break;
#user1788175 - just use a RECT struct. When the drawing starts, you set the left & top members to the x,y pos of the mouse. When the mouse is released, set the right,bottom members. Swap left,right if necessary, to ensure that left
Here's some code ripped from a class I wrote to deal with with selection rectangles. You can ignore the normalize code - it just converts the pixel coordinates to ones in the range [0..1], so I can draw the selection on a scaled-down version of the image, yet still select the same region if the image is shown at a different scale. My images were 4944x6992, so I had to display a scaled-down version of them.
LRESULT CALLBACK cSelBoxImg::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
RECT mappedRect, tmpRect, myRect;
HBRUSH myBrush;
BITMAP bm;
PAINTSTRUCT ps;
HDC mDC;
HBITMAP tmpBmp;
switch (uMsg)
{
case WM_LBUTTONDOWN:
if (bMouseSelectionEnabled)
{
bRubberBanding = true;
mouseClickDownPos.x = LOWORD(lParam);
mouseClickDownPos.y = HIWORD(lParam);
curMousePos = mouseClickDownPos;
selectionRect.left = min(curMousePos.x, mouseClickDownPos.x);
selectionRect.right = max(curMousePos.x, mouseClickDownPos.x);
selectionRect.top = min(curMousePos.y, mouseClickDownPos.y);
selectionRect.bottom = max(curMousePos.y, mouseClickDownPos.y);
normalizeSelRect();
InvalidateRect(mHwnd, NULL, isBkgTransparent);
PostMessage(GetParent(hWnd), WM_COMMAND, GetDlgCtrlID(hWnd), (LPARAM)hWnd);
}
return 1;
case WM_LBUTTONUP:
if (bMouseSelectionEnabled)
if (bRubberBanding)
{
bRubberBanding = false;
mouseClickUpPos.x = LOWORD(lParam);
mouseClickUpPos.y = HIWORD(lParam);
selectionRect.left = min(mouseClickUpPos.x, mouseClickDownPos.x);
selectionRect.right = max(mouseClickUpPos.x, mouseClickDownPos.x);
selectionRect.top = min(mouseClickUpPos.y, mouseClickDownPos.y);
selectionRect.bottom = max(mouseClickUpPos.y, mouseClickDownPos.y);
normalizeSelRect();
InvalidateRect(mHwnd, NULL, isBkgTransparent);
PostMessage(GetParent(hWnd), WM_COMMAND, GetDlgCtrlID(hWnd), (LPARAM)hWnd);
}
return 1;
case WM_MOUSEMOVE:
if (bMouseSelectionEnabled)
if (bRubberBanding)
{
curMousePos.x = LOWORD(lParam);
curMousePos.y = HIWORD(lParam);
selectionRect.left = min(curMousePos.x, mouseClickDownPos.x);
selectionRect.right = max(curMousePos.x, mouseClickDownPos.x);
selectionRect.top = min(curMousePos.y, mouseClickDownPos.y);
selectionRect.bottom = max(curMousePos.y, mouseClickDownPos.y);
// UpdateWindow(hWnd);
//RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW);
normalizeSelRect();
InvalidateRect(hWnd, NULL, false);
PostMessage(GetParent(hWnd), WM_COMMAND, GetDlgCtrlID(hWnd), (LPARAM)hWnd);
// printf("Message posted\n");
}
return 0;
case WM_PAINT:
mDC = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &tmpRect);
// GetObject(mBmp, sizeof(bm), &bm);
mappedRect.left = mLeft * tmpRect.right;
mappedRect.right = mRight * tmpRect.right;
mappedRect.top = mTop * tmpRect.bottom;
mappedRect.bottom = mBottom * tmpRect.bottom;
displayImage();
if (mBmp) drawRect(mDC, mappedRect, RGB(0,0,255));
DeleteObject(tmpBmp);
EndPaint(hWnd, &ps);
return 0;
case WM_ERASEBKGND:
if (isBkgTransparent)
{
GetClientRect(hWnd, &myRect);
myBrush = (HBRUSH) GetWindowLong(hWnd, GCL_HBRBACKGROUND);
FillRect((HDC)wParam, &myRect, myBrush);
printf("background cleared\n");
}
return true;
case WM_SETCURSOR:
SetCursor(mCursor);
return true;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

Custom edit control win32

I finally managed to get my syntax highlighting done using richedit and iczelion's tutorials.
Now that i find it, it certainly is not fast enough. I am thinking of taking this one step ahead: a custom edit control. But i do not know how to go about it. Could you guys tell me how to go about it? Give me some info to start on? Maybe even some tutorial or suggest some book?
Now I'm not asking for you guys to spell it out for me, just something to start on. I will be using C++/ASM/Win32 API for this. I'm sure many of you have already made custom edit controls befores, so may be you could even share your experience.
Thanks,
Devjeet
I spent one day on coding my own custom edit control - it's working well, so I would like to share my experience here, maybe for someone this code might be helpful... Because custom draw an common edit control is impossible (see here), you must write your own edit control. The general steps are:
// global vars
int select; // current selection position
int cursor; // current cursor position
HWND parent; // parent window
wchar_t buf[MAXINPUTBUF]; // edit buffer
WNDPROC oldproc; // old window procedure
// create custom control window
hWnd = CreateWindowW(L"static", NULL, WS_CHILD | WS_TABSTOP | SS_LEFT | SS_NOTIFY, 0, 0, 0, 0, parent, NULL, (HINSTANCE)GetWindowLongPtr(parent, GWL_HINSTANCE), NULL);
// todo: use SetProp() to store all global vars
oldproc = (WNDPROC)SetWindowLongPtrW(hWnd, GWL_WNDPROC, (LONG_PTR)InputWndProc);
SetWindowPos(hWnd, HWND_TOP, x, y, cx, cy, 0);
How to display keyboard input is described here. My window procedure looks as follows
LRESULT CALLBACK InputWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_LBUTTONDOWN:
//SetFocus(hWnd);
PostMessageW(GetParent(hWnd), WM_NEXTDLGCTL, (WPARAM)hWnd, TRUE);
break;
case WM_KILLFOCUS:
HideCaret(hWnd);
DestroyCaret();
break;
case WM_SETFOCUS:
{
RECT r;
GetClientRect(hWnd, &r);
// Create a solid black caret.
CreateCaret(hWnd, (HBITMAP) NULL, 2, r.bottom-r.top);
ShowCaret(hWnd);
InputWndRedraw(hWnd);
}
return FALSE;
case WM_GETDLGCODE:
return DLGC_WANTALLKEYS | DLGC_WANTARROWS;
case WM_KEYDOWN:
{
switch (wParam)
{
case 'V':
if (0x8000 & GetKeyState(VK_CONTROL))
{
HANDLE h;
wchar_t *cb;
int len,slen;
InputWndDelete(hWnd);
OpenClipboard(NULL);
h = GetClipboardData(CF_UNICODETEXT);
cb = (wchar_t*)GlobalLock(h);
if (cb)
{
memcpy(buf+(cursor+len)*sizeof(wchar_t), buf+cursor*sizeof(wchar_t), (slen-cursor)*sizeof(wchar_t));
memcpy(buf+cursor*sizeof(wchar_t), cb, len*sizeof(wchar_t));
}
GlobalUnlock(h);
CloseClipboard();
InputWndRedraw(hWnd);
}
break;
case VK_RIGHT:
if (cursor-1 >= MAXINPUTBUF || cursor >= (int)wcslen(buf))
break;
cursor++;
if (!(GetKeyState(VK_SHIFT) & 0x8000))
select = cursor;
InputWndRedraw(hWnd);
break;
case VK_TAB:
PostMessageW(GetParent(hWnd), WM_NEXTDLGCTL, GetKeyState(VK_SHIFT) & 0x8000, FALSE);
break;
case VK_LEFT:
if (cursor <= 0)
break;
cursor--;
if (!(GetKeyState(VK_SHIFT) & 0x8000))
select = cursor;
InputWndRedraw(hWnd);
break;
case VK_HOME:
cursor = 0;
if (!(GetKeyState(VK_SHIFT) & 0x8000))
select = cursor;
InputWndRedraw(hWnd);
break;
case VK_END:
cursor = wcslen(buf);
if (!(GetKeyState(VK_SHIFT) & 0x8000))
select = cursor;
InputWndRedraw(hWnd);
break;
case VK_DELETE:
if (cursor >= (int)wcslen(buf))
{
InputWndDelete(hWnd);
InputWndRedraw(hWnd);
break;
}
if (select == cursor)
select ++;
InputWndDelete(hWnd);
InputWndRedraw(hWnd);
break;
case VK_BACK:
if (cursor <= 0)
{
InputWndDelete(hWnd);
InputWndRedraw(hWnd);
break;
}
if (select == cursor)
cursor --;
InputWndDelete(hWnd);
InputWndRedraw(hWnd);
}
}
break;
case WM_CHAR:
if (wParam < VK_SPACE)
break;
InputWndDelete(hWnd);
if (wcslen(buf)+1 < MAXINPUTBUF)
{
wmemmove(buf+(cursor+1)*sizeof(wchar_t), buf+cursor*sizeof(wchar_t), wcslen(s->buf)-cursor);
buf[cursor] = wParam;
cursor++;
select = cursor;
}
InputWndRedraw(hWnd);
break;
case WM_ERASEBKGND:
// no flickering
return TRUE;
case WM_PAINT:
{
HDC dc;
PAINTSTRUCT paint;
dc = BeginPaint(hWnd, &paint);
InputWndDraw(hWnd, dc);
EndPaint(hWnd, &paint);
}
return TRUE;
}
return CallWindowProcW(oldproc, hWnd, msg, wParam, lParam);
}
Delete current selected text (from select to cursor).
void InputWndDelete(HWND hWnd)
{
int len;
len = wcslen(buf);
if (select > cursor)
{
memcpy(buf+cursor*sizeof(wchar_t), buf+select*sizeof(wchar_t), (len - select)*sizeof(wchar_t));
ZeroMemory(buf+(len-select+cursor)*sizeof(wchar_t), (MAXINPUTBUF-len+select-cursor)*sizeof(wchar_t));
select = cursor;
}
else if (select < cursor)
{
memcpy(buf+select*sizeof(wchar_t), buf+cursor*sizeof(wchar_t), (len - cursor)*sizeof(wchar_t));
ZeroMemory(buf+(len-cursor+select)*sizeof(wchar_t), (MAXINPUTBUF-len+cursor-select)*sizeof(wchar_t));
cursor = select;
}
else
{
select = cursor;
}
}
Draw window on window DC
void InputWndRedraw(HWND hWnd)
{
HDC hdc;
HideCaret(hWnd);
hdc = GetDC(hWnd);
InputWndDraw(hWnd, hdc);
ReleaseDC(hWnd, hdc);
ShowCaret(hWnd);
}
Draw input buffer (buf*) on device context. Syntax highlighting and other formatting features goes here...
void InputWndDraw(HWND hWnd, HDC hdc)
{
RECT r,cr;
GetClientRect(hWnd, &cr);
// draw selected rectangle FillRect()...
CopyRect(&r,&cr);
DrawTextW(hdc, buf, -1, &r, DT_LEFT | DT_TOP);
if (cursor)
DrawTextW(hdc, buf, cursor, &r, DT_LEFT | DT_TOP | DT_CALCRECT);
else
r.right = cr.left;
if (GetFocus() == hWnd)
{
if (r.right > cr.right)
SetCaretPos(cr.right, cr.top);
else
SetCaretPos(r.right, cr.top);
}
}