win32 htmlayout点击按钮创建新窗口,以及按钮图片样式

最近在做一个C++ win32的桌面图形程序,我不是C++程序员,做这个只是因为最近没什么java的活。

windows api,之前接触的时候,还是大学,那时用这个开发打飞机游戏纯粹是娱乐。现在基本忘光了。

我要实现的最初的需求也很简单,就是做一个界面,上面有按钮,点击按钮出现新的窗口。界面是htmlayout来做。所以就是看着代码,边学边做,完全摸着石头过河,这篇文章也没啥技术含量。

第一步,使用htmlayout,创建一个简单窗口,这在我上一篇文章有详细记述:http://www.cnblogs.com/rixiang/p/6605416.html

要说明一点,项目大的框架,例如如何htmlayoout怎样引入sdk等等在上述链接的文章里说明。本篇文章只具体记录我解决的问题。就是【win32 htmlayout点击按钮创建新窗口,以及按钮图片样式】。

第二步,点击按钮打开另一个新的窗口

这里我花了些时间摸索win32窗口的原理,其必要的流程是要先注册窗口ATOM MyRegisterClass(HINSTANCE hInstance),然后调用CreateWindow这一win32 API,之后

ShowWindow(hWnd, nCmdShow); 以及UpdateWindow(hWnd); main函数的话,接下来就是消息循环。

这里的重点,也就是我之前没注意到搞错过的事情,就是创建子窗口的时候,其CreateWindow中父窗口句柄的参数一定要放入父窗口的hWnd。另外就是注册lpszClassName一定要和创建的lpszClassName一致。

所以就是,触发点击按钮事件时,在该事件函数里调用创建子窗口的方法,接着进行子窗口注册、创建、显示、更新、消息循环的过程。

在创建子窗口后,通过EnableWindow(hWndPar,FALSE);函数使父窗口暂时失效,在关闭子窗口的时候,再启用父窗口。

第三步,窗口样式

我希望达到的样式是,界面没有默认的帮助菜单,以及右上角的X关闭。界面上只有按钮,关闭按钮也是自己定义。并且按钮背景图片是自己定义的。

实现了第二步的基础上又用了大概一天时间摸索。最后发现,取消默认的帮助菜单其实是需要在窗口注册函数里定义:

wcex.lpszMenuName    = NULL;//MAKEINTRESOURCE(IDC_WIN32HTML);

之后又困惑于自己CSS里定义的按钮背景图片为何没有生效,最后我试着把html加载路径由相对路径改为绝对路径就OK了。

      2017年6月27日补充:要达到界面没有默认的帮助菜单,以及右上角的X关闭的效果,CreateWindow函数的第三个函数需要为:WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_POPUP

        hwnd = CreateWindow("", NULL, WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_POPUP,
CW_USEDEFAULT, , , , hWndPar, NULL, hInst, NULL);

最后效果:

win32 htmlayout点击按钮创建新窗口,以及按钮图片样式

点击上面的按钮则弹出新窗口,点击下面的按钮则直接退出。

win32 htmlayout点击按钮创建新窗口,以及按钮图片样式

下面是源代码,以后扩展下也许会放到github,暂时就直接贴代码了。作为java程序员,感觉桌面窗口程序开发也是非常常见的需求,以后可以做一个总结,把python、C++等等多种桌面窗口程序demo做个合集,放到github:

Win32Html.cpp,这里定义的是main函数和主窗口:

// Win32Html.cpp : 定义应用程序的入口点
#include "stdafx.h"
#include "Win32Html.h"
#include "New.h" #include <htmlayout.h>
#include "behaviors/notifications.h"
#include <htmlayout_behavior.hpp> #pragma comment(lib,"HTMLayout.lib")
#define MAX_LOADSTRING 100 // 全局变量:
HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); // 父窗口句柄
HWND hWnd; #include "New.h" int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine); MSG msg;
HACCEL hAccelTable; // 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_WIN32HTML, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance); // 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
} hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32HTML)); // 主消息循环:
while (GetMessage(&msg, NULL, , ))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} return (int) msg.wParam;
} //
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
// 作者: sonne
// 日期: 2017-03-23
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = ;
wcex.cbWndExtra = ;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32HTML));
wcex.hCursor = NULL;//LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+);
wcex.lpszMenuName = NULL;//MAKEINTRESOURCE(IDC_WIN32HTML);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_ICON)); return RegisterClassEx(&wcex);
} //
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
// 作者: sonne
// 日期: 2017-03-23
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, NULL, WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_POPUP/*WS_OVERLAPPED*/,
CW_USEDEFAULT, , , , NULL, NULL, hInstance, NULL); if (!hWnd)
{
return FALSE;
} ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd); return TRUE;
} void OnButtonClick(HELEMENT button); struct DOMEventsHandlerType: htmlayout::event_handler
{
DOMEventsHandlerType(): event_handler(0xFFFFFFFF) {}
virtual BOOL handle_event (HELEMENT he, BEHAVIOR_EVENT_PARAMS& params )
{
switch( params.cmd )
{
case BUTTON_CLICK: OnButtonClick(params.heTarget); break;// click on button
case BUTTON_PRESS: break;// mouse down or key down in button
case BUTTON_STATE_CHANGED: break;
case EDIT_VALUE_CHANGING: break;// before text change
case EDIT_VALUE_CHANGED: break;//after text change
case SELECT_SELECTION_CHANGED: break;// selection in <select> changed
case SELECT_STATE_CHANGED: break;// node in select expanded/collapsed, heTarget is the node
case POPUP_REQUEST:
break;// request to show popup just received,
// here DOM of popup element can be modifed.
case POPUP_READY: break;// popup element has been measured and ready to be shown on screen,
// here you can use functions like ScrollToView.
case POPUP_DISMISSED: break;// popup element is closed,
// here DOM of popup element can be modifed again - e.g. some items can be removed
// to free memory.
case MENU_ITEM_ACTIVE: // menu item activated by mouse hover or by keyboard
break;
case MENU_ITEM_CLICK: // menu item click
break; // "grey" event codes - notfications from behaviors from this SDK
case HYPERLINK_CLICK: break;// hyperlink click
case TABLE_HEADER_CLICK: break;// click on some cell in table header,
// target = the cell,
// reason = index of the cell (column number, 0..n)
case TABLE_ROW_CLICK: break;// click on data row in the table, target is the row
// target = the row,
// reason = index of the row (fixed_rows..n)
case TABLE_ROW_DBL_CLICK: break;// mouse dbl click on data row in the table, target is the row
// target = the row,
// reason = index of the row (fixed_rows..n) case ELEMENT_COLLAPSED: break;// element was collapsed, so far only behavior:tabs is sending these two to the panels
case ELEMENT_EXPANDED: break;// element was expanded, }
return FALSE;
} } DOMEventsHandler; LRESULT CALLBACK HTMLayoutNotifyHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, LPVOID vParam)
{
// all HTMLayout notification are comming here.
NMHDR* phdr = (NMHDR*)lParam; switch(phdr->code)
{
case HLN_CREATE_CONTROL: break; //return OnCreateControl((LPNMHL_CREATE_CONTROL) lParam);
case HLN_CONTROL_CREATED: break; //return OnControlCreated((LPNMHL_CREATE_CONTROL) lParam);
case HLN_DESTROY_CONTROL: break; //return OnDestroyControl((LPNMHL_DESTROY_CONTROL) lParam);
case HLN_LOAD_DATA: break;
case HLN_DATA_LOADED: break; //return OnDataLoaded((LPNMHL_DATA_LOADED)lParam);
case HLN_DOCUMENT_COMPLETE: break; //return OnDocumentComplete();
case HLN_ATTACH_BEHAVIOR: break;
}
return ;
} //
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
// 作者: sonne
// 日期: 2017-03-23
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
LRESULT lResult;
BOOL bHandled; // HTMLayout +
// HTMLayout could be created as separate window
// using CreateWindow API.
// But in this case we are attaching HTMLayout functionality
// to the existing window delegating windows message handling to
// HTMLayoutProcND function.
lResult = HTMLayoutProcND(hWnd,message,wParam,lParam, &bHandled);
if(bHandled)
return lResult; // 相对路径
char* chRelativePath = "htmlayoutDemo.htm";
WCHAR wsRelativePath[]; switch (message)
{
case WM_CREATE:
{
// Normally HTMLayout sends its notifications
// to its parent.
// In this particular case we are using callback function to receive and
// and handle notification. Don't bother the desktop window (parent of this window)
// by our notfications.
HTMLayoutSetCallback(hWnd,&HTMLayoutNotifyHandler,); // attach DOM events handler so we will be able to receive DOM events like BUTTON_CLICK, HYPERLINK_CLICK, etc.
htmlayout::attach_event_handler(hWnd, &DOMEventsHandler); memset(wsRelativePath,,sizeof(wsRelativePath));
//char* 转LPCWSTR
MultiByteToWideChar(CP_ACP, , chRelativePath, strlen(chRelativePath)+, wsRelativePath,
sizeof(wsRelativePath)/sizeof(wsRelativePath[])); //使用绝对路径
WCHAR wsPath[MAX_PATH];
GetCurrentDirectoryW(,wsPath);
wcscat(wsPath,L"\\");
wcscat(wsPath,wsRelativePath); HTMLayoutLoadFile(hWnd,wsPath); SetWindowText(hWnd,"身份认证系统");
SetForegroundWindow( hWnd ); //窗口初始化
DWORD dWidth = HTMLayoutGetMinWidth(hWnd);
DWORD dHeight = HTMLayoutGetMinHeight(hWnd,dWidth);
int cx = GetSystemMetrics(SM_CXSCREEN);
int cy = GetSystemMetrics(SM_CYSCREEN);
SetWindowPos(hWnd,HWND_TOPMOST,cx/-dWidth/,cy/-dHeight/,dWidth,dHeight,SWP_NOZORDER);
HRGN hRgn;
RECT rect;
GetWindowRect(hWnd,&rect);
hRgn = CreateRoundRectRgn(,,rect.right-rect.left,rect.bottom-rect.top,,);
SetWindowRgn(hWnd,hRgn,TRUE);
}
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage();
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return ;
} // “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE; case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
} //************************************
// 作 者: sonne
// 函 数 名: OnButtonClick
// 功 能: 按钮响应事件
// 完 整 名: OnButtonClick
// 访 问 权: public
// 返回值类型: VOID
// 方法限定符:
//************************************
void OnButtonClick(HELEMENT button)
{
htmlayout::dom::element cBut = button;
if (!wcscmp(cBut.get_attribute("id"),L"button1"))
{
NEW::InitInstance(hInst, , hWnd, szWindowClass);
}
//关闭按钮
if (!wcscmp(cBut.get_attribute("id"),L"button2"))
{
PostMessage(hWnd,WM_DESTROY,NULL,NULL);
}
}

New.h,这里定义的是点击按钮弹出的新窗口的一系列方法:

#pragma once

#include <htmlayout.h>
#include "behaviors/notifications.h"
#include <htmlayout_behavior.hpp> namespace NEW
{
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); //
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
// 作者: sonne
// 日期: 2017-03-27
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK NewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
LRESULT lResult;
BOOL bHandled;
lResult = HTMLayoutProcND(hWnd,message,wParam,lParam, &bHandled);
if(bHandled)
return lResult; char* path = "newPage.htm";
WCHAR wsz[]; switch (message)
{
case WM_CREATE:
memset(wsz,,sizeof(wsz));
MultiByteToWideChar(CP_ACP, , path, strlen(path)+, wsz, sizeof(wsz)/sizeof(wsz[]));
HTMLayoutLoadFile(hWnd,wsz); //Hello.htm需要放在和exe同一目录,记得把dll也copy过去
break; case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage();
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return ;
} //
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
// 作者: sonne
// 日期: 2017-03-27
//
ATOM NewRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)NewWndProc;
wcex.cbClsExtra = ;
wcex.cbWndExtra = ;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32HTML));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32HTML);
wcex.lpszClassName = "hh";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex);
} //
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
// 作者: sonne
// 日期: 2017-03-27
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow, HWND hWndPar, TCHAR szWindowClass[])
{
HWND newHWnd;
hInst = hInstance; // 将实例句柄存储在全局变量中
htmlayout::dom::element root;
char szCurrPath[MAX_PATH];
GetCurrentDirectory(MAX_PATH,szCurrPath); NewRegisterClass(hInst); newHWnd = CreateWindow("hh", NULL, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, , CW_USEDEFAULT, , hWndPar, NULL, hInst, NULL); EnableWindow(hWndPar,FALSE); if (!newHWnd)
{
return FALSE;
} ShowWindow(newHWnd, );
UpdateWindow(newHWnd); MSG msg;
HACCEL hAccelTable;
hAccelTable = LoadAccelerators(hInst, (LPCTSTR)IDR_ACCELERATOR);
while (GetMessage(&msg, NULL, , ))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Sleep();
EnableWindow(hWndPar,TRUE);
SetForegroundWindow( hWndPar );
BringWindowToTop(hWndPar);
root = htmlayout::dom::element::root_element(hWndPar);
if (root)
{
root.refresh();
}
SetCurrentDirectory(szCurrPath);
return TRUE;
} // “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE; case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
} }

Resource.h:

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Win32Html.rc
// #define IDS_APP_TITLE 103 #define IDR_MAINFRAME 128
#define IDD_WIN32HTML_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_WIN32HTML 107
#define IDI_SMALL 108
#define IDC_WIN32HTML 109
#define IDC_MYICON 2 //{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Settings.rc 使用
//
#define IDI_ICON 101
#define IDR_ACCELERATOR 102
#define IDD_MASK 103
#define IDC_X 1002 #ifndef IDC_STATIC
#define IDC_STATIC -1
#endif
// 新对象的下一组默认值
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 130
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif

htmlayoutDemo.htm:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<style type="text/css">
body {
font:12px Helvetica,Georgia,Arial,sans-serif;
margin:0;
padding:0;
background:#fff;
}
ul{
list-style-type: none;
margin:0px;
}
.bStyle1 {
background:url(test_button1.png) top left no-repeat;
width:180px;
height:130px;
border:none;
cursor:pointer;
}
.bStyle2 {
background:url(test_button2.png) top left no-repeat;
width:180px;
height:130px;
border:none;
cursor:pointer;
}
</style>
<title>
new page
</title>
</head>
<body>
<h2>htmlayout demo.</h2>
<ul>
<li><input type="button" id="button1" class="bStyle1"/></li>
<li><input type="button" id="button2" class="bStyle2"/></li>
</ul>
</body>
</html>

项目结构:

win32 htmlayout点击按钮创建新窗口,以及按钮图片样式

github代码地址:

https://github.com/SonnAdolf/sonne_desktop_graphical_development

上一篇:ELK简介


下一篇:浏览器拦截js打开新窗口