БлогNot. Visual C++: размещаем компоненты с помощью TableLayoutPanel

Visual C++: размещаем компоненты с помощью TableLayoutPanel

В основном, этот пример был мне нужен для демонстрации того, как выравнивать компоненты по ячейкам таблицы, не мучаясь с их "ручным" позиционированием. А приложение-калькулятор - всегда хороший пример программы с "нестандартным" размещением интерфейсных элементов.

Предположим, наш калькулятор должен выглядеть так:

Внешний вид приложения
Внешний вид приложения

Видно, что все компоненты уместились в сетку таблицы 5 на 5 ячеек, при этом некоторые компоненты занимают более одной ячейки. Мы разместим и растянем компоненты в стандартном контейнере TableLayoutPanel, но сначала подготовим пустую форму и выставим ей некоторые нужные свойства:

MaximumSize: 600; 600
MinimumSize: 200; 200
Size: 240; 240
StartPosition: CenterScreen
Text: Калькулятор

Как видно, мы не хотим делать окно не-масштабируемым, но некие на глазок выбранные пределы изменения его размеров всё же задаём.

Теперь добавляем на форму контейнер TableLayoutPanel из списка "Контейнеры" Панели Элементов, ставим ему свойство Dock в значение Fill и с помощью встроенного инструмента "Задачи" переходим к правке строк и столбцов:

Правка строк и столбцов TableLayoutPanel
Правка строк и столбцов TableLayoutPanel

Сделаем 5 столбцов с шириной по 20% каждый и 5 строк, у которых высота задана как на картинке:

Высота строк в TableLayoutPanel
Высота строк в TableLayoutPanel

Заодно прочитаем, что написано на картинке о растягивании компонент, лежащих в ячейках таблицы, на несколько строк или столбцов с помощью свойств RowSpan и ColumnSpan.

В левую верхнюю ячейку перетаскиваем стандартный TextBox, ставим ему свойства ColumnSpan = 5 и Dock = Fill. Также поставим TextAlign = Right, чтобы результаты вычислений выравнивались по правому краю поля.

Перетаскиваем в нужные ячейки (см. первый рисунок) кнопки button1 ... button9, подписывая их соответствующими цифрами 1 ... 9 (в свойстве Text) и ставя каждой Dock = Fill. Кнопка button10 будет соответствовать цифре "ноль", с ней поступим так же, только не забудем выставить ColumnSpan = 3. Кнопка button11 будеми иметь подпись "=" и значение ColumnSpan = 2, таким образом, они с нулём займут всю нижнюю строку. Кнопки "+", "-", "*" и "/" у меня получились как button12 ... button15, их свойства - такие же, как у кнопок с цифрами 1 ... 9. Наконец, кнопка стирания button16, подписанная C, также имеет свойство ColumnSpan = 2.

Интерфейс, в общем, готов, осталось написать код. В приватной секции класса (например, сразу ниже директивы #pragma endregion), предусмотрим следующие переменные:

// Внешние переменные, видимые из всех методов класса Form1:
String^ Znak;      // знак арифметической операции
bool StartOfInput; // ожидание ввода 
double Number1, Number2; // Первый и второй операнды расчёта

Создадим обработчик события Load формы и напишем в нём следующий код:

StartOfInput = true;
Znak = nullptr;
 // Один обработчик для всех кнопок с цифрами
 button1->Click += gcnew EventHandler(this, &Form1::AddDigit);
 button2->Click += gcnew EventHandler(this, &Form1::AddDigit);
 button3->Click += gcnew EventHandler(this, &Form1::AddDigit);
 button4->Click += gcnew EventHandler(this, &Form1::AddDigit);
 button5->Click += gcnew EventHandler(this, &Form1::AddDigit);
 button6->Click += gcnew EventHandler(this, &Form1::AddDigit);
 button7->Click += gcnew EventHandler(this, &Form1::AddDigit);
 button8->Click += gcnew EventHandler(this, &Form1::AddDigit);
 button9->Click += gcnew EventHandler(this, &Form1::AddDigit);
 button10->Click += gcnew EventHandler(this, &Form1::AddDigit);
 //Второй - для операций
 button12->Click += gcnew EventHandler(this, &Form1::Operation);
 button13->Click += gcnew EventHandler(this, &Form1::Operation);
 button14->Click += gcnew EventHandler(this, &Form1::Operation);
 button15->Click += gcnew EventHandler(this, &Form1::Operation);
 //Отдельный "IsEqual"
 button11->Click += gcnew EventHandler(this, &Form1::IsEqual);
 //Отдельный "ClearMe"
 button16->Click += gcnew EventHandler(this, &Form1::ClearMe);

Как видно, отдельный обработчик каждой кнопке с цифрой не нужен, а показанный код служит примером того, как назначать обработчики событий программно (точно такие же примеры можно увидеть, разобрав автогенерируемый при работе в режиме конструктора код).

Под методом Form1_Load можно реализовать коды обработчиков, в них полезно посмотреть, как получить текст, отображаемый на кнопке, это нужно, чтобы обработчик мог различать, от какой именно кнопки пришло событие.

private: System::Void AddDigit(System::Object^ sender, System::EventArgs^ e) { 
 // Получить текст, отображаемый на кнопке
 Button^ MyButton = (Button^)sender;
 String^ Digit = MyButton->Text;
 if (StartOfInput == true) {  // Ввод первой цифры числа:
  textBox1->Text = Digit;
  StartOfInput = false; 
  return;
 }
 // Иначе "сцепляем" полученные цифры в новое число:
 if (StartOfInput == false)  textBox1->Text = textBox1->Text + Digit;
}

private: System::Void Operation(System::Object^ sender, System::EventArgs^  e) {
 Number1 = Double::Parse(textBox1->Text);
 // Получить текст, отображаемый на кнопке можно таким образом:
 Button^ MyButton = (Button^)sender;
 Znak = MyButton->Text;
 StartOfInput = true; // ожидаем ввод нового числа
}

private: System::Void IsEqual(System::Object^ sender, System::EventArgs^ e) {
 // Обработка нажатия клавиши "IsEqual"
 double Result = 0;
 Number2 = Double::Parse(textBox1->Text);
 if (Znak == "+") Result = Number1 + Number2;
 if (Znak == "-") Result = Number1 - Number2;
 if (Znak == "*") Result = Number1 * Number2;
 if (Znak == "/") Result = Number1 / Number2;
 Znak = nullptr;
 // Отображаем Result в текстовом поле:
 textBox1->Text = Result.ToString();
 Number1 = Result; StartOfInput = true;
}

private: System::Void ClearMe(System::Object^ sender, System::EventArgs^ e) {
 textBox1->Text = "0"; 
 Znak = nullptr; 
 StartOfInput = true;
}

Разумеется, пример можно и нужно улучшать, например, добавив обработку событий от клавиатуры для textBox1.

Ну а вопрос, заданный человеком, посмотревшим этот код - "как сделать, чтобы на при изменении размеров окна увеличивался шрифт на всех кнопках?", пожалуй лучше будет рассмотреть в отдельной заметке - потому что готового свойства "масштабировать всё" в .NET нету, а перебор всех компонентов в цикле - вещь неочевидная, особенно если компоненты могут содержать другие компоненты, а мы пишем на C++/CLI.

 Этот проект в архиве .zip, проверен в Visual C++ 2010 Express (15 Кб)

11.09.2016, 18:07 [7007 просмотров]


теги: учебное программирование c++/cli

К этой статье пока нет комментариев, Ваш будет первым