草庐IT

c++ - 如何处理 win32 winapi 中无效绘制的 ComboBox 控件?

coder 2024-06-12 原文

我遇到了使用 WinAPI 无效绘制 ComboBox 的问题。当您最小化应用程序并在未隐藏 ComboBox 控件的选择后恢复它时,它看起来像这样:

如您所见,OK 按钮获得了焦点,但 ComboBox 的选择仍未隐藏。当控件失去输入焦点时,ComboBox 的正常行为会隐藏选择。

代码:

#define WIN32_MEAN_AND_LEAN

#include <SDKDDKVer.h>
#include <Windows.h>
#include <Windowsx.h>
#include <CommCtrl.h>
#include <assert.h>

struct window_context {
    HINSTANCE _instance;
    HWND _window;

    HWND _combo_box2;
    HWND _ok_button;

    window_context(HINSTANCE instance) noexcept : _instance{ instance }
    {
    }
};

static BOOL on_create(HWND hwnd, LPCREATESTRUCT lpCreateStruct) noexcept
{
    auto context = reinterpret_cast<window_context*>(lpCreateStruct->lpCreateParams);

    context->_combo_box2 = CreateWindowW(WC_COMBOBOXW,
                                        L"",
                                        CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_TABSTOP | WS_VSCROLL | WS_VISIBLE | WS_CHILD,
                                        0, 0, 0, 0,
                                        hwnd,
                                        (HMENU)44,
                                        nullptr,
                                        nullptr);
    ComboBox_AddString(context->_combo_box2, L"select me");

    context->_ok_button = CreateWindowW(WC_BUTTONW,
                                        L"OK",
                                        BS_DEFPUSHBUTTON | BS_NOTIFY | WS_TABSTOP | WS_VISIBLE | WS_CHILD,
                                        0, 0, 0, 0,
                                        hwnd,
                                        nullptr,
                                        nullptr,
                                        nullptr);

    return true;
}

static void on_destroy(HWND hwnd) noexcept
{
    PostQuitMessage(0);
}

static void on_size(HWND hwnd, UINT state, int cx, int cy) noexcept
{
    auto context = reinterpret_cast<window_context*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));

    MoveWindow(context->_combo_box2,
            10,
            10,
            100,
            100,
            true); // causes the weird behaviour to occur
                   // using SetWindowPos doesn't help
                   // using SetWindowPos with SWP_NOSIZE prevents the bug from occuring
                   // but also locks it into size which is not acceptable
                   // since I use this to deal with DPI scaling changes

    MoveWindow(context->_ok_button,
            10,
            110,
            100,
            50,
            true);
}

static void on_activate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized) noexcept
{
    auto context = reinterpret_cast<window_context*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));

    if (state)
        SetFocus(context->_ok_button);
}

static BOOL on_nc_create(HWND hwnd, LPCREATESTRUCT lpCreateStruct) noexcept
{
    auto context = reinterpret_cast<window_context*>(lpCreateStruct->lpCreateParams);

    context->_window = hwnd;
    SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(context));

    return FORWARD_WM_NCCREATE(hwnd, lpCreateStruct, DefWindowProcW);
}

static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept
{
    switch (uMsg) {
        HANDLE_MSG(hwnd, WM_CREATE, on_create);
        HANDLE_MSG(hwnd, WM_DESTROY, on_destroy);
        HANDLE_MSG(hwnd, WM_SIZE, on_size);
        HANDLE_MSG(hwnd, WM_ACTIVATE, on_activate);
        HANDLE_MSG(hwnd, WM_NCCREATE, on_nc_create);
    }

    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

static bool register_class(HINSTANCE hInstance) noexcept
{
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(wcex);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = &wnd_proc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = nullptr;
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = nullptr;
    wcex.lpszClassName = L"test";
    wcex.hIconSm = nullptr;

    return RegisterClassExW(&wcex) != 0;
}

static bool create_window(window_context& context) noexcept
{
    return CreateWindowExW(0,
                        L"test",
                        L"Win32 Program",
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        800,
                        600,
                        nullptr,
                        nullptr,
                        context._instance,
                        &context);
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                    _In_opt_ HINSTANCE hPrevInstance,
                    _In_ LPWSTR    lpCmdLine,
                    _In_ int       nCmdShow)
{
    if (!register_class(hInstance))
        return false;

    window_context context{ hInstance };

    if (!create_window(context))
        return false;

    ShowWindow(context._window, nCmdShow);

    MSG msg;
    BOOL got_message;

    while ((got_message = GetMessageW(&msg, nullptr, 0, 0)) && got_message != -1) {
        if (IsDialogMessageW(context._window, &msg))
            continue;

        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }

    return static_cast<int>(msg.wParam);
}

重现问题的步骤:

  • 从组合框中选择选择我
  • 最小化窗口
  • 恢复窗口

ComboBox 现在应该显示为选中状态,即使它并未选中。

解决问题的步骤:

  • 使 ComboBox 的区域无效无济于事,重绘 Combobox 仍然无法解决问题
  • 只在指定位置创建 ComboBox 且不移动其位置或更改其大小时不会出现此问题
  • 当您使用SetWindowPos
  • 只是移动 ComboBox 的位置时,问题不会出现
  • 一旦更改 ComboBox 的大小,就会出现这种奇怪的行为。
  • 对话框以某种方式正确地处理了这个问题
  • WinForms 也能正确处理这个问题,但我没能找到他们如何防止它发生
  • 只有当您从下拉列表中选择一个项目时才会出现错误,当您输入自定义文本时错误会消失

这确实是一个非常奇特的问题,我非常感谢您的帮助。

最佳答案

您可以通过调用 ComboBox_SetEditSel(context->_combo_box2, -1, -1); 解决这个问题(参见 CB_SETEDITSEL )或者在调整大小之前在组合框的编辑控件中找到选定的字符,然后在调整大小后恢复这些值。

static void on_size(HWND hwnd, UINT, int, int) noexcept
{
    auto context=reinterpret_cast<window_context*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));

    DWORD range = ComboBox_GetEditSel(context->_combo_box2);
    DWORD start = LOWORD(range);
    DWORD end = HIWORD(range);

    MoveWindow(context->_combo_box2, 10, 10, 150, 100, true);
    MoveWindow(context->_ok_button, 10, 110, 100, 50, true);

    ComboBox_SetEditSel(context->_combo_box2, start, end);
}

但是您可能会发现在使用其他控件时会遇到类似的问题,很难找到解决所有问题的方法。考虑使用 DialogBox相反。

关于c++ - 如何处理 win32 winapi 中无效绘制的 ComboBox 控件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49603893/

有关c++ - 如何处理 win32 winapi 中无效绘制的 ComboBox 控件?的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

    我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

  3. ruby-on-rails - Rails 5 Active Record 记录无效错误 - 2

    我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa

  4. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  5. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  6. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  7. ruby-on-rails - 如何处理 Grape 中特定操作的过滤器之前? - 2

    我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?

  8. Ruby - 如何处理子类意外覆盖父类(super class)私有(private)字段的问题? - 2

    假设您编写了一个类Sup,我决定将其扩展为SubSup。我不仅需要了解你发布的接口(interface),还需要了解你的私有(private)字段。见证这次失败:classSupdefinitialize@privateField="fromsup"enddefgetXreturn@privateFieldendendclassSub问题是,解决这个问题的正确方法是什么?看起来子类应该能够使用它想要的任何字段而不会弄乱父类(superclass)。编辑:equivalentexampleinJava返回"fromSup",这也是它应该产生的答案。 最佳答案

  9. ruby - 如何排除无效日期 ruby - 2

    我想知道我应该引用什么异常名称。我的日期无效。我检查了文档,但找不到。BeginDate.new(day,month,year)Rescueexceptionnamestatements 最佳答案 我认为您正在寻找ArgumentError.使用irb:>Date.new(2,-200,3)ArgumentError:invaliddatefrom(irb):11:in`new'from(irb):11所以beginDate.new(2,-200,3)rescueArgumentError#yourlogicend

  10. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

随机推荐