Реклама
www.adm96.ru - компьютер сервис через интернет в Екатеринбурге.
Спонсорские ссылки
-
Статья
Версия для печати
Обсудить на форуме
Масштабирование элементов формы (CFormView) или диалога (CDialog) в зависимости от разрешения экрана



Нередко, а практически всегда, получается так, что элементы управления в формах программы размещаются программистом при одном разрешении экрана, а запускается программа при различных разрешениях. Если форма создана при разрешении 800x600, а показывается при 1152x864, то пользователь начинает заниматься пиксель-хантингом, и если он не заядлый квестер, это его злит. Кроме того, в режиме maximized (особенно в нём) все элементы оказываются сдвинутыми влево и вверх, образуя внизу справа неизящную пустоту. В этой статье я предложу способ, с помощью которого в объектах с классами, производными от классов MFC CFormView и CDialog, реализуется масштабирование элементов. При этом, при любом разрешении (на одном и том же мониторе, естественно) элементы визуально остаются примерно одинакового размера. Однако внизу формы может всё же остаться немного пустого места, но это нужно решать уже для каждой локальной задачи, например, смещением некоторых элементов по вертикали. Среда программирования, в которой написаны примеры,  Microsoft VisualC++6.

Итак, создаём некий проект с именем Class с View, производным от CFormView. Выбираем:
  • MFC AppWizard(exe)
  • Single document
  • в Advanced устанавливаем свойство окна Maximized
  • для класса View выбираем родительский класс CFormView
  • сохраняем проект.

В редакторе ресурсов поместите на форму четыре любых элемента, например, кнопки, и задайте их идентификаторы. В этом примере это IDC_1, IDC_2, IDC_3, IDC_4. Придайте элементам произвольные размеры. Теперь нажмите ctrl+F5 для компиляции и запуска программы. Вы видите исходные визуальные размеры такими, какие вы и хотели получить при проектировании формы. Но теперь поменяйте разрешение экрана, и созданная вами идиллия нарушится. Закройте вашу программу и вернитесь в VC6. В окне Workspase, во вкладке FileView выберите и откройте для редактирования файлы ClassView.h и ClassView.cpp. В заголовочном файле ClassView.h, в самом начале описания класса, добавьте объявление необходимых переменных, констант, структуры и прототипа процедуры масштабирования (выделено синим цветом):
Код:
//класс вью
class CClassView : public CFormView
{
//переменные и константы для масштабирования окна
private:
//количество масштабируемых элементов
#define NofScaledID 4

//базовое разрешение, т.е. при котором форма создавалась
#define BaseScreenX (long)800
#define BaseScreenY (long)600

//структура для описания элемента
struct CElem
{
int ID;
WINDOWPLACEMENT pm800x600;
};

//массив элементов формы для масштабирования
CElem Elems[NofScaledID];

//последние значения разрешения
long LastScreenX,LastScreenY;

//факторы масштабирования
float XF,YF;

//флаг первого выполнения функции OnPaint()
bool theThirstOnPaint;

//процедура масштабирования элементов
void ScaleElements();
...
...
};

Обратите внимание на строки:
Код:
//количество масштабируемых элементов
#define NofScaledID 4

//базовое разрешение, т.е. при котором форма создавалась
#define BaseScreenX (long)800
#define BaseScreenY (long)600

В файле реализации ClassView.cpp, добавьте в самый конец стандартного конструктора инициализацию:
Код:
//конструктор
CClassView::CClassView():CFormView(CClassView::IDD)
{
...
...
//инициализация переменных и флагов
LastScreenX=0;
LastScreenY=0;
theThirstOnPaint =true;
}

В этом же файле добавьте код процедуры масштабирования:
Код:
//процедура масштабирования элементов
void CClassView::ScaleElements()
{
int i;
CWnd *Item;
WINDOWPLACEMENT pm;

      //изменение базового размера элементов с помощью фактора масштабирования
for(i=0;i<NofScaledID;i++)
{
       //достаём базовое положение i-го элемента
       Item=GetDlgItem(Elems[i].ID);
       Item->GetWindowPlacement(&pm);

               //рассчитываем новое положение i-го элемента
       pm.rcNormalPosition.left=Elems[i].pm800x600.rcNormalPosition.left*XF;
       pm.rcNormalPosition.right=Elems[i].pm800x600.rcNormalPosition.right*XF;
       pm.rcNormalPosition.top=Elems[i].pm800x600.rcNormalPosition.top*YF;
       pm.rcNormalPosition.bottom=Elems[i].pm800x600.rcNormalPosition.bottom*YF;

//(СМ. ПОЯСНЕНИЯ В ТЕКСТЕ)
//если элемент - CComboBox, то корректируем bottom
if(Elems[i].ID==IDC_...)
{pm.rcNormalPosition.bottom*=4;}

//устанавливаем новые размеры элемента
Item->SetWindowPlacement(&pm);
}
}

Красным цветом выделены строки для дополнительной обработки элементов CComboBox (выпадающий список). Дело в том, что если параметр bottom изменить обычным образом, то длина выпадающей части списка укорачивается (если честно - не знаю, почему). Локально я решил проблему так: умножил на 4 параметр bottom (4 - подобрано экспериментально) и список стал выпадать полностью...

Здесь же, в файле ClassView.cpp, добавляем обработчик сообщения WM_PAINT (cltr+W, вкладка MessageMaps, в окне Object IDs выбираем CClassView, в окне Messages - WM_PAINT, затем кнопка Edit Code):
Код:
//обработчик сообщения WM_PAINT
void CClassView::OnPaint()
{
CPaintDC dc(this); // device context for painting


if(theThirstOnPaint)
{//первый вызов функции OnPaint()
theThirstOnPaint =false;
int i=0;
//инициализируем ID элементов в массиве
Elems[i++].ID=IDC_1;
Elems[i++].ID=IDC_2;
Elems[i++].ID=IDC_3;
Elems[i++].ID=IDC_4;
//диагностика выхода за количество элементов
#if   i != NofScaledID
#error
#endif

//инициализируем базовые размеры элементов
CWnd *Item;
for(i=0;i<NofScaledID;i++)
{
Item=GetDlgItem(Elems[i].ID);
Item->GetWindowPlacement(&(Elems[i].pm800x600));
}
}

//определяем, изменилось ли разрешение
CRect deskRect;
GetDesktopWindow()->GetWindowRect(deskRect);
if( deskRect.Size().cx!=LastScreenX || deskRect.Size().cy!=LastScreenY)
{//изменилось
//определяем факторы масштабирования
XF=((float)(LastScreenX=deskRect.Size().cx))/BaseScreenX;
YF=((float)(LastScreenY=deskRect.Size().cy))/BaseScreenY;
//масштабируем
ScaleElements();
}

}

Вот и всё.

Если ваша программа называется не Class, замените Class на имя своего проекта в следующих строках (замечание  если имя проекта начинается с цифры, перед ним добавится My, обратите внимание, что написано у других процедур, созданных мастером):
Код:
class CClassView : public CFormView
CClassView::CClassView():CFormView(CClassView::IDD)
void CClassView::ScaleElements()
void CClassView::OnPaint()

Для диалогов делается всё то же самое, только вместо файла ClassView.h используется Class.h, а вместо ClassView.cpp - Class.cpp. Заметьте, что изменяя параметры left, right, top и bottom, можно также и просто переместить элемент, не меняя его размеров.

Для изменения масштаба содержимого элементов, используйте переменные:
Код:
//факторы масштабирования
float XF,YF;

Если исходный базовый размер умножить на соответствующий коэффициент, то размер при текущем разрешении экрана масштабируется до ВИЗУАЛЬНОЙ величины базового размера при базовом разрешении.

Удачи и успехов.

Алексей1153
Версия для печати
Обсудить на форуме