User Tools

Site Tools


prog:dev_cpp:250724-01:index

Dev-C++ 建立一個視窗有2個Panel,3個 Button與 RichTextBox (2025-07-24)

Code

  • #include <windows.h>
    #include <commctrl.h>
    #include <vector>
    #include <string>
    #include <iostream>
    
    #pragma comment(lib, "comctl32.lib")
    #pragma comment(lib, "riched20.lib")
    
    // 控件 ID
    #define ID_PANEL_TOP    100
    #define ID_SPLITTER     101
    #define ID_PANEL_BOTTOM 102
    #define ID_BTN1         200
    #define ID_BTN2         201
    #define ID_BTN3         202
    #define ID_RICHEDIT     300
    #define ID_STATUSBAR    400
    #define ID_TIMER        500
    
    using namespace std;
    
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    void OnDragDrop(HWND hwnd, WPARAM wParam, LPARAM lParam);
    void AddLogEntry(const std::string& entry);
    
    // 全局變數,讓 hRichTextBox 在所有函數中可見
    HWND hRichTextBox;
    HWND hButton1;
    HWND hButton2;
    HWND hButton3;
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
        // 初始化公共控件
        InitCommonControls();
    
        // 設置窗口類
        const char CLASS_NAME[] = "Sample Window Class";
        WNDCLASS wc = {};
        wc.lpfnWndProc = WindowProc;
        wc.hInstance = hInstance;
        wc.lpszClassName = CLASS_NAME;
    
        RegisterClass(&wc);
    
        // 創建窗口
        HWND hwnd = CreateWindowEx(0, CLASS_NAME, "Dev-C++ Example", WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL);
    
        ShowWindow(hwnd, nCmdShow);
        UpdateWindow(hwnd);
    
        // 添加日誌條目
        AddLogEntry("Hello 2");
        AddLogEntry("Hello 3");
        
        // Reference: https://stackoverflow.com/questions/1333527/how-do-i-print-to-the-debug-output-window-in-a-win32-app
        // OutputDebugString("Start.");
    
        // 主消息循環
        MSG msg;
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return 0;
    }
    
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        static HWND hPanel1, hPanel2, hStatusBar;
        static int panel1Height = 240; // Panel1 的高度
        static int _PrevMsgNumber = 0;
    	char buf[32];
    	
        switch (uMsg) {
        case WM_LBUTTONDOWN:
            // Mouse left button pressed down
            AddLogEntry("Mouse Left Button Down");
            return 0;
        case WM_CREATE: {
        	HINSTANCE hInst = ((LPCREATESTRUCT)lParam)->hInstance; // for instance
        	
            // 創建 Panel1
            hPanel1 = CreateWindowEx(0, "STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
                0, 0, 800, panel1Height, hwnd, NULL, NULL, NULL);
    
            // 創建三個按鈕
            CreateWindow("BUTTON", "Button 1", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 10, 10, 100, 25, hwnd, (HMENU)ID_BTN1, ((LPCREATESTRUCT) lParam)->hInstance, NULL);
            CreateWindow("BUTTON", "Button 2", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 10, 50, 100, 25, hwnd, (HMENU)ID_BTN2, ((LPCREATESTRUCT) lParam)->hInstance, NULL);
            CreateWindow("BUTTON", "Button 3", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 10, 90, 100, 25, hwnd, (HMENU)ID_BTN3, ((LPCREATESTRUCT) lParam)->hInstance, NULL);
            
            // 創建 Panel2
            hPanel2 = CreateWindowEx(0, "STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
                0, panel1Height, 800, 360, hwnd, NULL, NULL, NULL);
    
            // 創建 RichTextBox,使用全局變數 hRichTextBox
            hRichTextBox = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, 
                WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
                0, 0, 800, 360, hPanel2, NULL, NULL, NULL);
    
            // 啟用拖放
            DragAcceptFiles(hwnd, TRUE);
            
            // 創建狀態欄
            // hStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE, "Status Bar", hwnd, 0);
            hStatusBar = CreateWindowA(
                STATUSCLASSNAMEA, NULL,
                WS_CHILD | WS_VISIBLE,
                0,0,0,0, hwnd, (HMENU)ID_STATUSBAR, hInst, NULL);
            SendMessage(hStatusBar, WM_SIZE, 0, 0);
            
            // 啟動 1 秒定時器
            SetTimer(hwnd, ID_TIMER, 1000, NULL);
        	}
        	return 0;
        case WM_SIZE: { 
            // 調整 Panel1 和 Panel2 大小
            MoveWindow(hPanel1, 0, 0, LOWORD(lParam), panel1Height, TRUE);
            MoveWindow(hPanel2, 0, panel1Height, LOWORD(lParam), HIWORD(lParam) - panel1Height, TRUE);
            // 要減掉狀態欄的高度為 20
            MoveWindow(hRichTextBox, 0, 0, LOWORD(lParam), HIWORD(lParam) - panel1Height - 20, TRUE); 
            MoveWindow(hStatusBar, 0, HIWORD(lParam) - 20, LOWORD(lParam), 20, TRUE); // 假設狀態欄的高度為 20
    	    }
            return 0;
        case WM_DROPFILES: {
            wsprintfA(buf, "Drop File %d",uMsg);
            AddLogEntry(buf);
            OnDragDrop(hwnd, wParam, lParam);
    	    }
    	    return 0;
    
        case WM_COMMAND:
    	//case WA_CLICKACTIVE:
    	{
        	// AddLogEntry("WM_COMMAND Pressed");
        	// 處理各控件的 _click() 事件
            wsprintfA(buf, "Message %d",uMsg);
            AddLogEntry(buf);
        	
            switch (LOWORD(wParam))
            {
            	case ID_BTN1:
                    //wsprintfA(buf, "Bbtton 1");
                    //SendMessageA(hStatusBar, SB_SETTEXTA, 0 | SBT_NOBORDERS, (LPARAM)buf);
            		AddLogEntry("Button 1 Pressed");
            		break;
            	case ID_BTN2:
            		AddLogEntry("Button 2 Pressed");
            		break;
            	case ID_BTN3:
            		AddLogEntry("Button 3 Pressed");
            		// OutputDebugString("My output string.");
            		break;
    		}
    		return 0;
        }
        case WM_TIMER: {
        	// 處理計時器事件
            if (wParam == ID_TIMER)
            {
                SYSTEMTIME st; GetLocalTime(&st);
                char buf[32];
                wsprintfA(buf, "%04d-%02d-%02d %02d:%02d:%02d",
                          st.wYear, st.wMonth, st.wDay,
                          st.wHour, st.wMinute, st.wSecond);
                SendMessageA(hStatusBar, SB_SETTEXTA, 0 | SBT_NOBORDERS, (LPARAM)buf);
                // AddLogEntry (buf);
            }
            return 0;
        }
        case WM_DESTROY:
            KillTimer(hwnd, ID_TIMER); // 刪除Timer
            PostQuitMessage(0);
            return 0;
        }
        //case WM_GETHOTKEY:
      	//{
        //	return 0;	
    	//}
        //if (_PrevMsgNumber != uMsg)
        //{
        //	_PrevMsgNumber = uMsg;
        //    wsprintfA(buf, "Message %d",_PrevMsgNumber);
        //    AddLogEntry(buf);
    	//}
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    
    void OnDragDrop(HWND hwnd, WPARAM wParam, LPARAM lParam) {
        HDROP hDrop = (HDROP)wParam;
        UINT cnt = DragQueryFileA(hDrop, 0xFFFFFFFF, NULL, 0);
        AddLogEntry("On Drag Drop");
        for (UINT i = 0; i < cnt; i++)
        {
            char path[MAX_PATH];
            DragQueryFileA(hDrop, i, path, MAX_PATH);
            // 手动判断扩展名
            char *ext = strrchr(path, '.');
            if (ext && _stricmp(ext, ".config") == 0)
            {
                //AddLogEntry(path);
            }
            AddLogEntry(std::to_string(i));
        
        // 將文件名添加到日誌
        //AddLogEntry(fileName);
        }
    
        DragFinish(hDrop);
    }
    
    void AddLogEntry(const std::string& entry) {
        std::cout << "Log Entry: " << entry << std::endl; // 調試輸出
        int len = GetWindowTextLength(hRichTextBox);
        SendMessage(hRichTextBox, EM_SETSEL, len, len);
        SendMessage(hRichTextBox, EM_REPLACESEL, TRUE, (LPARAM)entry.c_str());
        SendMessage(hRichTextBox, EM_REPLACESEL, TRUE, (LPARAM)"\r\n");
    }

Notes

  • Highlight 的行數是與 ChatGTP 產生不同之處
  • 下面是一個純Win32 API,可在
    • 用兩個STATIC窗口+一個細條`STSTATIC模擬SplitContainer,支
    • 在上半區(Panel1)放3 個按鈕,自上而下排列,並開啟WM_DROPFILES 接受.config檔案拖放,把文_
    • 在下半區(Panel2)放一個RichEdit(MSFTEDIT_CLASS)_
    • 視窗底部放一個StatusBar,用定時器每秒更新「YYYY-MM-DD HH:MM :SS 」 格式的目前時
  • // SplitterANSI.cpp
    #include <windows.h>
    #include <commctrl.h>
    #include <richedit.h>
    #include <shellapi.h>
    #include <stdio.h>
    #include <string.h>
    
    // 控件 ID
    #define ID_PANEL_TOP    100
    #define ID_SPLITTER     101
    #define ID_PANEL_BOTTOM 102
    #define ID_BTN1         200
    #define ID_BTN2         201
    #define ID_BTN3         202
    #define ID_RICHEDIT     300
    #define ID_STATUSBAR    400
    #define ID_TIMER        500
    
    // 全局句柄
    HWND hPanelTop, hSplitter, hPanelBot, hBtn1, hBtn2, hBtn3, hRE, hStatus;
    bool dragging = false;
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        switch (msg)
        {
        case WM_CREATE:
        {
            HINSTANCE hInst = ((LPCREATESTRUCT)lParam)->hInstance;
    
            // 上半区 Panel1
            hPanelTop = CreateWindowA(
                "STATIC", NULL,
                WS_CHILD | WS_VISIBLE | SS_ETCHEDFRAME,
                0,0,0,0,
                hWnd, (HMENU)ID_PANEL_TOP, hInst, NULL);
    
            // Splitter(高 4 像素)
            hSplitter = CreateWindowA(
                "STATIC", NULL,
                WS_CHILD | WS_VISIBLE | SS_ETCHEDFRAME,
                0,0,0,4,
                hWnd, (HMENU)ID_SPLITTER, hInst, NULL);
    
            // 下半区 Panel2
            hPanelBot = CreateWindowA(
                "STATIC", NULL,
                WS_CHILD | WS_VISIBLE | SS_ETCHEDFRAME,
                0,0,0,0,
                hWnd, (HMENU)ID_PANEL_BOTTOM, hInst, NULL);
    
            // 三个按钮
            hBtn1 = CreateWindowA("BUTTON", "Button 1",
                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                 0,0,0,0, hPanelTop, (HMENU)ID_BTN1, hInst, NULL);
            hBtn2 = CreateWindowA("BUTTON", "Button 2",
                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                 0,0,0,0, hPanelTop, (HMENU)ID_BTN2, hInst, NULL);
            hBtn3 = CreateWindowA("BUTTON", "Button 3",
                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                 0,0,0,0, hPanelTop, (HMENU)ID_BTN3, hInst, NULL);
    
            // RichEdit 下半区
            LoadLibraryA("Msftedit.dll");
            hRE = CreateWindowExA(
                0, "RICHEDIT50W", "Hello\n",
                WS_CHILD | WS_VISIBLE |
                ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN,
                0,0,0,0, hPanelBot, (HMENU)ID_RICHEDIT, hInst, NULL);
    
            // StatusBar
            hStatus = CreateWindowA(
                STATUSCLASSNAMEA, NULL,
                WS_CHILD | WS_VISIBLE,
                0,0,0,0, hWnd, (HMENU)ID_STATUSBAR, hInst, NULL);
    
            // 接受文件拖放
            DragAcceptFiles(hPanelTop, TRUE);
    
            // 启动 1 秒定时器
            SetTimer(hWnd, ID_TIMER, 1000, NULL);
        }
        return 0;
    
        case WM_SIZE:
        {
            // 计算尺寸
            RECT rc; GetClientRect(hWnd, &rc);
    
            // StatusBar 调整大小
            SendMessageA(hStatus, WM_SIZE, 0, 0);
            RECT rcSB; SendMessageA(hStatus, SB_GETRECT, 0, (LPARAM)&rcSB);
            int sbH = rcSB.bottom - rcSB.top;
    
            int availH = rc.bottom - sbH;
            int topH   = (int)(availH * 0.4);
            const int spH = 4;
    
            // 布局
            MoveWindow(hPanelTop,    0, 0, rc.right, topH,    TRUE);
            MoveWindow(hSplitter,    0, topH, rc.right, spH,   TRUE);
            MoveWindow(hPanelBot,    0, topH+spH, rc.right, availH-topH-spH, TRUE);
    
            // 按钮等分
            int btnH = topH / 3;
            MoveWindow(hBtn1,  0,     0,      rc.right, btnH, TRUE);
            MoveWindow(hBtn2,  0,     btnH,   rc.right, btnH, TRUE);
            MoveWindow(hBtn3,  0,     btnH*2, rc.right, topH-btnH*2, TRUE);
    
            // RichEdit 填满 PanelBot
            MoveWindow(hRE, 0, 0, rc.right, availH-topH-spH, TRUE);
        }
        return 0;
    
        case WM_LBUTTONDOWN:
        {
            POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
            RECT r; GetWindowRect(hSplitter, &r);
            ScreenToClient(hWnd, (LPPOINT)&r);
            if (pt.y >= r.top && pt.y <= r.bottom)
            {
                dragging = true;
                SetCapture(hWnd);
            }
        }
        return 0;
    
        case WM_MOUSEMOVE:
            if (dragging)
            {
                int y = GET_Y_LPARAM(lParam);
                RECT rc; GetClientRect(hWnd, &rc);
                RECT rcSB; SendMessageA(hStatus, SB_GETRECT, 0, (LPARAM)&rcSB);
                int sbH = rcSB.bottom - rcSB.top;
                int availH = rc.bottom - sbH;
                if (y < 0) y = 0;
                if (y > availH) y = availH;
    
                const int spH = 4;
                MoveWindow(hPanelTop,    0,0,rc.right,y, TRUE);
                MoveWindow(hSplitter,    0,y,rc.right,spH, TRUE);
                MoveWindow(hPanelBot,    0,y+spH, rc.right, availH-y-spH, TRUE);
    
                int btnH = y / 3;
                MoveWindow(hBtn1,  0,     0,      rc.right, btnH, TRUE);
                MoveWindow(hBtn2,  0,     btnH,   rc.right, btnH, TRUE);
                MoveWindow(hBtn3,  0,     btnH*2, rc.right, y-btnH*2, TRUE);
                MoveWindow(hRE,    0,     0,      rc.right, availH-y-spH, TRUE);
            }
            return 0;
    
        case WM_LBUTTONUP:
            if (dragging) { dragging = false; ReleaseCapture(); }
            return 0;
    
        case WM_DROPFILES:
        {
            HDROP hDrop = (HDROP)wParam;
            UINT cnt = DragQueryFileA(hDrop, 0xFFFFFFFF, NULL, 0);
            for (UINT i = 0; i < cnt; i++)
            {
                char path[MAX_PATH];
                DragQueryFileA(hDrop, i, path, MAX_PATH);
                // 手动判断扩展名
                char *ext = strrchr(path, '.');
                if (ext && _stricmp(ext, ".config") == 0)
                {
                    int len = GetWindowTextLengthA(hRE);
                    SendMessageA(hRE, EM_SETSEL, len, len);
                    SendMessageA(hRE, EM_REPLACESEL, 0, (LPARAM)path);
                    SendMessageA(hRE, EM_REPLACESEL, 0, (LPARAM)"\r\n");
                }
            }
            DragFinish(hDrop);
        }
        return 0;
    
        case WM_COMMAND:
        {
            int id = LOWORD(wParam);
            const char* msg = NULL;
            if (id == ID_BTN1) msg = "Button 1 Pressed\r\n";
            if (id == ID_BTN2) msg = "Button 2 Pressed\r\n";
            if (id == ID_BTN3) msg = "Button 3 Pressed\r\n";
            if (msg)
            {
                int len = GetWindowTextLengthA(hRE);
                SendMessageA(hRE, EM_SETSEL, len, len);
                SendMessageA(hRE, EM_REPLACESEL, 0, (LPARAM)msg);
            }
        }
        return 0;
    
        case WM_TIMER:
            if (wParam == ID_TIMER)
            {
                SYSTEMTIME st; GetLocalTime(&st);
                char buf[32];
                wsprintfA(buf, "%04d-%02d-%02d %02d:%02d:%02d",
                          st.wYear, st.wMonth, st.wDay,
                          st.wHour, st.wMinute, st.wSecond);
                SendMessageA(hStatus, SB_SETTEXTA, 0 | SBT_NOBORDERS, (LPARAM)buf);
            }
            return 0;
    
        case WM_DESTROY:
            KillTimer(hWnd, ID_TIMER);
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProcA(hWnd, msg, wParam, lParam);
    }
    
    int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int nCmdShow)
    {
        INITCOMMONCONTROLSEX icc = { sizeof(icc), ICC_BAR_CLASSES };
        InitCommonControlsEx(&icc);
    
        WNDCLASSA wc = {0};
        wc.lpfnWndProc   = WndProc;
        wc.hInstance     = hInst;
        wc.lpszClassName = "SplitWinANSI";
        wc.hCursor       = LoadCursorA(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
        RegisterClassA(&wc);
    
        HWND hWnd = CreateWindowA(
            wc.lpszClassName, "Win32 SplitContainer Demo (ANSI)",
            WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
            800, 600,
            NULL, NULL, hInst, NULL);
        ShowWindow(hWnd, nCmdShow);
    
        MSG msg;
        while (GetMessageA(&msg, NULL, 0, 0))
            DispatchMessageA(&msg);
        return (int)msg.wParam;
    }
  • 模擬SplitContainer:
    • 上半區、下半區皆用STATIC控制項(附邊框),SplitterSTATIC;
    • 在WM_MOUSE*中捕獲並根據滑鼠Y 座標動態*_
  • 按鈕
    • 在PanelTop裡創建三個`BUTTONBUTTON,統一通過`WM_COMMA_WM_COMMAND識別`I_ID_BTN1/2/3,並往下方RichEd
  • 拖放.config 文件
    • 對PanelTop呼叫`DragAcceptDragAcceptFiles(TRUE)並回應`WM_DROPFI_WM_DROPFILES,用`_DragQueryFile列出檔名,僅對`.c_.config擴展名做處理。
  • RichEdit 控件
    • 使用MSFTEDIT_CLASS(需載入`Msftedit._Msftedit.dll)ES_MULTILINE,、ES_AUTOVSCROLL_“Hello\n”。
  • 狀態列 + 計時器
    • 使用通用的Win32 CreateWindow(STATUSCLASSNAME…),然後SetTimer每秒觸發`WM_TIME_WM_TIMER,取`GetLo_GetLocalTime()並SB_SETTEXT更新。
  • 將上面這段程式碼在Dev-C++ 6.3 下新建一個Win32 GUI 工程,連結Comctl32.lib和Msftedit.lib,即可得到你要的效果:分割條可拖曳、40%/60% 初始、按鈕點擊與拖放日誌記錄、底部狀態列顯示目前時間。
  • 3 person(s) visited this page until now.

prog/dev_cpp/250724-01/index.txt · Last modified: 2025/07/24 10:23 by jethro