热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WindowsdialogdesigninC++withoutdialogtemplates.

WindowsdialogdesigninC++withoutdialogtemplates. Downloadautodlg.zip-37.2KB -codeonlyDownlo

Windows dialog design in C++ without dialog templates.

 



  • Download autodlg.zip - 37.2 KB - code only

  • Download VS2013Express_Example.zip - 39.7 KB

  • Download VS2005_Example.zip - 41.7 KB


Image 1

Background,   Introduction, 
Fundamentals of coded layout design, 
Building a dialog class, 
Reducing the verbosity of the layout definition with your own macros
Control methods and notifications, 
Controlling colors and fonts, 
Beyond notifications, 
Summary of event handling and more about aesthetic metrics structs,
Painted controls, 
A more complex example,   Tabbed dialogs, 
Display text in multiple languages
Quick reference,  How it works, 
Points of Interest, History

Code update 5 June 2015 - Language files would cause Link errors with multiple compilation units  - now fixed by changes to autodlg.h only.


Background

The Visual Dialog Editor, class wizard and MFC have been the entry point for many people (including myself) to C++ Windows programming. If all the wizards work correctly it is a comfortable way of starting to write code in an event driven and component supplied environment. However the wisdom of remaining with this as the only way to design and define a dialog is questionable. In particular:



  • Dragging and positioning boxes is still labour, especially if high standards of alignment and presentation are required. A lot of labour if you have to respond to frequently changing aestheitic directives.

  • The visual design requires a laborious assignment of names for control IDs which has to be done manually through the IDE.

  • The visual design requires a lot of code to be written or generated to make it do anything and much of this is not at all programmer friendly. The wizards that compensate for this can break, and may be unavailable or not tuned to the class library you are using..

  • Reusing a Dialog in different applications requires an awkward merging of its dialog template into the host applications .rc file. Code libraries cannot provide dialogs without imposing a requirement to carry out this merging process

I decided that a new approach is needed with the fundamental requirement being that:



  • the entire dialog including layout be defined by code which does not require the assistance of IDE tools to be written nor the presence of IDE generated resources to be executed.

Presented here is a way of achieving this encapsulated as a C++ base class for windows dialogs. It is 'Win 32' in that it requires neither MFC nor ATL/WTL. However it will sit comfortable with either class library and supports incorporating thier control class wrappers in its dialog definitions.


Introduction

The fundamental difference with the approach presented here is that your code does all the work of creating the dialog rather than have Windows create a dialog from a dialog template resource that you then attach code to. There is no need for dialog or control ID's because there is no run-time mapping of code to a dialog template. Instead there is compile time mapping of each control to a C++ variable. Furthermore each of those variables has its own unique data type. Such rich typing is perhaps radical but it brings many benefits and is key to the design. It allows the C++language to resolve many issues at compile time through type resolution and this reduces and simplifies the code that you have to write. In practice this means that for every variable representing a control, say btnCancel, a unique data type will be generated with the same name prefixed by an underscore _btnCancel.As you will see, there are times when you will need to refer to a control by its data type _btnCancel rather than its variable name btnCancel.

The unfamiliarity of coding a layout instead of dragging and dropping it is dealt with in the next section and after that, most things are simpler, cleaner and more concise than you are probabaly used to.

There are some other innovations including:



  • all control variables carry a dynamic text dynamic text buffer as_text through which the controls window text may be read and written, and which persists when the dialog is closed.

  • support for non-windows controls that are simply painted onto the dialog and respond to mouse events. (examples are provided for some cases where this makes more sense).

  • support for imposing aesthetic metrics (button sizes, spacing, colours, control styles etc.) at an application level on all dialogs hosted.

  • replacement of the notion of 'Anchors' with Expand_X and Expand_Y styles for controls that can benefit from being enlarged if the dialog window is enlarged.

  • some modest but effective streamlining of Win 32 programming with controls.


Fundamentals of coded layout design

This is what could be described as the hard bit because it replaces the use of a visual dialog editor. By the time you have your dialog working you will have written less code but the coded layout instructions require a bit more mindfullness than dragging boxes until it looks right. There will be rewards for that mindfullness though and you will not get them if you use visual editing. This is how it works:

Each control is fully defined, declared, and positioned with the following macro:

N.B. The verbose AUTODLG moniker prefixes all macros so they don't clash with anything else in the global namespace. 

C++

 

at - where Dx, and Dy are the absolute position from the top left corner of the dialog.
Image 2

  • to_right_of<_control> - where Dx, and Dy are offsets from that position
    Image 3

  • under<_control> - where Dx, and Dy are offsets from that position
    Image 4

  • and the label_locator can be any one of:



    • label_left - where 'by' is the amount by which the label extends to the left of the control.
      Image 5

    • label_left_align<_control> - where the left edge of the label aligns with the left edge of another control.
      Image 6

    • label_above- where 'by' is the amount by which the label hangs above the control.
      Image 7

    • label_top_align<_control>- where the top edge of the label aligns with the top edge of another control.
      Image 8

    Note that when referring to previously declared controls, we refer to them by their data type name _btnActivate rather than the the variable name btnActivate

    The data type name can also be used to access the calculated (at compile time) position and size of the control

    e.g. _btnActivate::left

    toprightbottomwidth, and height are also available. It can be useful to use these with the at verb where to_right_of or under aren't exactly what you want.

    For instance; There are no verbs for 'to left of' or 'above'. This is because the size of the newly created control is needed to calculate the position. However you, the programmer, do know the size of the control you are adding and can position a control to the left of another as follows:


    C++
     

    AUTODLG_WIDTH_TO(x_coord)

  • AUTODLG_HEIGHT_TO(y_coord)

  • They are useful for instructing a control to use up the available space up to the edge of another control.  


    C++
     

    control_type, control_styles, extended_stylesdescribe the control itself. The control type can be a raw control specified by its windows class name (BUTTON, EDIT, LISTBOX etc.). or it can be a control wrapper class such as the CButton, CEdit and CListBox of MFC and WTL

    Building a dialog class

    It is a good idea to start with a visual plan. You can hold it in your head, sketch it with pen and paper, use Paint.exe or even a visual dialog editor (any old one will do). I created this with Paint for a dialog that captures an entry code from the user.

    Image 9 

    There is always a way of coding any layout, indeed many ways, but what you want is a way that maintains its integrity when parameters such as button width change and also that is amenable to being altered. The key to this is to identify which groupings and alignments you care about and the dependancy chains involved. In this example we only have one group but we do care about some alignments as illustrated in the following diagram.

    Image 10

    We want the Cancel button to line up under the Reset button, the OK button to the right of the Cancel button by a standard spacing and we want the right edge of the edit to line up with the right of the OK button with its label left aligning with the Reset button. The more arbritrary exact position of the left edge of the edit can then be adjusted without affecting anything else.

    The dialog class definition takes a metrics struct as a template parameter and uses autodlg::def_metrics as a default. For now you need to know that autodlg::def_metrics defines the following enums


    C++
     

    dlg has been declared as a EnterCodeDlg<> type with empty braces <>. This means that it will use the default metrics struct with which it was defined. A different metrics struct (but with the same parameter structure) can be forced on it by passing it in as the template parameter. e.g.


    C++
     

    OnInitDialog is called after the dialog and all its controls have been created. Exactly as you will be accustomed to.

    The controls are disabled using their enable method. This is one of a generic group of methods that can be called on all controls. A full list of generic methods provided for all controls can be found in the quick reference section below.

    Now lets deal with the controls as they become enabled starting with the cancel button that is always enabled and always does the same thing. We just create a nofication handler for btnCancelThere is no need to create a message map entry or any code to call this handler. It will be called automatically simply by being there.


    C++
     

    _btnCancel*. This is what determines that only btnCancel will call the handler. We aren't interested in the pointer passed in because we know it just points to btnCancel.

    EndDialog() does what youi are accustomed to but can be used to end both modal and modeless dialogs so there is no need to code them differently. Typically its argument will be IDOK or IDCANCEL which are always defined. You will not be able to pass control IDs because you don't have any. You can of course pass your own numbers that mean things to you.

    Now the response to text being entered into the edit control:


    C++
     

    BannerLabel is distinguished from PaintedLabel only by having a different type name and this is simply so that it can be identified for special handling by the OnCtlColorByControlType(BannerLabel*, ...) handler as defined in the my_app_metrics struct defined earlier. If you decide to remain with STATIC controls for labels then you can use STATIC_BANNER and that also will recieve the same special handling from the OnCtlColorByControlType(STATIC_BANNER*, ...) handler provided in my_app_metrics

    Other painted controls included in the library are SpacerControl which is a  hidden reference frame on which other controls can be hung (it has no code), GroupBox which is a replacement for the Windows group box and PaintedTabFrame which you have to use instead of a Windows tab control. The code for these can be found at the foot of autodlg.h. Further anecdotal examples StopButtonStartupButton and PaintedSmiler can be found in misc_painted_controls.h

    If a painted control only displays and needs no mouse interaction then it should be derived from painted_control and needs only to implement bool OnPaint(HDC& hDC, RECT& r, bool bMouseOver) to handle paint messages. These are the only messages it will recieve unless you send some to it using do_msg. The RECT r passed in is in dialog coordiates because the hDC is that of the dialog.

    If mouse interaction is required then it must derive from painted_mouse_control and will also have to provide the handler void OnNonPaintMessage(UINT message, WPARAM wParam, LPARAM lParam). This will recieve mouse messages which can be decoded using the mouse_parms struct. The x,y coordinates in this case are with respect to your control, not the dialog. 


    C++
     

    MOUSE_OVER_STYLE which causes controls to invalidate when the mouse enters and leves it rectangle

  • EXPAND_X_STYLE and EXPAND_Y_STYLE which mark a control for expansion if the dialog is resized to larger than its design sise.

  •  

    Naming conventionsthese names are generated automatically



    • The name of data type of a control on your dialog is the control name prefixed by underscore 
      e.g. the data type of btnOK is _btnOK

    • The name of a control's label is the name of the control postfixed by _label 
      e.g. the label for edtCode is called edtCode_label

    Dialog public members


    C++
     

    set_first_ctrl may be passed a reference to the dialog in whih it is written as in the example above, or it can be passed another dialog 


    C++
     

    auto_string is the dynamic text buffer type that is used for the as_text variable supplied with every control. Its special feature is that writing to it also writes to the control and reading from it reads from the control. Otherwise it is a very basic dynamic text buffer. You can do the following with it:


    C++
     

    BUTTONCHECKBOXRADIOBUTTONGROUPBOXEDITRICHEDITLISTBOXCOMBOBOX
    SCROLLBARSTATICSTATIC_BANNERSysMonthCal32PROGRESSBAR
    SysListView32SysTreeView32 

    List of painted controls supplied with the library

    PaintedLabelPaintedBannerSpacerControlGroupBoxPaintedTabFrame

    For more informatiom about these and also how to design painted controls see the Painted controls section

    PaintedTabFrame

    PaintedTabFrame should be declared in your layout as a control, specifying how many tabs it has as a template parameter.


    C++
     

    PaintedTabFrame public methods in addition the generic control methods are:


    C++
     

    autodlg_on_control_query is a virtual function implementation and does actualy generate run-time code that will be called.

    Finally the variable is declared to be of the newly defined type.

    An immediate advantage of declaring the layout as static const class info  is that having defined and declared one control:


    C++
     

    control is the most developed common enumerable base class. That is it is the only class that says that it is a control without being at all specific about what type of control. control is in turn derived from dialog_element which is less than a control and serves two purposes; to provide the virtual function autodlg_on_control_query which which can be called by everything deriving from dialog_element and to provide a null control marker when it stands alone. 

    Controls are declared as adjacent variables of varying sizes. There is no list or array of pointers to traverse so another way has to be found. The method used is to move from one control to the next by incrementing a pointer by its class size. The only thing is that the full class size is not immediately available to the base class control. It can only be found by calling a virtual function that is implemented in the complete derived class. The one and only virtual function is autodlg_on_control_query which is used for all such queries including the class info registered by the AUTODLG_CONTROL macro. A query is a class with a code, appropriate data member and a function to execute the query. It can request an action or information. The query is initialised, passed to autodlg_on_control_query and is executed by the autodlg_on_control_query in the most derived class. The most important query has code INFO and this fill itself out with all of the class info:


    C++
    Shrink ▲   

    T is the most derived class passed in by its autodlg_on_control_query implementation. Two methods are called on the dialog. In the general case, they will be handled by the following sinks provided by the AUTODLG_DECLARE_CONTROLS_FOR macro which do nothing and compile nothing.


    C++
     

    template <class C> inline void OnNotificationsByControlType\
    (C* pC, UINT NotifyCode, LPARAM lParam){}\
    template <class C> inline void OnNotificationsFrom\
    (C* pC, UINT NotifyCode, LPARAM lParam){}\

    They are indifferent to the type of the first parameter. Any type will be accepted. However if you provide the dialog with a version that types the first parameter as the control you called it from:

    C++

     

    void OnNotificationsFrom(_btnCancel*, UINT NotifyCode, LPARAM lParam)
    { ....

    then that will get called instead. So all you have to do is provide a handler with the first parameter typed for the control and it will get called. All event handling uses this mechanism.

    Those are the main arteries of how it works. To know more you have to examine the code according to your curiosity. Most of it is found inside the class dialog_base. It has to be encapsulated somwhere and putting control class definitions inside the dialog base class eliminates many problems of precedence between the two and makes for cleaner coding.

    Points of Interest

    I already knew from working on a previous project Units of measurement types in C++ that rich typing can allow the compiler to check many things and make better decisions without any run-time cost (because it has already been done during compilation) but it still surprised me that it could bring so many seemingly miraculous benefits to dialog design. It really is no miracle. It seems so because as designers we see the dialog layout as something variable because it is our job to vary it. This causes us to overlook the fact that it is a constant fully known at compile time for any given compilation. The use of dialog resource templates reinforces this oversight because it seems (and probably is) a variable loaded up at run-time. Expressing the layout as pure C++ code provides the opportunity for the constness of a dialog layout to be properly recognised by the language and providing an informative type for each control is the way to do it. The apparent miracles result from full information about the layout being available at compile time. 

    Most of the code has a very clear logic to follow and my hope is that this will work under what ever circumstances you want to throw at it. The least developed part is the resizing and rearranging of controls as a dialog is resized. Nevertheless you will probably find that it works well for you and that it is not difficult to avoid the corner cases that catch it out. I have decided to publish it as is and refine this later at my leisure.

    For me the biggest achievement is that coding your layout empowers you as a programmer, the skill you are good and clever with, and it releases you from having to put aside those fine skills to labour as a draughtsman and IDE operative.

    I have done this work and published it because I would like to see others using it. I enjoy writing libraries more than using them and in some ways I wrote it as a remedy for the years I have suffered with tradional dialog design. I wanted to put an end to that suffering for everyone. I would be very interested in any feedback on the joys and frustrations experienced by anyone who uses what is offered here.

    Finally I must mention the excellent series of articles Custom Controls in Win32 API by Martin Mitáš which I have used as a Bible while working on this project. It provided clarity on many  issues that I could find nowhere else.

    History

    First publication and release 29 May 2015
    Code update 3 June 2015 - Added "Display text in multiple languages" and fixed small errors.
    Code update 5 June 2015 - Language files would cause Link errors with multiple compilation units  - now fixed by changes to autodlg.h only


    License



    推荐阅读
    • 本文介绍了在多平台下进行条件编译的必要性,以及具体的实现方法。通过示例代码展示了如何使用条件编译来实现不同平台的功能。最后总结了只要接口相同,不同平台下的编译运行结果也会相同。 ... [详细]
    • 本文介绍了OkHttp3的基本使用和特性,包括支持HTTP/2、连接池、GZIP压缩、缓存等功能。同时还提到了OkHttp3的适用平台和源码阅读计划。文章还介绍了OkHttp3的请求/响应API的设计和使用方式,包括阻塞式的同步请求和带回调的异步请求。 ... [详细]
    • VSCode快速查看函数定义和代码追踪方法详解
      本文详细介绍了在VSCode中快速查看函数定义和代码追踪的方法,包括跳转到定义位置的三种方式和返回跳转前的位置的快捷键。同时,还介绍了代码追踪插件的使用以及对符号跳转的不足之处。文章指出,直接跳转到定义和实现的位置对于程序员来说非常重要,但需要语言本身的支持。以TypeScript为例,按下F12即可跳转到函数的定义处。 ... [详细]
    • 本文介绍了使用Rust语言编写、保存和编译程序的简单步骤。首先,打开记事本文件并编写程序代码,然后将代码保存到一个以.rs为扩展名的文件中。接下来,使用rustc命令来编译运行程序。最后,通过命令行运行编译后的程序,得到输出结果。如果遇到编译错误,可以下载Build Tools for Visual Studio 2017来解决。 ... [详细]
    • 1、工具VS2015OpenCV3.20下载地址:https:sourceforge.netprojectsopencvlibrary2、步骤1.下载工具ÿ ... [详细]
    • vb.net不用多线程如何同时运行两个过程?不用多线程?即使用多线程,也不会是“同时”执行,题主只要略懂一些计算机编译原理就能明白了。不用多线程更不可能让两个过程同步执行了。不过可 ... [详细]
    • GetWindowLong函数
      今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
    • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
      本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
    • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
    • Metasploit攻击渗透实践
      本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
    • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
    • 本文介绍了绕过WAF的XSS检测机制的方法,包括确定payload结构、测试和混淆。同时提出了一种构建XSS payload的方法,该payload与安全机制使用的正则表达式不匹配。通过清理用户输入、转义输出、使用文档对象模型(DOM)接收器和源、实施适当的跨域资源共享(CORS)策略和其他安全策略,可以有效阻止XSS漏洞。但是,WAF或自定义过滤器仍然被广泛使用来增加安全性。本文的方法可以绕过这种安全机制,构建与正则表达式不匹配的XSS payload。 ... [详细]
    • 如何提高PHP编程技能及推荐高级教程
      本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
    • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
      本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
    • 本文介绍了2015年九月八日的js学习总结及相关知识点,包括参考书《javaScript Dom编程的艺术》、js简史、Dom、DHTML、解释型程序设计和编译型程序设计等内容。同时还提到了最佳实践是将标签放到HTML文档的最后,并且对语句和注释的使用进行了说明。 ... [详细]
    author-avatar
    opheliamaizi
    这个家伙很懒,什么也没留下!
    PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有