Приложение-калькулятор с выравниванием компонент по ячейкам таблицы



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

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



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

Видно, что все компоненты уместились в сетку таблицы 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.
Динамическая смена размеров шрифта на всех контролах
На примере рассмотренного выше калькулятора, аналогично можно работать и с другими свойствами.
//1. Прописали в классе формы текущий размер шрифта и переменную класса "Шрифт"

private: Single currentSize;

private: System::Drawing::Font ^Font;
//2. Предусмотрели обработчик события SizeChanged формы

private: System::Void Form1_SizeChanged(System::Object^ sender,

System::EventArgs^ e) {

int clientSize = Math::Min

(this->ClientSize.Width,this->ClientSize.Height);

//Берём меньший из размеров "ширина" и "высота" клиентской части окна

currentSize = Math::Max (8.,Math::Round(clientSize*4./100.,2));

//Размер шрифта сделаем 4% от клиентских размеров окна, но не менее 8 пт

Font = gcnew System::Drawing::Font( //Инициализируем шрифт с нужным размером

button1->Font->Name, currentSize, button1->Font->Style,

button1->Font->Unit);

iterateControls (this);

//Рекурсивно обходим все нужные компоненты, применяя шрифт

}
//3. Разработать и добавить в класс формы метод рекурсивного обхода всех нужных компонент. Так как компоненты могут содержать другие компоненты, обойти их все можно только рекурсивно. Вот такой метод подойдёт в нашем случае:

private: System::Void iterateControls (System::Windows::Forms::Control ^control) {

if (control->GetType()->ToString() ==

(String ^)"System.Windows.Forms.Button")

((System::Windows::Forms::Button ^)control)->Font=Font;

else if (control->GetType()->ToString() ==

(String ^)"System.Windows.Forms.TextBox")

((System::Windows::Forms::TextBox ^)control)->Font=Font;

//Меняем фонт для нужных типов контролов

System::Windows::Forms::Control ^subcontrol; //Вложенный контрол

for each (subcontrol in control->Controls)



iterateControls (subcontrol); //Проверить вложенные контролы рекурсивно

}

Поделитесь с Вашими друзьями:


База данных защищена авторским правом ©nethash.ru 2017
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал