随着对话框的增强/扩展,降低资源文件增长率的方法

问题描述 投票:1回答:1

作为在用C编写的旧Windows WinAPI GUI应用程序中扩展对话框功能的一部分,我再次面临为六行数据输入对话框的每一行添加多个复选框。我无法忍受重复资源文件和源代码文件更改的麻烦,并决定从Java UI借用UI设计方法从窗格构建UI。

Visual Studio工具,至少是Visual Studio 2005,似乎不鼓励这种方法,在这种情况下,我手动编辑了资源文件。也许Visual Studio 2017的资源编辑工具更灵活。

我的问题是这种方法的替代方案似乎很容易做,并且更符合Visual Studio的理念。

我也想知道这种方法的缺点。

对于使我烦恼的Visual Studio C WinAPI GUI应用程序,这种方法似乎不常见。我不能声称自己特别具有创新性,所以我想知道我错过了什么,因为这种方法似乎至少在对资源文件进行手工编辑时效果很好。

我正在考虑进行另一次迭代,其中我将重复的每一行的控件列表移动到无模式对话框模板中,并且让原始对话框成为6个静态窗口的堆栈,每行一个。

这种方法的好处是定义更少,并且能够重用定义。将新功能插入现有的对话行为源代码也更容易,尽管这主要是因为这些只是简单的自动复选框。

我看到的一个问题是在执行此更改后使用Visual Studio工具。但是,此特定应用程序的资源文件无论如何都无法与Visual Studio资源编辑工具一起使用。

当我需要向无模式对话框模板添加一些额外的复选框时,这种方法已经有了回报。我必须做的资源文件更改是将新的对话框模板添加其他复选框,并调整原始对话框大小,无模式对话框大小和静态窗口的大小,以使所有内容都可见。

实施

我实施的替代方案是:

  • 使用一组复选框创建一个对话框模板
  • 将无模式对话框的对话框模板样式修改为WS_CHILD
  • 在新对话框模板的原始对话框的六行中的每一行上创建一个静态窗口
  • 将无模式对话框的实例放入每行的静态窗口中

该对话框的新版本看起来像screen shot of new version of the data entry dialog当显示原始对话框时,init对话框消息的处理程序创建一组六个无模式对话框,每个新添加的静态窗口一个,对话框的父窗口是静态的窗口。这将无模式对话框置于静态窗口中,当静态窗口移动时,无模式对话框也会移动。

所有六个无模式对话框都使用相同的对话框消息处理程序。消息处理程序本身不处理任何消息。

无模式对话框模板是:

IDD_A170_DAYS DIALOG DISCARDABLE  0, 0, 240, 20
STYLE  WS_CHILD | WS_VISIBLE
FONT 8, "MS Sans Serif"
BEGIN
    CONTROL         "Ovr",IDD_A170_STR1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,1,25,10
    CONTROL         "AND",IDD_A170_STR2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,35,1,40,10
    CONTROL         "S",IDD_A170_CAPTION1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,75,1,20,10
    CONTROL         "M",IDD_A170_CAPTION2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,100,1,20,10
    CONTROL         "T",IDD_A170_CAPTION3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,1,20,10
    CONTROL         "W",IDD_A170_CAPTION4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,1,20,10
    CONTROL         "T",IDD_A170_CAPTION5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,175,1,20,10
    CONTROL         "F",IDD_A170_CAPTION6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,195,1,20,10
    CONTROL         "S",IDD_A170_CAPTION7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,220,1,20,10
END

和静态窗口的主对话框是:

IDD_A170 DIALOG DISCARDABLE  2, 17, 530, 190
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Set Sales Code Restriction Table of PLU (AC 170)"
FONT 8, "MS Sans Serif"
BEGIN
    LTEXT           "Address              (PLU Sales Code)",IDD_A170_CAPTION1,14,10,64,20
    LTEXT           "Date",IDD_A170_CAPTION2,86,14,28,12
    LTEXT           "Day of week",IDD_A170_CAPTION3,115,10,33,21
    LTEXT           "Start hour",IDD_A170_CAPTION4,153,10,20,18
    LTEXT           "Minute",IDD_A170_CAPTION5,182,14,26,12
    LTEXT           "End hour",IDD_A170_CAPTION6,217,10,20,18
    LTEXT           "Minute",IDD_A170_CAPTION7,245,14,26,12
    LTEXT           "Override/Type",IDC_STATIC,290,14,50,12
    LTEXT           "Days To Restrict",IDC_STATIC,390,14,100,12
    LTEXT           "",IDD_A170_STR1,8,34,74,12                     // first control on line 1
    EDITTEXT        IDD_A170_DATE1,87,33,18,12,ES_AUTOHSCROLL
    SCROLLBAR       IDD_A170_DATESPIN1,104,33,8,12,SBS_VERT
    EDITTEXT        IDD_A170_WEEK1,119,33,18,12,ES_AUTOHSCROLL
    SCROLLBAR       IDD_A170_WEEKSPIN1,136,33,8,12,SBS_VERT
    EDITTEXT        IDD_A170_SHOUR1,151,33,18,12,ES_AUTOHSCROLL
    SCROLLBAR       IDD_A170_SHOURSPIN1,168,33,8,12,SBS_VERT
    EDITTEXT        IDD_A170_SMINUTE1,183,33,18,12,ES_AUTOHSCROLL
    SCROLLBAR       IDD_A170_SMINUTESPIN1,200,33,8,12,SBS_VERT
    EDITTEXT        IDD_A170_EHOUR1,214,33,18,12,ES_AUTOHSCROLL
    SCROLLBAR       IDD_A170_EHOURSPIN1,231,33,8,12,SBS_VERT
    EDITTEXT        IDD_A170_EMINUTE1,246,33,18,12,ES_AUTOHSCROLL
    SCROLLBAR       IDD_A170_EMINUTESPIN1,263,33,8,12,SBS_VERT
    LTEXT           "D1",IDD_A170_DAYS_1,281,33,240,20             // static window to contain the modeless dialog box from the template IDD_A170_DAYS above
    //   .. repeated sequence for 5 more lines
    CONTROL         "MDC 298 - Sales Restriction Type is AND",IDD_A170_MDC_PLU5_ADR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,140,170,9
    LTEXT           "[Address :  1 - 6, Date : 0 - 31",IDD_A170_CAPTION8,9,154,99,9
    LTEXT           "Day of week : 0 - 7 (1 - Sunday, 7 - Saturday)]",IDD_A170_CAPTION9,110,154,167,9
    LTEXT           "[Hour : 0 - 24, Minute : 0 - 59 (For 0:00, enter 24:00)]",IDD_A170_CAPTION10,9,168,167,9
    PUSHBUTTON      "&Ok",IDOK,285,154,48,20
    PUSHBUTTON      "&Cancel",IDCANCEL,345,154,48,20
END

您可能会注意到我刚刚在新的无模式对话框中重用了一些已在原始对话框中使用的定义。我能够这样做是因为控件标识符特定于对话框本身。因此,在不同的对话框中使用相同的定义不会导致问题,因为使用GetDlgItem()来获取对话框中控件的窗口句柄需要特定对话框实例的窗口句柄。

然后我创建了一组辅助函数来处理无模式对话框的实例。

static struct {
    int   iId;
    HWND  hWnd;
} A170DlgTabs[10] = { {0, 0} };

// modeless dialog box message handler which has nothing to do but the
// WinAPI requires it.
BOOL    WINAPI  A170DlgChildProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
        return FALSE;
}

void  A170ModeLessChildDialogClear ()
{
    memset (A170DlgTabs, 0, sizeof(A170DlgTabs));
}

HWND A170ModeLessChildDialog (HWND hParentWnd, int nCmdShow, int iId)
{
    int   i;
    HWND  hWnd = DialogCreation(hResourceDll/*hActInst*/,  //RPH 4-23-03 Multilingual
                                   MAKEINTRESOURCEW(IDD_A170_DAYS),
                                   hParentWnd,
                                   A170DlgChildProc);
    hWnd && ShowWindow (hWnd, nCmdShow);
    for (i = 0; i < sizeof(A170DlgTabs)/sizeof(A170DlgTabs[0]); i++) {
        if (A170DlgTabs[i].hWnd == 0) {
            A170DlgTabs[i].iId = iId;
            A170DlgTabs[i].hWnd = hWnd;
            break;
        }
    }

    return hWnd;
}

HWND A170ModeLessChildDialogFind (int iId)
{
    int   i;
    HWND  hWnd = NULL;

    for (i = 0; i < sizeof(A170DlgTabs)/sizeof(A170DlgTabs[0]); i++) {
        if (A170DlgTabs[i].iId == iId) {
            hWnd = A170DlgTabs[i].hWnd;
            break;
        }
    }

    return hWnd;
}

USHORT A170ModeLessChildDialogSettings (int iId)
{
    int     i;
    USHORT  iBits = 0, kBits = 1;
    HWND hWnd = A170ModeLessChildDialogFind (iId);

    // least significant byte contains the bit mask for the days of the week.
    // the next higher byte contains the indicators for the override type or
    // whether MDC 298 is to be overriden or not.
    for (i = IDD_A170_CAPTION1; i <= IDD_A170_CAPTION7; i++, (kBits <<= 1)) {
        iBits |= IsDlgButtonChecked (hWnd, i) ? kBits : 0;
    }

    iBits |= iBits ? RESTRICT_WEEK_DAYS_ON : 0;

    iBits |= IsDlgButtonChecked(hWnd, IDD_A170_STR1) ? KBITS_RESTRICT_OVERRIDE_ANDOR : 0;
    iBits |= IsDlgButtonChecked(hWnd, IDD_A170_STR2) ? KBITS_RESTRICT_OVERRIDE_AND : 0;

    return iBits;
}


USHORT A170ModeLessChildDialogSettingsSetMask (int iId, USHORT  usMask)
{
    int     i;
    USHORT  k = 1;
    HWND    hWnd = A170ModeLessChildDialogFind (iId);

    CheckDlgButton(hWnd, IDD_A170_STR1, (usMask & KBITS_RESTRICT_OVERRIDE_ANDOR) ? TRUE : FALSE);
    CheckDlgButton(hWnd, IDD_A170_STR2, (usMask & KBITS_RESTRICT_OVERRIDE_AND) ? TRUE : FALSE);

    for (i = IDD_A170_CAPTION1; i <= IDD_A170_CAPTION7; i++, (k <<= 1)) {
        CheckDlgButton(hWnd, i, (usMask & k) ? TRUE : FALSE);
    }

    return usMask;
}
windows visual-studio winapi dialog
1个回答
0
投票

使用Visual Studio 2017社区版采用这种方法从对话框模板创建组件,然后用于构建GUI更容易。

我使用“关于”对话框进行了粗略的概念验证练习,该对话框是由创建新的Windows桌面应用程序项目自动生成的,因为它非常方便。

这是一个在对话框模板中表达的简单组件,简单性可能会使这种概念证明误导。

我开始使用作为新项目创建的初始Windows桌面应用程序框架。然后,我对使用New项目自动生成的About对话框进行了以下修改。我能够使用IDE和资源编辑器而无需手动编辑资源文件。

步骤如下:

  • 使用资源编辑器修改现有的“关于”对话框并创建新的无模式对话框
  • 添加一个新类来管理新的无模式对话框
  • 修改“关于”对话框消息处理程序以使用新类

使用资源编辑器修改对话框资源非常简单:

  • 通过使其更大并在列中添加两个静态文本窗口来修改自动生成的“关于”对话框
  • 为添加到“关于”对话框的每个静态文本框指定实际控件标识符,以便可以使用GetDlgItem()引用它们,这是一个重要步骤,因为IDE默认情况下不会将可用控件标识符分配给静态窗口
  • 切换到资源视图后,使用资源编辑器创建了一个新的对话框模板
  • 在对话框的“属性”列表中修改了几个“外观”属性,将对话框更改为无边框的无模式对话框
  • 使用资源编辑器将复选框添加到新对话框并删除默认按钮

源代码更改也相当简单,因为这是一个简单的控件。

新的“关于”对话框如下所示。我将两个静态窗口放大后将其添加到“关联”对话框中。每个静态窗口都有自己的基于新对话框模板的控件实例。请注意,对话框模板的大小大于静态窗口的大小,导致将对话框模板剪切到静态窗口的显示区域。

screenshot of modified About dialog

什么做的细节

创建和修改对话框样式

使用资源视图我添加了一个新对话框。我单击了新对话框以在资源编辑器中显示它。然后我通过更改border属性和style属性以及删除IDE首次创建对话框模板时添加的默认按钮来修改初始模态对话框模板,我将对话框模板转换为适合放入静态窗口的无模式对话框容器。

screenshot of Properties screenshots of before and after of new dialog

为行为创建代码

然后我创建了一个类CDialogChild来管理这个新的无模式对话框,其中包含以下源代码:

#pragma once

#include "resource.h"

class CDialogChild
{
private:
    // static shared by all instances of this class
    static LRESULT CALLBACK WndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

    enum {DIALOG_ID = IDD_DIALOG1};
    static const UINT idCheckBox[3];    // identifiers for the controls of the modeless dialog.

    // management data for each modeless dialog instance created.
    HINSTANCE m_hinst;       // handle to the instance resources
    HWND      m_hWnd;        // handle to the modeless dialog window
    HWND      m_hParent;     // handle to the parent of the modeless dialog, usually static window

    // operational data displayed and captured by this modeless dialog
    // this is the data for the various controls we have in the dialog template.
    bool      bCheckBox[3] = { false, false, false };

public:
    CDialogChild();
    ~CDialogChild();
    bool GetCheck(int iIndex);    // for reading checkbox value
    void GetCheckFinal(void);     // for capturing final checkbox states
    bool SetCheck(int iIndex, bool bValue);   // for writing checkbox value
    void SetCheckInitial(void);               // for setting the initial checkbox states.

    HWND Create(HWND hParent, HINSTANCE hinst);

};

执行:

#include "stdafx.h"
#include "CDialogChild.h"

const UINT CDialogChild::idCheckBox[3] = { IDC_CHECK1, IDC_CHECK2, IDC_CHECK3 };


CDialogChild::CDialogChild()
{
}


CDialogChild::~CDialogChild()
{
}

HWND CDialogChild::Create(HWND hParent, HINSTANCE hinst)
{
    // called to create the modeless dialog using the dialog resource in the
    // specified resource file instance. the hParent is the container we are
    // going to put this modeless dialogbox into.
    m_hinst = hinst;
    m_hParent = hParent;
    m_hWnd = CreateDialog(hinst, MAKEINTRESOURCE(DIALOG_ID), hParent, (DLGPROC)CDialogChild::WndProc);

    ShowWindow(m_hWnd, SW_SHOW);
    return m_hWnd;
}

bool CDialogChild::GetCheck(int iIndex)
{
    if (iIndex > 0 && iIndex < sizeof(bCheckBox) / sizeof(bCheckBox[0])) {
        iIndex = 0;
    }

    bCheckBox [iIndex] = IsDlgButtonChecked(m_hWnd, idCheckBox[iIndex]);
    return bCheckBox [iIndex];
}

bool CDialogChild::SetCheck(int iIndex, bool bValue)
{
    if (iIndex > 0 && iIndex < sizeof(bCheckBox) / sizeof(bCheckBox[0])) {
        iIndex = 0;
    }

    CheckDlgButton (m_hWnd, idCheckBox[iIndex], bValue);
    bCheckBox[iIndex] = bValue;
    return bCheckBox [iIndex];
}

void CDialogChild::GetCheckFinal(void)
{
    for (int iIndex = 0; iIndex < sizeof(bCheckBox) / sizeof(bCheckBox[0]); iIndex++) {
        bCheckBox[iIndex] = IsDlgButtonChecked(m_hWnd, idCheckBox[iIndex]);
    }
}

void CDialogChild::SetCheckInitial(void)
{
    for (int iIndex = 0; iIndex < sizeof(bCheckBox) / sizeof(bCheckBox[0]); iIndex++) {
        CheckDlgButton(m_hWnd, idCheckBox[iIndex], bCheckBox[iIndex]);
    }
}

// CDialogChild class Windows message procedure to handle any messages sent
// to a modeless dialog window. This simple example there is not much to do.
LRESULT CALLBACK CDialogChild::WndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    return (INT_PTR)FALSE;
}

使用对话框中的新控件

最后,我修改了About对话框,以使用基于对话框模板的新控件。第一件事是将静态窗口添加到“关于”对话框模板,以便为新控件提供容器,该控件为我想要控件实例的位置提供了位置。

接下来,我添加了处理源代码,以便在修改后的About对话框中使用基于对话框模板的新控件。

// other source code from the Windows Desktop Application main window handler
// is above this. We are only modifying the About dialog code which is at the
// bottom of the source file.

#include "CDialogChild.h"

CDialogChild myAbout1;
CDialogChild myAbout2;

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        myAbout1.Create(GetDlgItem(hDlg, IDC_STATIC4), hInst);
        myAbout1.SetCheckInitial();
        myAbout2.Create(GetDlgItem(hDlg, IDC_STATIC5), hInst);
        myAbout2.SetCheckInitial();
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            if (LOWORD(wParam) == IDOK) {
                myAbout1.GetCheckFinal();
                myAbout2.GetCheckFinal();
            }
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
© www.soinside.com 2019 - 2024. All rights reserved.