Шаг 168.
Microsoft Visual C++ 2010. Язык С/С++.
Компоненты Windows Forms. Компонент CheckedListBox. Пример использования

    На этом шаге мы рассмотрим пример использования этого компонента.

    Приведем пример приложения для домашнего телефонного справочника, построенного с использованием компонента CheckedListBox. Справочник должен содержать номера телефонов и специальный ящик (горячий ящик), в котором будут храниться самые необходимые из всего списка телефоны (чтобы ими можно было быстро воспользоваться). В ящик будут посылаться только те телефоны, которые в списке будут помечаться галочками (т. е. их флажки будут включены). Как только флажок выключается, номер телефона из горячего ящика удаляется. Однако строку из горячего ящика можно удалить и по щелчку мыши на ней.

    При создании приложения возникли проблемы с переводом текста на русский язык и с методами записи в файл строк типа String. Одни методы читают только английский текст, другие читают и русский, но у них имеются недостатки с переходом от типа String к типу char*. Поэтому применение аппарата обычных функций работы с файлами, таких как fopen(), fgets(), fputs (), оказалось довольно затруднительным. Для упрощения проблем были выбраны два простейших метода чтения/записи одной строки типа String (см. по тексту примера). Ввиду того, что справочник имеет довольно мало строк, их можно сцепить в одну длинную, с которой можно работать - записывать и читать. Этим проблема преобразования типов от String к char* была снята.

    Итак, приложение работает следующим образом: как только оно загружается для выполнения (фактически загружается форма), автоматически читаются два текстовых файла, в которых находятся сведения о предыдущей работе приложения (перед тем, как форме появиться на экране, срабатывает событие Load, в обработчик которого помещены операторы загрузки файлов). Содержимым этих файлов заполняется содержимое двух контейнеров: компонента ChekedListBox и компонента ComboBox. Далее приложение работает в обычном режиме: строки можно добавлявлять в оба контейнера и удалять из каждого в отдельности. Как только приложение завершит свою работу (кнопка Выход), содержимое контейнеров сохранится в соответствующих файлах (чтобы оно при выгрузке не исчезало).

    Отсюда возникает проблема первоначального запуска приложения: надо вручную сформировать указанные два файла (их имена указаны в тексте приложения). Можно взять обыкновенный WordPad (Блокнот) и с его помощью создать пустые текстовые файлы в кодировке UTF-8. А далее в приложении вы сами введете те тексты, которые вам нужны.

    Форма приложения приведена на рисунке 1, а текст - в примере ниже. На рисунках 2 и 3 показана работа с приложением.


Рис.1. Форма приложения "Домашний телефонный справочник"

    Текст приложения:

#pragma once

namespace My168_1 {

	using namespace System;
	using namespace System::ComponentModel;
	using namespace System::Collections;
	using namespace System::Windows::Forms;
	using namespace System::Data;
	using namespace System::Drawing;
	using namespace System::IO;  // Добавлено для методов чтения/записи
				     // строки
.   .   .   .   .
	private:
		/// <summary>
		/// Требуется переменная конструктора.
		//--- Функции выгрузки в файл и загрузки из файла ---
		void LoadFromFile (String^ File, CheckedListBox^ lb)
		{
			// Этот метод открывает текстовый файл, читает все его строки 
			// в строку String^ и закрывает файл. 
                        // Поскольку справочник небольшой, 
			// то этот мето можно применять. 
                        // Это же касается и метода записи (см. ниже)
			String ^d, ^b = File::ReadAllText(File); 
			//(надо будет выделять по разделителю "/") 
			lb->Items->Clear();
			
			//Разборка длинной строки на настоящие строки 
			while(b->Length > 0) {
				int i = b->IndexOf("/");  //поиск 1-го вхождения 
                                                          //подстроки в строку 
				if(i == -1) break;
				d = b->Substring(0,i); 
				lb->Items->Add(d); 
				b = b->Substring(i + 1, b->Length - d->Length - 1);
			}
			return;
		}
		// --------------------------------------------
		void LoadFromFile(String ^File, ComboBox ^lb)
		{
			// Этот метод открывает текстовый файл, читает все его 
			// строки в строку String^ и закрывает файл.
			String ^d, ^b = File::ReadAllText(File); 
			//(надо будет вьделять по разделителю "/") 
			lb->Items->Clear();

			//Разборка длинной строки на настоящие строки 
			while(b->Length > 0) {
				int i = b->IndexOf("/");  //поиск 1-го вхождения 
                                                          //подстроки в строку
				d = b->Substring(0, i);
				lb->Items->Add(d);
				b = b->Substring(i + 1, b->Length - d->Length - 1);
			} 
			return;
		}
		// ============================================
		void SaveToFile(String ^File, CheckedListBox ^lb) {
			String ^a, ^b;
			int j = lb->Items->Count;
			File::Delete(File); 
			for(int i=0; i < j; i++) {
				// Чтение строк ChekedListBox в a и 
				// формирование длинной строки в b
				a = lb->Items[i]->ToString();
				b += a->Concat(a,"/");  //добавка разделителя строк 
				// Этот тот метод открывает файл, добавляет к нему 
				// строку типа String^, акрывает файл. Если файл 
				// не существует, он создается.
			} //for
			File::AppendAllText(File, b); 
			return;
		}
		// ---------------------------------------------
		void SaveToFile(String ^File, ComboBox^ lb) {
			String ^a, ^b;
			int j = lb->Items->Count;
			File::Delete(File);

			for(int i = 0; i < j; i++) {
				// Чтение строк ChekedListBox в a и формирование 
				// длинной строки в b 
				a = lb->Items[i]->ToString(); 
				b += a->Concat(a, "/");  //добавка разделителя строк
				// Этот метод открывает файл, добавляет к нему строку 
				// типа String^, закрывает файл. 
                                // Если файл не существует, он создается.
			} //for
			File::AppendAllText(File, b);
			return;
		}
		// ----------------------------------------------------
		/// </summary>
		System::ComponentModel::Container ^components;
.   .   .   .   .
// Кнопка "Добавить в список"
private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
	 String^ r = "                                                          ";

	 // Формирование в строке r данных, введенных в поля ввода 
	 // для телефона и комментария 
	 r=r->Concat(textBox1->Text, r); 
	 r=r->Insert(35, textBox2->Text); 
	 checkedListBox1->Items->Add(r, 0); //состояние = 0 отключен
	 textBox1->Text = ""; 
	 textBox2->Text = "";
	 textBox1->Focus();
 }
// Обработка нажатия клавиши <Enter> при вводе номера абонента
private: System::Void textBox1_KeyDown(System::Object^  sender, 
      System::Windows::Forms::KeyEventArgs^  e) {
	 if (e->KeyCode == Keys::Enter)
		 textBox2->Focus();
 }
// Обработка нажатия клавиши <Enter> при вводе комментария
private: System::Void textBox2_KeyDown(System::Object^  sender, 
      System::Windows::Forms::KeyEventArgs^  e) {
	 if (e->KeyCode == Keys::Enter)
		 button2->Focus();
 }
// Обработка кнопки "Выход"
private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
	 // Когда приложение завершается,
	 // надо сохранить данные ChekedListBox и ComboBox в файлах 
	 SaveToFile("d:\\file1.txt", checkedListBox1); 
	 SaveToFile("d:\\file2.txt", comboBox1); 
	 Close();
 }
// Обработка кнопки "Удалить из списка"
private: System::Void button3_Click(System::Object^  sender, System::EventArgs^  e) {
	 if (checkedListBox1->SelectedIndex == -1) // строку не отметили для удаления
	 { 
		 MessageBox::Show("Отметьте строку для удаления", 
			 "Телефонный справочник", 
			 MessageBoxButtons::OK,
			 MessageBoxIcon::Asterisk); 
		 return;
	 }
	 checkedListBox1->Items->Remove(checkedListBox1->SelectedItem);
 }
// Обработка изменения состояния
private: System::Void checkedListBox1_ItemCheck(System::Object^  sender, 
    System::Windows::Forms::ItemCheckEventArgs^  e) {
	 //Обработка выборки из списка
	 //В зависимости от свойства CheckOnClick состояние флажка меняется
	 //либо от одного щелчка, либо от повторного.
	 //Здесь установлено, что от одного щелчка;
	 //сюда попадаем, когда щелчком мыши выбираем строку из списка
	 String ^str, ^tel, ^str1;
	 int i = this->checkedListBox1->SelectedIndex; 
	 // здесь будет индекс выбранной строки после щелчка на ней
			 
	 str = dynamic_cast <String^>(checkedListBox1->SelectedItem);
	 //перевод из типа Object^ в String^
	 // здесь будет выбранная строка после щелчка на ней
	 tel = str->Substring(0, str->Length); //выделили номер телефона

	 //добавка или удаление номера телефона в (из) ComboBox 
	 // поиск строки в ComboBox: если она найдена, то удаляется, 
	 // если не найдена, то после этого блока она добавляется
	 int k=0, j=comboBox1->Items->Count; 
	 for (int i=0; i < j; i++) {
		 str1 = dynamic_cast <String^>(comboBox1->Items[i]); 
		 if (System::String::Compare(str1, tel) != 0) //строки не сравнились
			 continue; 
		 else {
			 Object^ str2 = dynamic_cast <Object^> (str1); 
			 // Метод Remove() требует типа Object^, поэтому мы 
			 // перевели тип String^ в тип Object^
			 comboBox1->Items->Remove(str2);
			 k = 1;
			 break;
		 }
	 } //for()
	 if(k == 1)  //строку удалили
		 return; 
	 // здесь ситуация, когда строки в ящике нет, поэтому ее надо в
	 // него добавить 
	 if (comboBox1->Items->Count > 10)
		 return; 
	 // если в ящике уже 10 строк, то добавлять не надо (он 
	 // будет содержать не более 10-ти строк
	 comboBox1->Items->Add(tel);
 }
// Обработка загрузки формы
private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
	 // Когда форма загружается, надо загрузить в 
	 // CheckedListBox и ComboBox их строки, сохраненные при выгрузке
	 LoadFromFile("d:\\file1.txt", checkedListBox1); 
	 LoadFromFile("d:\\file2.txt", comboBox1);
 }
// Обработка сворачивания CombоBox
private: System::Void comboBox1_DropDownClosed(System::Object^  sender, 
   System::EventArgs^  e) {
	 //Удаление строки из ComboBox по щелчку на ней 
	 int i = comboBox1->SelectedIndex;
	 comboBox1->Items->Remove(comboBox1->SelectedItem); 
 }
Архив проекта можно взять здесь.



Рис.2-3. Результат работы приложения

    Пояснение:

    Чтобы добавить новую строку в список, надо активизировать щелчком мыши поле ввода номера телефона (оно расположено над кнопкой Добавить в список). Затем нужно ввести в это поле текст и нажать клавишу Enter.

    Фокус ввода перейдет к полю над кнопкой Удалить из списка (туда вводится второй текст (например, комментарий к первому введенному тексту) и снова нажимается клавиша Enter. Фокус ввода перейдет к кнопке Добавить в список, после чего надо снова нажать клавишу Enter. Обе введенные строки перенесутся в поле CheckedListBox.

    Чтобы удалить строку из CheckedListBox, ее надо пометить, щелкнув на ней мышью, а затем нажать на кнопку Удалить из списка.

    Чтобы удалить строку из ComboBox, надо открыть выпадающий список и щелкнуть на строке, которую требуется удалить.

    На следующем шаге мы рассмотрим дополнение к вводу/выводу файлов.




Предыдущий шаг Содержание Следующий шаг