БлогNot. Делаем ListBox с редактированием элементов

Делаем ListBox с редактированием элементов

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

На форму добавлен TableLayoutPanel из 2 столбцов и 1 строки, относительная ширина столбцов равна 75% и 25%, выравнивание Dock = Fill.

В левую ячейку поместили список ListBox, а в правую - панель для кнопок Panel, также поставив им свойство Dock=Fill.

Кроме того, у ListBox установлено свойство ScrollAlwaysVisible=True (полоса прокрутки).

На панель поместим 3 кнопки Button с именами Name = addButton, deleteButton, editButton, у всех них установлено Dock = Top.

Вот вид формы в конструкторе:

вид формы приложения
вид формы приложения

Нам понадобится пара новых свойств класса формы, как обычно, опишем их под директивой #pragma endregion файла MyForm.h:

private: 
 int Cnt, //номер очередной строки
  EditIndex; //номер редактируемого элемента
 TextBox ^textBox1; //текстовое поле

На загрузку формы программно создадим и спрячем TextBox, пока не позиционируя его:

private: System::Void MyForm_Load(System::Object^  sender, System::EventArgs^  e) {
 Cnt = 0;
 textBox1 = gcnew TextBox();
 textBox1->Location = System::Drawing::Point(0, 0);
 textBox1->Size = System::Drawing::Size(0, 0);
 textBox1->Parent = this;
 textBox1->Hide();
 textBox1->Text = "";
 textBox1->BackColor = Color::Beige;
 textBox1->ForeColor = Color::Blue;
 textBox1->Font = gcnew System::Drawing::Font("Microsoft Sans Serif", 10, 
  FontStyle::Regular | FontStyle::Underline, GraphicsUnit::Pixel);
 textBox1->BorderStyle = BorderStyle::FixedSingle;
 textBox1->KeyPress += gcnew System::Windows::Forms::KeyPressEventHandler(this, &MyForm::textBox_KeyPress);
 textBox1->LostFocus += gcnew System::EventHandler(this, &MyForm::textBox_LostFocus);
 this->Controls->Add(textBox1);
}

Из кода видно, как "украсить" поле ввода.

Код также программно добавил созданному текстовому полю обработчики событий "нажатие клавиши" и "потеря фокуса", оба метода просто будут переписывать значение из поля ввода в список а потом вызывать ещё не написанный метод hideTextEditor, который передаст управление из поля ввода снова в список.

private: System::Void textBox_KeyPress(System::Object ^sender, System::Windows::Forms::KeyPressEventArgs^  e) {
 if (e->KeyChar == 13) {
  listBox1->Items[EditIndex] = textBox1->Text;
  hideTextEditor();
 }
}

private: System::Void textBox_LostFocus(System::Object ^sender, System::EventArgs ^e) {
 listBox1->Items[EditIndex] = textBox1->Text;
 hideTextEditor();
}

Самому списку можно добавить обработчики событий через конструктор форм, мы будем отслеживать двойной щелчок с списке, а также нажатие клавиш Enter или F2, все эти действия будут вызывать ещё не написанный метод CreateEditBox, ответственный за переход к редактированию элемента.

private: System::Void listBox1_DoubleClick(System::Object^  sender, System::EventArgs^  e) {
 CreateEditBox(sender);
}

private: System::Void listBox1_KeyPress(System::Object^  sender, System::Windows::Forms::KeyPressEventArgs^  e) {
 if (e->KeyChar == 13) CreateEditBox(sender);
}

private: System::Void listBox1_KeyDown(System::Object^  sender, System::Windows::Forms::KeyEventArgs^  e) {
 if (e->KeyData == Keys::F2) CreateEditBox(sender);
}

Кнопки "Добавить", "Удалить" и "Править" выполнят несложную работу по управлению элементами списка, причём, последняя также лишь вызывает метод CreateEditBox.

private: System::Void addButton_Click(System::Object^  sender, System::EventArgs^  e) {
 String ^Str = gcnew String("Строка " + ++Cnt);
 listBox1->Items->Add(Str);
}
	
private: System::Void deleteButton_Click(System::Object^  sender, System::EventArgs^  e) {
 int n = listBox1->SelectedIndex;
 if (n == -1) {
  MessageBox::Show("Сначала выберите строку", "Ошибка", MessageBoxButtons::OK);
  return;
 }
 listBox1->Items->RemoveAt(n);
}

private: System::Void editButton_Click(System::Object^  sender, System::EventArgs^  e) {
 CreateEditBox(listBox1);
}

Наконец, нам остаётся написать собственные методы класса, которые ранее мы неоднократно вызывали.

private: System::Void CreateEditBox (System::Object ^sender) {
 ListBox ^listBox1 = (ListBox ^)sender;
 int n = listBox1->SelectedIndex;
 if (n < 0) {
  MessageBox::Show("Сначала выберите строку", "Ошибка", MessageBoxButtons::OK);
  return;
 }
 Rectangle ^r = listBox1->GetItemRectangle(n);
 String ^itemText = (String ^)listBox1->Items[n];
 int delta = 2; //Смещение от позиции элемента списка
 textBox1->Location = System::Drawing::Point(r->X + delta, r->Y + delta);
 textBox1->Size = System::Drawing::Size(r->Width - 10, r->Height - delta);
 showTextEditor();
 textBox1->Text = itemText;
 textBox1->Focus();
 textBox1->SelectAll();
 EditIndex = n;
}

private: System::Void showTextEditor(System::Void) {
 listBox1->SendToBack();
 textBox1->BringToFront();
 textBox1->Show();
}

private: System::Void hideTextEditor() {
 textBox1->Hide();
 textBox1->SendToBack();
 listBox1->BringToFront();
}

Текстовое поле появляется прямо поверх списка, это достигается сменой переднего/заднего планов списка и текстового поля.

 Скачать этот пример в архиве .zip, внутри - папка с решением Visual Studio 2015 (7 Кб)

Ниже показана немного "улучшенная" версия приложения с таким же интерфейсом, в частности, в ней всё можно делать с клавиатуры нажатиями Enter, Insert и Delete. Это вся часть файла MyForm.h (или Form.h) после директивы #pragma endregion

#pragma endregion
	private:
  int Cnt; //Счетчик строк
  int editIndex;  //Индекс последнего выбранного элемента
  TextBox ^textBox1; //Поле для редактирования выбранного элемента

private: System::Void MyForm_Load(System::Object^  sender, System::EventArgs^  e) {
 //Загрузка формы
 Cnt = 0;
 editIndex = -1;
 //Создаём и настраиваем TextBox для редактирования элементов списка
 textBox1 = gcnew TextBox();
 textBox1->Parent = this;
 textBox1->Text = "";
 textBox1->BackColor = Color::Yellow;
 textBox1->KeyPress +=
  gcnew KeyPressEventHandler(this, &MyForm::textBox1_KeyPress);
 textBox1->LostFocus +=
  gcnew EventHandler(this, &MyForm::textBox1_LostFocus);
 hideTextEditor();
 this->Controls->Add(textBox1);
}

private: System::Void addButton_Click(System::Object^  sender, System::EventArgs^  e) {
 //Нажатие кнопки "Добавить"
 String ^str = gcnew String(L"Строка "+ ++Cnt);
 listBox1->Items->Add(str);
 editIndex = listBox1->Items->Count - 1;
 listBox1->SelectedIndex = editIndex;
}

private: System::Void deleteButton_Click(System::Object^  sender, System::EventArgs^  e) {
 //Нажатие кнопки "Удалить"
 int n = listBox1->SelectedIndex;
 if (n < 0) {
  MessageBox::Show(L"Сначала выберите строку");
  return;
 }
 editIndex = n;
 listBox1->Items->RemoveAt(n);
 int cnt = listBox1->Items->Count;
 if (cnt < 1) return;
 if (editIndex >= cnt) editIndex = cnt - 1;
 listBox1->SelectedIndex = editIndex;
}

private: System::Void textBox1_KeyPress(System::Object^  sender, KeyPressEventArgs^ e) {
 //Обработчик нажатия клавиш при редактировании строки списка
 if (e->KeyChar == (int)Keys::Enter) {
  listBox1->Items[editIndex] = textBox1->Text;
  hideTextEditor();
 }
}

private: System::Void textBox1_LostFocus(System::Object^  sender, EventArgs^ e) {
 //Обработчик смены фокуса при редактировании строки списка
 listBox1->Items[editIndex] = textBox1->Text;
 hideTextEditor();
}

private: System::Void editButton_Click(System::Object^  sender, System::EventArgs^  e) {
 //Нажатие кнопки "Править"
 createActiveTextBox(listBox1);
}

private: System::Void createActiveTextBox(System::Object^  sender) {
 //Метод для позиционирования поля редактирования и его показа
 ListBox ^list = (ListBox ^)sender;
 int n = list->SelectedIndex;
 if (n < 0) {
  MessageBox::Show(L"Сначала выберите строку");
  return;
 }
 Rectangle ^R = list->GetItemRectangle(n); //Место выбранного элемента на экране
 textBox1->Location = Point(R->X,R->Y);
 textBox1->Size = System::Drawing::Size(R->Width,R->Height);
  //Можно попробовать позиционировать точнее, применяя list->Margin
 textBox1->Text = (String ^)list->Items[n];
 editIndex = n;
 showTextEditor();
}
private: System::Void showTextEditor() {
 //Метод "активировать редактор строки списка"
 listBox1->SendToBack();
 textBox1->BringToFront();
 textBox1->Show();
 textBox1->SelectAll();
 textBox1->Focus();
}
private: System::Void hideTextEditor() {
 //Метод "убрать редактор строки списка"
 textBox1->Hide();
 textBox1->SendToBack();
 listBox1->BringToFront(); 
 if (editIndex >= 0 && editIndex < listBox1->Items->Count)
  listBox1->SelectedIndex = editIndex;
}

private: System::Void listBox1_DoubleClick(System::Object^  sender, System::EventArgs^  e) {
 //Редактор также можно вызвать двойным щелчком по элементу списка
 createActiveTextBox(listBox1);
}
private: System::Void listBox1_KeyPress(System::Object^  sender, System::Windows::Forms::KeyPressEventArgs^  e) {
 //или нажатием клавиш. При этом:
 //KeyPress "ловит" только ASCII-коды клавиш
 if (e->KeyChar == (unsigned int)Keys::Enter) {
  createActiveTextBox(listBox1);
 }
}
private: System::Void listBox1_KeyDown(System::Object^  sender, System::Windows::Forms::KeyEventArgs^  e) {
 //А в KeyDown можно отслеживать и другие клавиши
 switch (e->KeyCode) {
  case Keys::F2: createActiveTextBox(listBox1); break;
  case Keys::Delete: this->deleteButton_Click(sender, e); break;
  case Keys::Insert: this->addButton_Click(sender, e); break;
 }
}
//Ниже закрывающие скобки класса и namespace

08.10.2017, 16:30 [5403 просмотра]


теги: программирование список c++/cli

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