Форум программистов, компьютерный форум, киберфорум
C/C++: WinAPI
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.50/8: Рейтинг темы: голосов - 8, средняя оценка - 4.50
0 / 0 / 0
Регистрация: 19.12.2021
Сообщений: 56

Scrollbar в дочернем окне

08.01.2024, 19:57. Показов 1765. Ответов 3

Студворк — интернет-сервис помощи студентам
Как заставить работать scrollbar в окне hStatic?

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#include <windows.h>
// Файлы заголовков среды выполнения C
#include <stdlib.h>
#include <regex>
 
#define ID_FIRSTCHILD 100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD 102
 
// Глобальные переменные:
HINSTANCE hInst;    // Указатель приложения
LPCTSTR szWindowClass = "Andy";
LPCTSTR szTitle = "Window";
int g_scrollY = 0;
HWND hStatic;
 
// Предварительное описание функций 
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 
// Основная программа 
int APIENTRY WinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR     lpCmdLine,
    int       nCmdShow)
{
    MSG msg;
    // Регистрация класса окна 
    MyRegisterClass(hInstance);
 
    // Создание окна приложения
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }    
 
    // Цикл обработки сообщений
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
 
//Регистрация класса
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;   // стиль окна
    wcex.lpfnWndProc = (WNDPROC)WndProc; // оконная процедура
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;     // указатель приложения
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);       // опре-деление иконки
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);    // опреде-ление курсора
    wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);   // ус-тановка фона
    wcex.lpszMenuName = NULL;       // определение меню
    wcex.lpszClassName = szWindowClass; // имя класса
    wcex.hIconSm = NULL;
 
    return RegisterClassEx(&wcex); // регистрация класса окна
}
 
//Создание окна приложения
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;
 
    hInst = hInstance; // сохраняет указатель приложения в переменной hInst
 
    hWnd = CreateWindow(szWindowClass, // имя класса окна
        szTitle,   // имя приложения
        WS_OVERLAPPEDWINDOW | WS_MINIMIZEBOX, // стиль окна
        300,  // положение по Х
        300,  // положение по Y
        800,    // размер по Х
        800,    // размер по Y
        NULL,   // описатель родительского окна
        NULL,       // описатель меню окна
        hInstance,  // указатель приложения
        NULL);     // параметры создания.
 
    if (!hWnd) // Если окно не создалось, функция возвращает FALSE
    {
        return FALSE;
    }   
 
    ShowWindow(hWnd, nCmdShow);     // Показать окно
    UpdateWindow(hWnd);         // Обновить окно
    return TRUE;                //Успешное завершение функции
}
 
 
typedef struct { //структура "буфер"
    HDC memDC; //совместимый контекст устройства в памяти
    HBITMAP memBM, oldBM; //совместимый битмап в памяти и старый битмап
    HPEN pen, oldPen; //"ручка" для рисования
} BUFFER, * LPBUFFER;
 
 
VOID DestroyBuffer(LPBUFFER lpBuf) //уничтожение буфера
{
    SelectObject(lpBuf->memDC, lpBuf->oldBM);
    SelectObject(lpBuf->memDC, lpBuf->oldPen);
 
    DeleteObject(lpBuf->memBM);
    DeleteObject(lpBuf->pen);
 
    DeleteDC(lpBuf->memDC);
}
 
VOID InitBuffer(HWND hWnd, LPBUFFER lpBuf) //инициализация буфера
{
    if (lpBuf->memDC) //если буфер был ранее инициализирован
        DestroyBuffer(lpBuf);
 
    HDC hdc = GetDC(hWnd);
    lpBuf->memDC = CreateCompatibleDC(hdc);
 
    RECT rect = { 0,0,10000,10000 };
 
    lpBuf->memBM = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
 
    lpBuf->oldBM = (HBITMAP)SelectObject(lpBuf->memDC, lpBuf->memBM);
 
    lpBuf->pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
    lpBuf->oldPen = (HPEN)SelectObject(lpBuf->memDC, lpBuf->pen);
 
    FillRect(lpBuf->memDC, &rect, GetSysColorBrush(COLOR_WINDOW));
}
 
//******************************************************************************//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static BOOL draw = FALSE;
    static POINT point{ 0 };
    static BUFFER buffer{ 0 };
    PAINTSTRUCT ps;
    HDC hdc;
    static RECT rt;
    int i = 0;
    static int x1, x2, y1, y2, temp = 4;
    static POINT mas[10000];
 
    static HWND hb1, hb2, hb3, m_hScroll, m_vScroll;
    HINSTANCE hInstance = NULL;
    static RECT test1 = { 50,50,50,50 };   
 
    switch (message)
    {
    case WM_VSCROLL:
    {
        auto action = LOWORD(wParam);
        HWND hScroll = (HWND)lParam;
        int pos = -1;
        if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
            pos = HIWORD(wParam);
        }
        else if (action == SB_LINEDOWN) {
            pos = g_scrollY + 30;
        }
        else if (action == SB_LINEUP) {
            pos = g_scrollY - 30;
        }
        if (pos == -1)
            break;
        WCHAR buf[20];
        SCROLLINFO si = { 0 };
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_POS;
        si.nPos = pos;
        si.nTrackPos = 0;
        SetScrollInfo(hWnd, SB_VERT, &si, true);
        GetScrollInfo(hWnd, SB_VERT, &si);
        pos = si.nPos;
        POINT pt;
        pt.x = 0;
        pt.y = pos - g_scrollY;
        auto hdc = GetDC(hWnd);
        LPtoDP(hdc, &pt, 1);
        ReleaseDC(hWnd, hdc);
        ScrollWindow(hWnd, 0, -pt.y, NULL, NULL);
        g_scrollY = pos;
        return 0;
    }
    case WM_CREATE:
    {       
        hStatic = CreateWindowEx(WS_EX_CLIENTEDGE, "BUTTON", "343434",
            WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL, 5, 5, 250, 300, hWnd, NULL, hInst, NULL);
        EnableScrollBar(hStatic, SB_VERT, ESB_ENABLE_BOTH);
 
        for (int i = 0; i < 100; ++i) {
            auto hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
                WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, 10, 30 * i, 300, 21, hStatic, NULL, hInst, NULL);
            char buf[10];
            snprintf(buf, 10, "%i", i);
            SetWindowText(hEdit, buf);
        }
        RECT rc = { 0 };
        GetClientRect(hWnd, &rc);
        SCROLLINFO si = { 0 };
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_ALL;
        si.nMin = 0;
        si.nMax = 30 * 99 + 21;
        si.nPage = (rc.bottom - rc.top);
        si.nPos = 0;
        si.nTrackPos = 0;
        SetScrollInfo(hWnd, SB_VERT, &si, true);
        return 0;
    }
 
 
    case WM_LBUTTONDOWN:
        draw = false;
        x1 = LOWORD(lParam);
        y1 = HIWORD(lParam);
        mas[i].x = x1;
        mas[i].y = y1;
        //InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_MOUSEMOVE:
        if (wParam & MK_LBUTTON)
        {
            x2 = LOWORD(lParam);
            y2 = HIWORD(lParam);
            mas[i + 1].x = x2;
            mas[i + 1].y = y2;
            i++;
            draw = false;
            RECT test = { 100, 0, x2 * 10, y2 * 10 };
            InvalidateRect(hWnd, &test, TRUE);
        }
        break;
 
    case WM_COMMAND:
 
        if (LOWORD(wParam) == ID_FIRSTCHILD)
            temp = 2;
 
        if (LOWORD(wParam) == ID_SECONDCHILD)
            temp = 0;
 
        if (LOWORD(wParam) == ID_THIRDCHILD)
            temp = 1;
        break;
 
    case WM_LBUTTONUP:
        draw = true;
        InvalidateRect(hWnd, NULL, TRUE);
        break;
 
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt(  //копируем буфер на экран
            ps.hdc,
            ps.rcPaint.left,
            ps.rcPaint.top,
            ps.rcPaint.right - ps.rcPaint.left,
            ps.rcPaint.bottom - ps.rcPaint.top,
            buffer.memDC,
            ps.rcPaint.left,
            ps.rcPaint.top,
            SRCCOPY
        );
        if (temp == 0)
        {
            Rectangle(hdc, x1, y1, x2, y2);
            if (draw) {
                Rectangle(buffer.memDC, x1, y1, x2, y2);
            }
        }
 
        if (temp == 1)
        {
            Ellipse(hdc, x1, y1, x2, y2);
            if (draw) {
                Ellipse(buffer.memDC, x1, y1, x2, y2);
            }
        }
 
 
        if (temp == 2)
        {
            MoveToEx(hdc, x1, y1, NULL);
            LineTo(hdc, x2, y2);
            if (draw) {
                MoveToEx(buffer.memDC, x1, y1, NULL);
                LineTo(buffer.memDC, x2, y2);
            }
        }
        EndPaint(hWnd, &ps);// Закончить графический вывод
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
08.01.2024, 19:57
Ответы с готовыми решениями:

Не работает WM_KEYDOWN в дочернем окне
Помогите разобраться, не работает WM_KEYDOWN в дочернем окне ChildWndProc, при этом через WM_LBUTTONDOWN всё работает, в чем может быть...

Дочерние кнопки в дочернем окне
Не могу понять, почему не отображаются кнопки дочернего окна в дочернем окне? Перепроверил стили класса и окна, оконные функции. Вроде...

Не работает edit в дочернем окне
Добрый вечер. У меня вопрос: я создал основное окно. Затем в нём создал дочернее окно (со стилем WS_CHILD ) и на дочернем окне создал edit....

3
2736 / 891 / 331
Регистрация: 10.02.2018
Сообщений: 2,118
09.01.2024, 18:40
Цитата Сообщение от WEewt345 Посмотреть сообщение
Как заставить работать scrollbar в окне hStatic?
Лучше так не делать.
Окно с собственным скролом должно обрабатывать сообщения этого скрола в своей оконной процедуре. Используя чужой класс окна вы не в полной мере контролируете логику работы таких окон. Если вы хотите сохранить логику работы чужого класса и добавить в эту логику что-то своё, то скорее всего это будет работать не корректно. Нужно полностью замещать чужую оконную функцию, а лучше сделать свой класс окна со своей функцией.
Дополню ваш пример замещением оконной функции, собственная оконная функция будет обрабатывать только сообщения скролинга, остальные сообщения будут направляться в исходную оконную функцию (сделано три вставки кода отмеченные комментарием). Если запустить этот пример, то можно увидеть, что скроллинг у контрола заработал, но он немного кривоват.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
#include <windows.h>
// Файлы заголовков среды выполнения C
#include <stdlib.h>
#include <regex>
 
#define ID_FIRSTCHILD 100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD 102
 
// Глобальные переменные:
HINSTANCE hInst;    // Указатель приложения
LPCTSTR szWindowClass = "Andy";
LPCTSTR szTitle = "Window";
int g_scrollY = 0;
HWND hStatic;
 
// Предварительное описание функций 
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 
// Основная программа 
int APIENTRY WinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR     lpCmdLine,
    int       nCmdShow)
{
    MSG msg;
    // Регистрация класса окна 
    MyRegisterClass(hInstance);
 
    // Создание окна приложения
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }
 
    // Цикл обработки сообщений
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
 
//Регистрация класса
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;   // стиль окна
    wcex.lpfnWndProc = (WNDPROC)WndProc; // оконная процедура
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;     // указатель приложения
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);       // опре-деление иконки
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);    // опреде-ление курсора
    wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);   // ус-тановка фона
    wcex.lpszMenuName = NULL;       // определение меню
    wcex.lpszClassName = szWindowClass; // имя класса
    wcex.hIconSm = NULL;
 
    return RegisterClassEx(&wcex); // регистрация класса окна
}
 
//Создание окна приложения
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;
 
    hInst = hInstance; // сохраняет указатель приложения в переменной hInst
 
    hWnd = CreateWindow(szWindowClass, // имя класса окна
        szTitle,   // имя приложения
        WS_OVERLAPPEDWINDOW | WS_MINIMIZEBOX, // стиль окна
        300,  // положение по Х
        300,  // положение по Y
        800,    // размер по Х
        800,    // размер по Y
        NULL,   // описатель родительского окна
        NULL,       // описатель меню окна
        hInstance,  // указатель приложения
        NULL);     // параметры создания.
 
    if (!hWnd) // Если окно не создалось, функция возвращает FALSE
    {
        return FALSE;
    }
 
    ShowWindow(hWnd, nCmdShow);     // Показать окно
    UpdateWindow(hWnd);         // Обновить окно
    return TRUE;                //Успешное завершение функции
}
 
 
typedef struct { //структура "буфер"
    HDC memDC; //совместимый контекст устройства в памяти
    HBITMAP memBM, oldBM; //совместимый битмап в памяти и старый битмап
    HPEN pen, oldPen; //"ручка" для рисования
} BUFFER, * LPBUFFER;
 
 
VOID DestroyBuffer(LPBUFFER lpBuf) //уничтожение буфера
{
    SelectObject(lpBuf->memDC, lpBuf->oldBM);
    SelectObject(lpBuf->memDC, lpBuf->oldPen);
 
    DeleteObject(lpBuf->memBM);
    DeleteObject(lpBuf->pen);
 
    DeleteDC(lpBuf->memDC);
}
 
VOID InitBuffer(HWND hWnd, LPBUFFER lpBuf) //инициализация буфера
{
    if (lpBuf->memDC) //если буфер был ранее инициализирован
        DestroyBuffer(lpBuf);
 
    HDC hdc = GetDC(hWnd);
    lpBuf->memDC = CreateCompatibleDC(hdc);
 
    RECT rect = { 0,0,10000,10000 };
 
    lpBuf->memBM = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
 
    lpBuf->oldBM = (HBITMAP)SelectObject(lpBuf->memDC, lpBuf->memBM);
 
    lpBuf->pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
    lpBuf->oldPen = (HPEN)SelectObject(lpBuf->memDC, lpBuf->pen);
 
    FillRect(lpBuf->memDC, &rect, GetSysColorBrush(COLOR_WINDOW));
}
 
//+++++++++++++++++++++ (добавленный код)
WNDPROC g_pDefStaticProc;
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_VSCROLL:
        {
            auto action = LOWORD(wParam);
            HWND hScroll = (HWND)lParam;
            int pos = -1;
            if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
                pos = HIWORD(wParam);
            }
            else if (action == SB_LINEDOWN) {
                pos = g_scrollY + 30;
            }
            else if (action == SB_LINEUP) {
                pos = g_scrollY - 30;
            }
            if (pos == -1)
                break;
            WCHAR buf[20];
            SCROLLINFO si = { 0 };
            si.cbSize = sizeof(SCROLLINFO);
            si.fMask = SIF_POS;
            si.nPos = pos;
            si.nTrackPos = 0;
            SetScrollInfo(hWnd, SB_VERT, &si, true);
            GetScrollInfo(hWnd, SB_VERT, &si);
            pos = si.nPos;
            POINT pt;
            pt.x = 0;
            pt.y = pos - g_scrollY;
            auto hdc = GetDC(hWnd);
            LPtoDP(hdc, &pt, 1);
            ReleaseDC(hWnd, hdc);
            RECT rcClient;
            GetClientRect(hWnd, &rcClient);
            ScrollWindow(hWnd, 0, -pt.y, NULL, &rcClient);//ScrollWindow(hWnd, 0, -pt.y, NULL, NULL);
            g_scrollY = pos;
            return 0;
        }
    }
 
    CallWindowProc(g_pDefStaticProc, hWnd, message, wParam, lParam);
}
//+++++++++++++++++++++ (добавленный код)
 
//******************************************************************************//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static BOOL draw = FALSE;
    static POINT point{ 0 };
    static BUFFER buffer{ 0 };
    PAINTSTRUCT ps;
    HDC hdc;
    static RECT rt;
    int i = 0;
    static int x1, x2, y1, y2, temp = 4;
    static POINT mas[10000];
 
    static HWND hb1, hb2, hb3, m_hScroll, m_vScroll;
    HINSTANCE hInstance = NULL;
    static RECT test1 = { 50,50,50,50 };
 
    switch (message)
    {
    case WM_VSCROLL:
    {
        auto action = LOWORD(wParam);
        HWND hScroll = (HWND)lParam;
        int pos = -1;
        if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
            pos = HIWORD(wParam);
        }
        else if (action == SB_LINEDOWN) {
            pos = g_scrollY + 30;
        }
        else if (action == SB_LINEUP) {
            pos = g_scrollY - 30;
        }
        if (pos == -1)
            break;
        WCHAR buf[20];
        SCROLLINFO si = { 0 };
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_POS;
        si.nPos = pos;
        si.nTrackPos = 0;
        SetScrollInfo(hWnd, SB_VERT, &si, true);
        GetScrollInfo(hWnd, SB_VERT, &si);
        pos = si.nPos;
        POINT pt;
        pt.x = 0;
        pt.y = pos - g_scrollY;
        auto hdc = GetDC(hWnd);
        LPtoDP(hdc, &pt, 1);
        ReleaseDC(hWnd, hdc);
        ScrollWindow(hWnd, 0, -pt.y, NULL, NULL);
        g_scrollY = pos;
        return 0;
    }
    case WM_CREATE:
    {
        hStatic = CreateWindowEx(WS_EX_CLIENTEDGE, "BUTTON", "343434",
            WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL, 5, 5, 250, 300, hWnd, NULL, hInst, NULL);
        EnableScrollBar(hStatic, SB_VERT, ESB_ENABLE_BOTH);
 
        g_pDefStaticProc = (WNDPROC)SetWindowLongPtr(hStatic, GWLP_WNDPROC, (LONG_PTR)StaticWndProc); //+++++++++++++++++++++ (добавленный код)
 
        for (int i = 0; i < 100; ++i) {
            auto hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
                WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, 10, 30 * i, 300, 21, hStatic, NULL, hInst, NULL);
            char buf[10];
            snprintf(buf, 10, "%i", i);
            SetWindowText(hEdit, buf);
        }
        RECT rc = { 0 };
        GetClientRect(hWnd, &rc);
        SCROLLINFO si = { 0 };
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_ALL;
        si.nMin = 0;
        si.nMax = 30 * 99 + 21;
        si.nPage = (rc.bottom - rc.top);
        si.nPos = 0;
        si.nTrackPos = 0;
        SetScrollInfo(hWnd, SB_VERT, &si, true);
        SetScrollInfo(hStatic, SB_VERT, &si, true); //+++++++++++++++++++++ (добавленный код)
        return 0;
    }
 
 
    case WM_LBUTTONDOWN:
        draw = false;
        x1 = LOWORD(lParam);
        y1 = HIWORD(lParam);
        mas[i].x = x1;
        mas[i].y = y1;
        //InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_MOUSEMOVE:
        if (wParam & MK_LBUTTON)
        {
            x2 = LOWORD(lParam);
            y2 = HIWORD(lParam);
            mas[i + 1].x = x2;
            mas[i + 1].y = y2;
            i++;
            draw = false;
            RECT test = { 100, 0, x2 * 10, y2 * 10 };
            InvalidateRect(hWnd, &test, TRUE);
        }
        break;
 
    case WM_COMMAND:
 
        if (LOWORD(wParam) == ID_FIRSTCHILD)
            temp = 2;
 
        if (LOWORD(wParam) == ID_SECONDCHILD)
            temp = 0;
 
        if (LOWORD(wParam) == ID_THIRDCHILD)
            temp = 1;
        break;
 
    case WM_LBUTTONUP:
        draw = true;
        InvalidateRect(hWnd, NULL, TRUE);
        break;
 
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt(  //копируем буфер на экран
            ps.hdc,
            ps.rcPaint.left,
            ps.rcPaint.top,
            ps.rcPaint.right - ps.rcPaint.left,
            ps.rcPaint.bottom - ps.rcPaint.top,
            buffer.memDC,
            ps.rcPaint.left,
            ps.rcPaint.top,
            SRCCOPY
        );
        if (temp == 0)
        {
            Rectangle(hdc, x1, y1, x2, y2);
            if (draw) {
                Rectangle(buffer.memDC, x1, y1, x2, y2);
            }
        }
 
        if (temp == 1)
        {
            Ellipse(hdc, x1, y1, x2, y2);
            if (draw) {
                Ellipse(buffer.memDC, x1, y1, x2, y2);
            }
        }
 
 
        if (temp == 2)
        {
            MoveToEx(hdc, x1, y1, NULL);
            LineTo(hdc, x2, y2);
            if (draw) {
                MoveToEx(buffer.memDC, x1, y1, NULL);
                LineTo(buffer.memDC, x2, y2);
            }
        }
        EndPaint(hWnd, &ps);// Закончить графический вывод
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
1
0 / 0 / 0
Регистрация: 19.12.2021
Сообщений: 56
10.01.2024, 16:28  [ТС]
Цитата Сообщение от Ygg Посмотреть сообщение
Дополню ваш пример замещением оконной функции, собственная оконная функция будет обрабатывать только сообщения скролинга, остальные сообщения будут направляться в исходную оконную функцию (сделано три вставки кода отмеченные комментарием). Если запустить этот пример, то можно увидеть, что скроллинг у контрола заработал, но он немного кривоват.
Подскажите как сделать окно без кнопки?
C++
1
2
3
hStatic = CreateWindowEx(WS_EX_CLIENTEDGE, "BUTTON", "343434",
            WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL, 5, 5, 250, 300, hWnd, NULL, hInst, NULL);
        EnableScrollBar(hStatic, SB_VERT, ESB_ENABLE_BOTH);
Если я ставлю класс STATIC, то скрол перестает работать.
0
2736 / 891 / 331
Регистрация: 10.02.2018
Сообщений: 2,118
10.01.2024, 17:17
Лучший ответ Сообщение было отмечено WEewt345 как решение

Решение

Цитата Сообщение от WEewt345 Посмотреть сообщение
Подскажите как сделать окно без кнопки?
Проще и правильнее зарегистрировать ещё один новый класс окна под данную задачу и использовать его. Для статика, как минимум, нужно ещё переопределять обработчик сообщения WM_NCHITTEST, так как у статика он работает своеобразно.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
#include <windows.h>
// Файлы заголовков среды выполнения C
#include <stdlib.h>
#include <regex>
 
#define ID_FIRSTCHILD 100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD 102
 
// Глобальные переменные:
HINSTANCE hInst;    // Указатель приложения
LPCTSTR szWindowClass = "Andy";
LPCTSTR szTitle = "Window";
int g_scrollY = 0;
HWND hStatic;
 
// Предварительное описание функций 
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 
// Основная программа 
int APIENTRY WinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR     lpCmdLine,
    int       nCmdShow)
{
    MSG msg;
    // Регистрация класса окна 
    MyRegisterClass(hInstance);
 
    // Создание окна приложения
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }
 
    // Цикл обработки сообщений
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
 
//Регистрация класса
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;   // стиль окна
    wcex.lpfnWndProc = (WNDPROC)WndProc; // оконная процедура
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;     // указатель приложения
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);       // опре-деление иконки
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);    // опреде-ление курсора
    wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);   // ус-тановка фона
    wcex.lpszMenuName = NULL;       // определение меню
    wcex.lpszClassName = szWindowClass; // имя класса
    wcex.hIconSm = NULL;
 
    return RegisterClassEx(&wcex); // регистрация класса окна
}
 
//Создание окна приложения
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;
 
    hInst = hInstance; // сохраняет указатель приложения в переменной hInst
 
    hWnd = CreateWindow(szWindowClass, // имя класса окна
        szTitle,   // имя приложения
        WS_OVERLAPPEDWINDOW | WS_MINIMIZEBOX, // стиль окна
        300,  // положение по Х
        300,  // положение по Y
        800,    // размер по Х
        800,    // размер по Y
        NULL,   // описатель родительского окна
        NULL,       // описатель меню окна
        hInstance,  // указатель приложения
        NULL);     // параметры создания.
 
    if (!hWnd) // Если окно не создалось, функция возвращает FALSE
    {
        return FALSE;
    }
 
    ShowWindow(hWnd, nCmdShow);     // Показать окно
    UpdateWindow(hWnd);         // Обновить окно
    return TRUE;                //Успешное завершение функции
}
 
 
typedef struct { //структура "буфер"
    HDC memDC; //совместимый контекст устройства в памяти
    HBITMAP memBM, oldBM; //совместимый битмап в памяти и старый битмап
    HPEN pen, oldPen; //"ручка" для рисования
} BUFFER, * LPBUFFER;
 
 
VOID DestroyBuffer(LPBUFFER lpBuf) //уничтожение буфера
{
    SelectObject(lpBuf->memDC, lpBuf->oldBM);
    SelectObject(lpBuf->memDC, lpBuf->oldPen);
 
    DeleteObject(lpBuf->memBM);
    DeleteObject(lpBuf->pen);
 
    DeleteDC(lpBuf->memDC);
}
 
VOID InitBuffer(HWND hWnd, LPBUFFER lpBuf) //инициализация буфера
{
    if (lpBuf->memDC) //если буфер был ранее инициализирован
        DestroyBuffer(lpBuf);
 
    HDC hdc = GetDC(hWnd);
    lpBuf->memDC = CreateCompatibleDC(hdc);
 
    RECT rect = { 0,0,10000,10000 };
 
    lpBuf->memBM = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
 
    lpBuf->oldBM = (HBITMAP)SelectObject(lpBuf->memDC, lpBuf->memBM);
 
    lpBuf->pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
    lpBuf->oldPen = (HPEN)SelectObject(lpBuf->memDC, lpBuf->pen);
 
    FillRect(lpBuf->memDC, &rect, GetSysColorBrush(COLOR_WINDOW));
}
 
//+++++++++++++++++++++ (добавленный код)
WNDPROC g_pDefStaticProc;
LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_VSCROLL:
        {
            auto action = LOWORD(wParam);
            HWND hScroll = (HWND)lParam;
            int pos = -1;
            if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
                pos = HIWORD(wParam);
            }
            else if (action == SB_LINEDOWN) {
                pos = g_scrollY + 30;
            }
            else if (action == SB_LINEUP) {
                pos = g_scrollY - 30;
            }
            if (pos == -1)
                break;
            WCHAR buf[20];
            SCROLLINFO si = { 0 };
            si.cbSize = sizeof(SCROLLINFO);
            si.fMask = SIF_POS;
            si.nPos = pos;
            si.nTrackPos = 0;
            SetScrollInfo(hWnd, SB_VERT, &si, true);
            GetScrollInfo(hWnd, SB_VERT, &si);
            pos = si.nPos;
            POINT pt;
            pt.x = 0;
            pt.y = pos - g_scrollY;
            auto hdc = GetDC(hWnd);
            LPtoDP(hdc, &pt, 1);
            ReleaseDC(hWnd, hdc);
            RECT rcClient;
            GetClientRect(hWnd, &rcClient);
            ScrollWindow(hWnd, 0, -pt.y, NULL, &rcClient);//ScrollWindow(hWnd, 0, -pt.y, NULL, NULL);
            g_scrollY = pos;
            return 0;
        }
    }
 
    DefWindowProc(hWnd, message, wParam, lParam); // ver 2.0
    //CallWindowProc(g_pDefStaticProc, hWnd, message, wParam, lParam);
}
//+++++++++++++++++++++ (добавленный код)
 
//******************************************************************************//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static BOOL draw = FALSE;
    static POINT point{ 0 };
    static BUFFER buffer{ 0 };
    PAINTSTRUCT ps;
    HDC hdc;
    static RECT rt;
    int i = 0;
    static int x1, x2, y1, y2, temp = 4;
    static POINT mas[10000];
 
    static HWND hb1, hb2, hb3, m_hScroll, m_vScroll;
    HINSTANCE hInstance = NULL;
    static RECT test1 = { 50,50,50,50 };
 
    switch (message)
    {
    case WM_VSCROLL:
    {
        auto action = LOWORD(wParam);
        HWND hScroll = (HWND)lParam;
        int pos = -1;
        if (action == SB_THUMBPOSITION || action == SB_THUMBTRACK) {
            pos = HIWORD(wParam);
        }
        else if (action == SB_LINEDOWN) {
            pos = g_scrollY + 30;
        }
        else if (action == SB_LINEUP) {
            pos = g_scrollY - 30;
        }
        if (pos == -1)
            break;
        WCHAR buf[20];
        SCROLLINFO si = { 0 };
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_POS;
        si.nPos = pos;
        si.nTrackPos = 0;
        SetScrollInfo(hWnd, SB_VERT, &si, true);
        GetScrollInfo(hWnd, SB_VERT, &si);
        pos = si.nPos;
        POINT pt;
        pt.x = 0;
        pt.y = pos - g_scrollY;
        auto hdc = GetDC(hWnd);
        LPtoDP(hdc, &pt, 1);
        ReleaseDC(hWnd, hdc);
        ScrollWindow(hWnd, 0, -pt.y, NULL, NULL);
        g_scrollY = pos;
        return 0;
    }
    case WM_CREATE:
    {
        // ver 2.0 <<<
        WNDCLASSEX wcex = {};
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style = CS_HREDRAW | CS_VREDRAW;   // стиль окна
        wcex.lpfnWndProc = StaticWndProc; // оконная процедура
        wcex.hInstance = hInstance;     // указатель приложения
        wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);       // опре-деление иконки
        wcex.hCursor = LoadCursor(NULL, IDC_ARROW);    // опреде-ление курсора
        wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);   // ус-тановка фона
        wcex.lpszClassName = TEXT("MyScrollArea"); // имя класса
        RegisterClassEx(&wcex); // регистрация класса окна
 
        hStatic = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("MyScrollArea"), "343434",
            WS_CHILD | WS_VISIBLE | WS_VSCROLL, 5, 5, 250, 300, hWnd, NULL, hInst, NULL);
        EnableScrollBar(hStatic, SB_VERT, ESB_ENABLE_BOTH);
        
        //g_pDefStaticProc = (WNDPROC)SetWindowLongPtr(hStatic, GWLP_WNDPROC, (LONG_PTR)StaticWndProc); //+++++++++++++++++++++ (добавленный код)
        // ver 2.0 >>>
 
        for (int i = 0; i < 100; ++i) {
            auto hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
                WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, 10, 30 * i, 300, 21, hStatic, NULL, hInst, NULL);
            char buf[10];
            snprintf(buf, 10, "%i", i);
            SetWindowText(hEdit, buf);
        }
        RECT rc = { 0 };
        GetClientRect(hWnd, &rc);
        SCROLLINFO si = { 0 };
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_ALL;
        si.nMin = 0;
        si.nMax = 30 * 99 + 21;
        si.nPage = (rc.bottom - rc.top);
        si.nPos = 0;
        si.nTrackPos = 0;
        SetScrollInfo(hWnd, SB_VERT, &si, true);
        SetScrollInfo(hStatic, SB_VERT, &si, true); //+++++++++++++++++++++ (добавленный код)
        return 0;
    }
 
 
    case WM_LBUTTONDOWN:
        draw = false;
        x1 = LOWORD(lParam);
        y1 = HIWORD(lParam);
        mas[i].x = x1;
        mas[i].y = y1;
        //InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_MOUSEMOVE:
        if (wParam & MK_LBUTTON)
        {
            x2 = LOWORD(lParam);
            y2 = HIWORD(lParam);
            mas[i + 1].x = x2;
            mas[i + 1].y = y2;
            i++;
            draw = false;
            RECT test = { 100, 0, x2 * 10, y2 * 10 };
            InvalidateRect(hWnd, &test, TRUE);
        }
        break;
 
    case WM_COMMAND:
 
        if (LOWORD(wParam) == ID_FIRSTCHILD)
            temp = 2;
 
        if (LOWORD(wParam) == ID_SECONDCHILD)
            temp = 0;
 
        if (LOWORD(wParam) == ID_THIRDCHILD)
            temp = 1;
        break;
 
    case WM_LBUTTONUP:
        draw = true;
        InvalidateRect(hWnd, NULL, TRUE);
        break;
 
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt(  //копируем буфер на экран
            ps.hdc,
            ps.rcPaint.left,
            ps.rcPaint.top,
            ps.rcPaint.right - ps.rcPaint.left,
            ps.rcPaint.bottom - ps.rcPaint.top,
            buffer.memDC,
            ps.rcPaint.left,
            ps.rcPaint.top,
            SRCCOPY
        );
        if (temp == 0)
        {
            Rectangle(hdc, x1, y1, x2, y2);
            if (draw) {
                Rectangle(buffer.memDC, x1, y1, x2, y2);
            }
        }
 
        if (temp == 1)
        {
            Ellipse(hdc, x1, y1, x2, y2);
            if (draw) {
                Ellipse(buffer.memDC, x1, y1, x2, y2);
            }
        }
 
 
        if (temp == 2)
        {
            MoveToEx(hdc, x1, y1, NULL);
            LineTo(hdc, x2, y2);
            if (draw) {
                MoveToEx(buffer.memDC, x1, y1, NULL);
                LineTo(buffer.memDC, x2, y2);
            }
        }
        EndPaint(hWnd, &ps);// Закончить графический вывод
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
10.01.2024, 17:17
Помогаю со студенческими работами здесь

Drop Files в дочернем окне
Как закинуть файлы в дочернее окно? В родительское без проблем, оно генерирует WM_DROPFILES. Стиль расширенный поставил, DragAcceptFiles...

Создание в дочернем окне дочернего
Доброго времени суток! Я создаю главное окно с стилем WS_OVERLAPPEDWINDOW. Потом создаю дочернее с стилем WS_CHILD | WS_CAPTION |...

Как добраться до дочернего окна в дочернем окне
Подскажите пожалуйста как добраться до дочернего окна, если оно находится глубоко? HWND Handle =...

Перехват двойного клика мыши в дочернем окне диалога
Добрый день! По ряду причин переписываю макрос в экселе на VBA на С++ (скачал Dev C++). В приложении создается диалоговое окно (диалог...

Кнопки, флажки и переключатели в дочернем окне, б) окно с горизонтальной и вертикальной полосами прокрутки
Разработать Windows-приложения, отображающие а) кнопки, флажки и переключатели в дочернем окне, б) окно с горизонтальной и вертикальной...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
Новые блоги и статьи
Перемещение выделенных строк ТЧ из одного документа в другой
Maks 30.03.2026
Реализация из решения ниже выполнена на примере нетипового документа "ВыдачаОборудованияНаСпецтехнику" с единственной табличной частью "ОборудованиеИКомплектующие" разработанного в конфигурации КА2. . . .
Functional First Web Framework Suave
DevAlt 30.03.2026
Sauve. IO Апнулись до NET10. Из зависимостей один пакет, работает одинаково хорошо как в режиме проекта так и в интерактивном режиме. из сложностей - чисто функциональный подход. Решил. . .
Автоматическое создание документа при проведении другого документа
Maks 29.03.2026
Реализация из решения ниже выполнена на нетиповых документах, разработанных в конфигурации КА2. Есть нетиповой документ "ЗаявкаНаРемонтСпецтехники" и нетиповой документ "ПланированиеСпецтехники". В. . .
Настройка движения справочника по регистру сведений
Maks 29.03.2026
Решение ниже реализовано на примере нетипового справочника "ТарифыМобильнойСвязи" разработанного в конфигурации КА2, с целью учета корпоративной мобильной связи в коммерческом предприятии. . . .
Автозаполнение реквизита при выборе элемента справочника
Maks 27.03.2026
Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. При выборе "Спецтехники" (Тип Справочник. Спецтехника), заполняется. . .
Сумматор с применением элементов трёх состояний.
Hrethgir 26.03.2026
Тут. https:/ / fips. ru/ EGD/ ab3c85c8-836d-4866-871b-c2f0c5d77fbc Первый документ красиво выглядит, но без схемы. Это конечно не даёт никаких плюсов автору, но тем не менее. . . всё может быть. . .
Автозаполнение реквизитов при создании документа
Maks 26.03.2026
Программный код из решения ниже размещается в модуле объекта документа, в процедуре "ПриСозданииНаСервере". Алгоритм проверки заполнения реализован для исключения перезаписи значения реквизита,. . .
Команды формы и диалоговое окно
Maks 26.03.2026
1. Команда формы "ЗаполнитьЗапчасти". Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. В качестве источника данных. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru