PropertyPage变大变形问题
问题描述
- 手上维护的一个Tool, 开发平台是 Win7 + VS2008 MFC C++
- 由于前段时间微软发布声明不再对Win7做更新及支持,以及现在电脑基本上出厂预装Win10,所以不得不支持Win10了。由于不是专业的软件公司,上面要求能在Win10上正常使用就行了。
- 在迁移至Win10过程中发现部分也面出现变形的情况,原本在Win7上能正常显示的页面,在Win10 笔记本中中会在Height及Width方向上增大,导致页面超出屏幕,底部有些控件会显示不出来;在PC上页面虽然变大,由于PC屏幕较大的缘故,可以显示完全。
- 经过测试发现,基于一般Dialog的页面在Win10上不会出现变形,而基于PropertyPage的页面则会出现此问题。
- 刚开始猜测与高DPI有关,因为基于一般Dialog的页面在“放大模式为125%”(我这样称呼哈)同样也出现了变形,不过五一过后回来再测,发现一般Dialog的页面居然不会变形了,而PropertyPage仍然会,猜测或许是微软修复了对于一般Dialog的这个问题。
解决方式
方式1
本想着在Win10上放大了,超出屏幕,干脆加个VScrollBar或许就可解决该问题,不过加上倒是看得完了,但是由于在该页面使用了GDI绘制,而绘制的坐标是固定的,滚动VScrollBar时,GDI绘制的并未随着页面滚动,算是失败了。(不过通过加滚动条应该是可以解决的,就是太懒,不想去弄那些坐标,感觉挺麻烦…)
方式2
根据系统版本使用不同的对话框资源,这个也试了试,发现在Win10上不能获取正确的系统版本,不过下载的示例又是可以的,搞不懂,不知道是哪里设置错了,还是太菜啊。
方式3
获取屏幕宽度、高度,根据屏幕比例,调整页面的大小,不过感觉通过调整Add的每一个Page的高度,最终并不能调整高度,不是说PropertySheet的高度是根据最大的PropertyPage的大小来确定的吗。
方式4
使用TabCtrl替换原有的ProperSheet,不过这个就是比较麻烦,原有的对话框类基本上是不能使用了,需要根据TabCtrl的实现方法,重新实现每一个对话框类。不过唯一值得庆幸的是该方法是可行的,且效果与使用PropertySheet基本无差别。 虽然在实作前,有写Demo程序在Win10上测试,不会变形,但还是有点担心最终不可行。
方式5
虽然TabCtrl可行,但是这仅使用于我这个Tool中Page较少的情况,对于Page很多的情况改动起来就贼难受了,下面这种方法是可行且改动很小的方式,甚至比改TabCtrl还要小很多。这种方式是CSDN上一位资源主提供的,我也是下载了示例程序修改了一下适用我的程序,以下是主要Source code。
.h
/*****************************************************************************************
File: CBPropertySheet.h
Author: Ovidiu Cucu - Microsoft MVP Viusal C++
Codeguru nickname: ovidiucucu
Homepage: www.codexpert.ro
Updated: January 02, 2010
Contents: CCBPropertySheet class definition.
Remarks: CCBPropertySheet is an MFC-extension class for creating properties sheets
which are using a custom font.
******************************************************************************************/
#pragma once
#if _MSC_VER < 1300 // VS6.0 and older
#define DOMODAL_RET int
#else
#define DOMODAL_RET INT_PTR
#endif
class CCBPropertySheet : public CPropertySheet
{
DECLARE_DYNAMIC(CCBPropertySheet)
public:
CCBPropertySheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);
CCBPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);
virtual ~CCBPropertySheet() = 0;
// Attributes
private:
static __declspec(thread) WORD m_wFontSize;
static __declspec(thread) LPCTSTR m_pszFontFaceName;
// Operations
public:
// call this function to create a modal property sheet with custom font.
DOMODAL_RET DoModal(LPCTSTR pszFontFaceName, WORD wFontSize);
// call this function to create a modal property sheet with default font.
virtual DOMODAL_RET DoModal();
// call this function to create a modeless property sheet with custom font.
BOOL Create(LPCTSTR pszFontFaceName, WORD wFontSize, CWnd* pParentWnd = NULL,
DWORD dwStyle = (DWORD)-1, DWORD dwExStyle = 0);
// call this function to create a modeless property sheet with default font.
BOOL Create(CWnd* pParentWnd = NULL, DWORD dwStyle = (DWORD)-1, DWORD dwExStyle = 0);
// Overides
protected:
virtual void BuildPropPageArray();
// Implementation
private:
static int CALLBACK PropSheetProc(HWND hWndDlg, UINT uMsg, LPARAM lParam);
void Init(LPCTSTR pszFontFaceName, WORD wFontSize);
};
.cpp
/*****************************************************************************************
File: CBPropertySheet.cpp
Author: Ovidiu Cucu - Microsoft MVP Viusal C++
Codeguru nickname: ovidiucucu
Homepage: www.codexpert.ro
Updated: January 02, 2010
Contents: CCBPropertySheet class implementation.
Remarks: CCBPropertySheet is an MFC-extension class for creating properties sheets
which are using a custom font.
******************************************************************************************/
#include "stdafx.h"
#include "CBPropertySheet.h"
#include <AFXPRIV.H>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
WORD __declspec(thread) CCBPropertySheet::m_wFontSize = 0;
LPCTSTR __declspec(thread) CCBPropertySheet::m_pszFontFaceName = NULL;
IMPLEMENT_DYNAMIC(CCBPropertySheet, CPropertySheet)
/*****************************************************************************************
Function: CCBPropertySheet::CCBPropertySheet
Purpose: Constructs a CCBPropertySheet object.
Parameters: - nIDCaption: Resource ID of the caption to be used for the property sheet.
- pParentWnd: Parent window (default NULL).
- iSelectPage: Index of the page that will initially be on top (default 0).
******************************************************************************************/
CCBPropertySheet::CCBPropertySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
:CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
}
/*****************************************************************************************
Function: CCBPropertySheet::CCBPropertySheet
Purpose: Constructs a CCBPropertySheet object.
Parameters: - pszCaption: The string to be displayed on the property sheet caption.
- pParentWnd: Parent window (default NULL).
- iSelectPage: Index of the page that will initially be on top (default 0).
******************************************************************************************/
CCBPropertySheet::CCBPropertySheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
:CPropertySheet(pszCaption, pParentWnd, iSelectPage)
{
}
/*****************************************************************************************
Function: CCBPropertySheet::~CCBPropertySheet
Purpose: Destructs a CCBPropertySheet object.
******************************************************************************************/
CCBPropertySheet::~CCBPropertySheet()
{
}
/*****************************************************************************************
Function: CCBPropertySheet::PropSheetProc
Purpose: Application-defined callback function that the system calls when the property
sheet is being created and initialized
Parameters: - hWndDlg: Handle to the property sheet dialog box.
- uMsg: Message being received
- lParam: Additional information about the message.
Remarks: If uMsg is PSCB_PRECREATE, lParam is the address of a dialog template in
memory. This template is in the form of a DLGTEMPLATE or DLGTEMPLATEEX
structure followed by one or more DLGITEMTEMPLATE structures.
******************************************************************************************/
int CALLBACK CCBPropertySheet::PropSheetProc(HWND hWndDlg, UINT uMsg, LPARAM lParam)
{
switch(uMsg)
{
case PSCB_PRECREATE:
{
if((m_wFontSize > 0) && (NULL != m_pszFontFaceName))
{
LPDLGTEMPLATE pResource = (LPDLGTEMPLATE)lParam;
CDialogTemplate dlgTemplate(pResource);
dlgTemplate.SetFont(m_pszFontFaceName, m_wFontSize);
memmove((void*)lParam, dlgTemplate.m_hTemplate, dlgTemplate.m_dwTemplateSize);
}
}
break;
}
return 0;
}
/*****************************************************************************************
Function: CCBPropertySheet::Create
Purpose: Creates a modeless property sheet.
Parameters: - pszFontFaceName: Font face name.
- wFontSize: Font size.
- pParentWnd: parent window (default NULL).
- dwStyle: style (default -1).
- dwExStyle: extended style (default 0).
Remarks: Call this function for creating modeless property sheets with CUSTOM FONT.
******************************************************************************************/
BOOL CCBPropertySheet::Create(LPCTSTR pszFontFaceName, WORD wFontSize, CWnd* pParentWnd,
DWORD dwStyle, DWORD dwExStyle)
{
Init(pszFontFaceName, wFontSize);
return CPropertySheet::Create(pParentWnd, dwStyle, dwExStyle);
}
/*****************************************************************************************
Function: CCBPropertySheet::Create
Purpose: Creates a modeless property sheet.
Parameters: - pParentWnd: parent window (default NULL).
- dwStyle: style (default -1).
- dwExStyle: extended style (default 0).
Remarks: Call this function for creating modeless property sheets with DEFAULT FONT.
******************************************************************************************/
BOOL CCBPropertySheet::Create(CWnd* pParentWnd, DWORD dwStyle, DWORD dwExStyle)
{
Init(NULL, 0);
return CPropertySheet::Create(pParentWnd, dwStyle, dwExStyle);
}
/*****************************************************************************************
Function: CCBPropertySheet::DoModal
Purpose: Creates a modal property sheet.
Parameters: - pszFontFaceName: Font face name.
- wFontSize: Font size.
Remarks: Call this function for creating modal property sheets with CUSTOM FONT.
******************************************************************************************/
DOMODAL_RET CCBPropertySheet::DoModal(LPCTSTR pszFontFaceName, WORD wFontSize)
{
Init(pszFontFaceName, wFontSize);
return CPropertySheet::DoModal();
}
/*****************************************************************************************
Function: CCBPropertySheet::DoModal
Purpose: Creates a modal property sheet.
Remarks: Call this function for creating modal property sheets with DEFAULT FONT.
******************************************************************************************/
DOMODAL_RET CCBPropertySheet::DoModal()
{
Init(NULL, 0);
return CPropertySheet::DoModal();
}
/*****************************************************************************************
Function: CCBPropertySheet::Init
Purpose: Initializes font info as well as PROPSHEETHEADER structure in order to
use application-defined callback function (CCBPropertySheet::PropSheetProc).
Parameters: - pszFontFaceName: Font face name.
- wFontSize: Font size.
Remarks: Called either from Create and DoModal functions.
If m_wFontSize = 0 or pszFontFaceName is NULL then default font will be used.
******************************************************************************************/
void CCBPropertySheet::Init(LPCTSTR pszFontFaceName, WORD wFontSize)
{
m_pszFontFaceName = pszFontFaceName;
m_wFontSize = wFontSize;
if((m_wFontSize > 0) && (NULL != m_pszFontFaceName))
{
m_psh.pfnCallback = &CCBPropertySheet::PropSheetProc;
m_psh.dwFlags |= PSH_USECALLBACK;
}
}
/*****************************************************************************************
Function: CCBPropertySheet::BuildPropPageArray
Purpose: Overrides CPropertySheet::BuildPropPageArray
******************************************************************************************/
void CCBPropertySheet::BuildPropPageArray()
{
CPropertySheet::BuildPropPageArray();
if((m_wFontSize > 0) && (NULL != m_pszFontFaceName))
{
LPCPROPSHEETPAGE ppsp = m_psh.ppsp;
const int nSize = static_cast<int>(m_pages.GetSize());
for(int nPage = 0; nPage < nSize; nPage++)
{
const DLGTEMPLATE* pResource = ppsp->pResource;
CDialogTemplate dlgTemplate(pResource);
dlgTemplate.SetFont(m_pszFontFaceName, m_wFontSize);
memmove((void*)pResource, dlgTemplate.m_hTemplate, dlgTemplate.m_dwTemplateSize);
(BYTE*&)ppsp += ppsp->dwSize;
}
}
}
在使用过程中主要需要添加这个类,然后你的Sheet类直接继承于CCBPropertySheet类而不是CPropertySheet, 例如:
Sheet类.h
#pragma once
#include "CBPropertySheet.h"
#include "Prop1.h"
#include "Prop2.h"
// CPropertySheetDemo
class CPropertySheetDemo : public CCBPropertySheet
{
DECLARE_DYNAMIC(CPropertySheetDemo)
public:
CPropertySheetDemo(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);
virtual ~CPropertySheetDemo();
// Attributes
private:
CProp1 m_page1;
CProp2 m_page2;
// Implementaion
protected:
DECLARE_MESSAGE_MAP()
};
Sheet类.CPP
// PropertySheetDemo.cpp : implementation file
//
#include "stdafx.h"
#include "LingoTest.h"
#include "PropertySheetDemo.h"
// CPropertySheetDemo
IMPLEMENT_DYNAMIC(CPropertySheetDemo, CCBPropertySheet)
CPropertySheetDemo::CPropertySheetDemo(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
: CCBPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
AddPage(&m_page1);
AddPage(&m_page2);
}
CPropertySheetDemo::~CPropertySheetDemo()
{
}
BEGIN_MESSAGE_MAP(CPropertySheetDemo, CCBPropertySheet)
END_MESSAGE_MAP()
至于Add 至 PropertySheet 的PropertyPage,例如这里的CProp1、CProp2的实现方式就是最基本的PropertyPage的实现方式继承于CPropertyPage。
在对话框中使用 CPropertySheetDemo
// LingoTestDlg.h : header file
//
#pragma once
class CPropertySheetDemo;
// CLingoTestDlg dialog
class CLingoTestDlg : public CDialogEx
{
// Construction
public:
CLingoTestDlg(CWnd* pParent = NULL); // standard constructor
~CLingoTestDlg();
// Dialog Data
enum { IDD = IDD_LINGOTEST_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedButtonTest();
BOOL m_nDefault;
private:
CPropertySheetDemo *m_pps;
};
// LingoTestDlg.cpp : implementation file
//
#include "stdafx.h"
#include "LingoTest.h"
#include "LingoTestDlg.h"
#include "PropertySheetDemo.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CLingoTestDlg dialog
CLingoTestDlg::CLingoTestDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CLingoTestDlg::IDD, pParent)
, m_nDefault(FALSE)
{
m_pps = NULL;
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CLingoTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Radio(pDX, IDC_RADIO_DEFAULT, m_nDefault);
}
BEGIN_MESSAGE_MAP(CLingoTestDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_BUTTON_TEST, &CLingoTestDlg::OnBnClickedButtonTest)
END_MESSAGE_MAP()
// CLingoTestDlg message handlers
BOOL CLingoTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
...
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CLingoTestDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
...
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CLingoTestDlg::OnPaint()
{
...
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CLingoTestDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CLingoTestDlg::OnBnClickedButtonTest()
{
// TODO: Add your control notification handler code here
UpdateData();
CPropertySheetDemo pps(IDS_PROPERTYSHEET_CAPTION, this);
if (0 == m_nDefault)
{
pps.DoModal();
}
else
{
// 创建模式属性页对话框
// pps.DoModal(strFontFaceName, m_nSize);
//创建非模式属性页对话框
if (m_pps == NULL)
{
int m_nSize = 8;
CString strFontFaceName;
strFontFaceName.Format(_T("Microsoft Sans Serif"));
m_pps = new CPropertySheetDemo(IDS_PROPERTYSHEET_CAPTION, this);
m_pps->Create(strFontFaceName, m_nSize, this, WS_SYSMENU | WS_POPUP | WS_CAPTION | DS_MODALFRAME | DS_CONTEXTHELP | WS_VISIBLE);
// m_pps->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
}
else
{
if (m_pps->GetSafeHwnd())
{
if (m_pps->IsWindowVisible())
{
m_pps->ShowWindow(SW_HIDE);
}
else
{
m_pps->ShowWindow(SW_SHOW);
}
}
}
}
}
CLingoTestDlg::~CLingoTestDlg()
{
if(m_pps)
{
if (m_pps->GetSafeHwnd())
{
m_pps->DestroyWindow();
}
delete m_pps;
m_pps = NULL;
}
}
主要在 CLingoTestDlg::OnBnClickedButtonTest()中,原有DoModal()创建指定字体模态属性页OK,但我的程序中需要创建非模态属性页对话框,就得使用非模态的Create的方式了。
最终经过验证,在我的程序中使用这种方式比较OK,没有较大的改动,由此PropertySheet、PropertyPage在非英文版Win10中变大变形的问题基本得以解决,问题应该也比较清楚是对话框字体的原因,不过具体深层次的原因,目前以我的水平是无法深入了解了。
Tips: 另外通过测试发现在150%时,这种方式表现的不如CTabCtrl…
以上则为解决PropertySheet\PropertyPage在Win10上被拉大的情况的相关经历和方式,供需要的童靴参考。
相关链接:
https://download.csdn.net/download/yayun/10503607
感谢资源主提供的Code,虽然可能看不到,但还是聊表谢意 ^ _ ^