如何使用 WinAPI 在 C++ 中制作带有标签的旋转框的窗口

Posted

技术标签:

【中文标题】如何使用 WinAPI 在 C++ 中制作带有标签的旋转框的窗口【英文标题】:How to make a window with labeled spinbox in C++ using WinAPI 【发布时间】:2016-12-06 09:04:23 【问题描述】:

我花了一天时间试图让工作变得像这样简单: (来源:s-msft.com)

我只需要能够以编程方式获取值、设置值,当然还允许用户通过写入字段或按下箭头来设置值。 MSDN 对此真的很混乱,并且没有提供足够的信息它是如何工作的。在互联网的其他地方,没有代码的工作示例。

msdn.microsoft.com/en-us/library/windows/desktop/hh298353(v=vs.85).aspx - 不可编译 www.drdobbs.com/creating-spin-controls-for-windows/184402858?pgno=4 - 不可编译 forums.codeguru.com/showthread.php?484588-Up-down-spin-control - 不起作用

我正在 Windows 7 中使用 MinGW 编译程序,我需要它在 Windows 7 及更高版本上工作。 不管是使用过程化 Win32 API 还是 MFC 编写。

编辑: 经过一番讨论,我意识到我忘了指出重要的事实。我真的只是在编写一个非常简单的应用程序,它包含不超过 3 个小部件,并且我不打算在未来为此使用 Win32 API。因此,我没有理由学习所有那些复杂的 Windows 特定机制并遵循 MSDN 稳健的方法,使用资源文件中的自定义对话框定义或在内存中构建。

有没有办法,如何在没有资源文件、自定义对话框类以及尽可能少的代码行的情况下做到这一点?如何在现有窗口中直接创建旋转框,需要处理哪些消息?

【问题讨论】:

到目前为止你尝试过什么?你遇到过什么问题?你能告诉我们什么代码? “我正在用 MinGW 编译程序” - 你为什么认为 MSDN 提供了这个工具链的代码示例?如果您仍在为理解编译器/链接器错误而苦苦挣扎,请获取 Visual Studio Community。 @IInspectable:无论编译器如何,我都希望 API 是相同的,但显然 MinGW windows.hcommctrl.h 标头中缺少一些声明。 Vanilla MinGW 在 Windows 2000 之后没有更新任何 API。如果您坚持使用 MinGW,请改用 MinGW-w64。下次您发现无法编译的示例代码并来这里询问时,请务必发布您尝试编译的确切文件(因为我假设您正确格式化了示例代码!)和编译器错误。这样,我们就可以找出您犯了什么错误。 @andlabs:我明白你的意思,但我根据许多不同的来源尝试了许多不同的方法,但总是卡在其他地方。所以我放弃了这一切,来到这里问,希望你能指出我正确的方向,这不需要读一本书、写 500 行代码和花费 4 个小时的编程。 【参考方案1】:

您在这些链接旁边的括号中的 cmets 在我的脑海中留下了一些不好的印象。不,当然它们是“不可编译的”。文档的目的不是为您提供可以简单地放入项目中的完整编写和完善的代码。 The MSDN article 试图让您了解 up-down 控件的工作原理,并且凭借您对 Win32 API 的背景知识,这应该足以让它们在您的项目中工作。

公平地说,the Dr. Dobbs article 你链接关于 16 位 Windows,这是一个与 32 位 Windows 完全不同的编程环境,所以如果你'这可能不是一个好的参考还不是 Win32 专家,因此您可以发现差异。其中一些是微妙的。

另外,我在上面提到了“你对 Win32 API 的背景知识”。这很重要。如果你想做 Windows 编程,你需要了解 Windows 编程。通过将来自网络各个地方的代码示例拼凑在一起,您不会成功。很少有这样的编程,但肯定不是复杂的 C API。学习 Win32 的标准资源是 Charles Petzold 的 Programming Windows 第 5 版。由于您想要第 5 版(不是更新版),因此您可以定期在 Amazon 上以低价购买二手副本。

准备工作已经结束,我将尝试回答您的问题。当你说要创建一个“窗口”时,这有点模棱两可,因为在 Windows 编程语言中,一切都是一个窗口。这些控件中的每一个实际上都是 Windows,它们可以托管在本身就是窗口的容器上,并且该容器可以托管在对话框或***窗口上。所以我不清楚你希望最终产品是什么样子。

我猜你可能想要一个包含这些控件的对话框(当然,它是一个窗口)。使用对话框使事情变得非常简单,因为您可以使用资源编辑器以图形方式使用您想要的控件来布置对话框。 Visual Studio 编程环境带有一个内置的资源编辑器,但如果您愿意,也可以使用第三方解决方案。 ResEdit 和 Resource Hacker 是不错的选择。当然,您需要阅读文档以了解如何使这些工具为您工作。最终,他们将生成一个对话框模板,该模板存储在资源文件中并链接到您的二进制文件中。示例对话框模板如下所示:

IDD_DIALOG1 DIALOGEX 0, 0, 238, 54
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    LTEXT           "&Month:",IDC_STATIC,7,9,30,8
    EDITTEXT        IDC_MONTH,37,7,40,14,ES_AUTOHSCROLL
    CONTROL         "",IDC_MONTH_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,63,15,11,14
    LTEXT           "&Day:",IDC_STATIC,84,9,30,8
    EDITTEXT        IDC_DAY,114,7,40,14,ES_AUTOHSCROLL
    CONTROL         "",IDC_DAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,140,16,11,14
    LTEXT           "&Year",IDC_STATIC,161,9,30,8
    EDITTEXT        IDC_YEAR,191,7,40,14,ES_AUTOHSCROLL
    CONTROL         "",IDC_YEAR_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,217,16,11,14
    DEFPUSHBUTTON   "OK",IDOK,127,32,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,181,32,50,14
END

请注意,正如 MSDN 文档所述,Up-Down 控件实际上只是一个小微调框。您需要将其附加到文本框(编辑控件)以使其有用。微调框将自动与 Z 顺序中位于其前面的编辑​​控件配对。这极大地简化了代码,尽管您可以使用 API 来手动操作控件以实现相同的效果,如果您愿意的话。 MSDN 文档解释了如何操作。

一旦您定义了此对话框模板并将其设置为链接到您的项目中,您可以通过简单地调用DialogBox 函数来显示对话框。在上面的代码中,我给对话框赋予了符号名称IDD_DIALOG1;这应该在常规代码和资源编译器都可以全局访问的资源头文件中定义。

您还需要定义一个对话过程来处理对话的消息。对于我或其他任何人来说,消息处理太多了,无法在简单的文章/答案中进行解释。这就是您需要咨询 Petzold 或其他一些通用 Win32 编程资源来填补空白的地方。

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>        // include Windows SDK headers
#include <commctrl.h>
#include <tchar.h>
#include "resource.h"       // include resource definitions

INT_PTR CALLBACK Dialog1Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)

    switch (uMsg)
    
      case WM_INITDIALOG:
      
           return (INT_PTR)TRUE;
      
      case WM_COMMAND:
      
         switch (LOWORD(wParam))
         
            case IDOK:
            case IDCANCEL:
            
               EndDialog(hwndDlg, LOWORD(wParam));
               return (INT_PTR)TRUE;
            
            default:
            
               break;
            
         
         break;
      
    
    return (INT_PTR)FALSE;


int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int nCmdShow)

   // Initialize the common controls at the start of your application.
   INITCOMMONCONTROLSEX iccex;
   iccex.dwSize = sizeof(iccex);
   iccex.dwICC  = ICC_WIN95_CLASSES;

    // Show the dialog box.
    // This creates a modal dialog box that runs its own message loop,
    // and thus does not require your code to have its own.
    // A modal dialog box is therefore *blocking*, and this function call
    // does not return until the dialog box has been dismissed.
    DialogBox(hInstance,   // handle to instance that contains your resources (probably your EXE)
              MAKEINTRESOURCE(IDD_DIALOG1),
              NULL,        // handle to parent window of dialog, or NULL if no parent
              Dialog1Proc  // pointer to dialog's window procedure
             );
    return 0;

这将在屏幕上显示对话框:

但乐趣才刚刚开始!您可能希望在对话框的窗口过程中编写代码来处理某些消息并控制对话框的行为。 up-down 控件确实可以更改文本框中显示的整数值,但您可能希望设置一个可接受的范围。或者您可能想要反转最大值和最小值,以便底部箭头减小值而不是增加值(出于兼容性原因,这种不寻常和意外的行为是默认行为)。

如果您真的想要检索用户在文本框中输入的值,您还有更多工作要做。一个基本策略是检索值以响应WM_DESTROY 消息并将它们存储在可以从调用者处读取它们的位置。当然,还有更好的方法可以做到这一点,并且使用 C++ 面向对象的包装器会更容易,但它更高级,需要一些有关 Windows 编程工作原理的基本知识。

还值得指出的是,如果您出于某种原因无法使用对话框模板,或者您想创建一个标准窗口,您还可以编写代码,通过调用CreateWindowEx 函数手动创建这些单独的窗口。这就是您链接的the MSDN article 试图描述的内容。它确实有效,而且更灵活,但也更复杂。老实说,您应该能够通过阅读 MSDN 文章来弄清楚。它非常完整且解释清楚,我承认这并不是您在 MSDN 上看到的总是,但这不是其中一种情况。

【讨论】:

哇,感谢您的宝贵时间。我真的没想到有人会写出如此详细的答案。是的,问题确实是,我对在 Windows API 中编程 GUI 了解不多,通常我会使用 Qt 之类的库,这会更容易,但这次是一个特定的作业,我必须处理纯系统API。 我发现 Qt 更难使用。我想这都是你习惯的。如果这不是您所期望的答案,那么当您提出这个问题时,我不确定您所期望的 是什么。请注意,Stack Overflow 通常认为“推荐教程”问题是题外话。有一个专门的原因,请参阅number 4 here。 @youda 对不起,我的意思是你解释得比我希望的要好,并不是说你回答的不是我想要的。

以上是关于如何使用 WinAPI 在 C++ 中制作带有标签的旋转框的窗口的主要内容,如果未能解决你的问题,请参考以下文章

使用winapi从桌面打开C++文件

Winapi检测按钮悬停

如何使用 C++ 和 winAPI 检查目录是不是存在 [重复]

同时播放两种声音的最简单方法(c++ winapi)

如何安全地调用 TerminateThread 和 FreeLibrary (WinAPI, C++)

如何在纯 C++ 中高效快速地清理我的 GDI 对象 - winapi(不是 .net,c#)?