Характерно, что эта функция будет автоматически вызываться не только в те моменты времени, когда пользователь переводит фокус с одной строки списка на другую, но и тогда, когда программным способом изменяется свойство list.SelectedIndex, а это происходит достаточно часто. Чтобы на практике увидеть взаимодействие элементов, полезно расставить точки останова в ключевых функциях и поработать со списком в режиме отладки. Используя способ, аналогичный тому, что вы применили для ввода реакции на событие SelectedIndexChanged, введите реакцию на событие KeyDown.
private void list_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete)
Delete();
}
Код настолько естественный, что не требует комментария. Обратите внимание на смену делегатного типа (KeyEventArgs).
Для восстановления одной, нечаянно удаленной записи, удобно использовать ту же технику, которая была использована при отслеживании перемещений по списку. Создайте обработчик нажатия кнопки Отмена и введите в него свой код.
private void bUndo_Click(object sender, EventArgs e)
{
if (sUndo != null) // Если есть, что восстанавливать
{
string[] ss = sUndo.Split(new Char[]{';',':'},6); // Расшифровыаем запись
//==== Ваш код
}
}
Учитывая специфику наших данных (текст), выбираем текстовый формат файла, то есть потоковый вариант ввода-вывода в файл с расширением txt. Прежде чем начать чтение или запись, надо открыть файл. Эта операция сопряжена с некоторым риском. Поэтому это действие следует выполнить в отдельной функции. Пусть ее параметр задает режим открытия (чтение или запись), а возвращает она строку, которая содержит файловый путь.
private string FileDlg (bool bOpen)
{
FileDialog dlg = bOpen ? // Создаем объект того или иного класса
(FileDialog) new OpenFileDialog() :
(FileDialog) new SaveFileDialog();
dlg.InitialDirectory = Environment.CurrentDirectory; // Текущая директория
dlg.Filter = "Text files(*.txt)|*.txt|All files (*.*)|*.*" ;
dlg.Title = bOpen ? "Найдите файл с данными" : "Запишите в файл";
dlg.FileName = fileName;
dlg.RestoreDirectory = true; // Перед закрытием восстановим текущую директорию
return dlg.ShowDialog() == DialogResult.OK ? dlg.FileName : null;
}
Методом двойного щелчка над кнопками bSave и bOpen введите в класс реакции на их нажатие.
private void bSave_Click(object sender, EventArgs e)
{
Save (FileDlg(false));// Запись в файл
}
private void bOpen_Click(object sender, EventArgs e)
{
Open (FileDlg(true)); // Чтение из файла
}
Функцию Save следует разработать так, чтобы она построчно вывела содержимое списка в файл, предварительно пометив его. Для этой цели используем строку текста version.
private void Save (string file)
{
if (file == null)
return;
StreamWriter sr = new StreamWriter (file); // Открываем файл для записи
sr.Write (version + "\n"); // Выводим секретную строку
for (int i=0; i<list.Items.Count; i++) // Построчно выводим элементы списка
sr.Write(list.Items[i].ToString() + "\n");
// Закройте файл, измените bModified, fileName и заголовок окна (на имя файла)
}
Метод Open должен открыть файл для чтения, считать данные и заполнить список. Перед тем, как начать считывание записей, произведите анализ строки version. Большую часть тела функции должны занимать коды обработки возможных сбоев. Часть кодов функции разработайте самостоятельно.
private void Open (string file)
{
if (file == null)
return;
StreamReader sr = null; // Создание потокового читателя
try // Попытка открыть файл
{
//==== Ваш код
}
catch (Exception e)
{
//==== Ваш код
}
string line = sr.ReadLine(); // Первая строка идентифицирует наш файл
if (line != version)
{
MessageBox.Show("Попытка прочесть не наш файл: " + file, "Формат");
sr.Close();
return;
}
list.Items.Clear(); // Уничтожаем текущий список, так как читаем новый
list.BeginUpdate(); // Тормозим перерисовку ListBox’а на время чтения
//==== Построчное считывание данных
//==== Ваш код
list.EndUpdate(); // Разрешаем перерисовку ListBox’а
//=== Закройте файл и зпомните путь к нему. Измените заголовок и т.д.
//==== Ваш код
}
При закрытии приложения следует проверить необходимость сохранения данных (флаг bModified) и сделать это (если нужно). По умолчанию закрытие инициируется кнопкой системного меню (крестик). Если мы хотим предупредить пользователя о необходимости записать данные, то следует ввести в класс Form1 реакцию на событие Closing. Сделайте это и введите в заготовку функции обработки события следующий код.
private void Form1_Closing (object sender, CancelEventArgs e)
{
PromptSave(); // Записать данные, если необходимо
}
Ниже приведена заготовка для тела функции PromptSave.
private void PromptSave()
{
if (bModified) // Если файл изменен, предлагаем сохранить данные
{
//=== Ваш код
}
}
Так как, кроме кнопки закрытия окна (системного меню), на форме есть еще одна кнопка (Выход), которая тоже должна завершить приложение, то в класс надо добавить обработчик ее нажатия и вставить в него вызов метода Close, унаследованного нашей формой от класса Form. По умолчанию вызов делается для объекта, на который ссылается this, то есть для формы главного окна. Это действие порождает событие Closing и управление будет передано в уже существующую функцию Form1_Closing, которая вызовет PromptSave. Другим стандартным способом закрытия приложения является вызов статического метода Exit класса Application. В этом случае пришлось бы предварительно вызвать функцию PromptSave.
Возвращаясь к ошибке в алгоритме вставки в список новых данных, приведу, наверное, самую короткую версию заплаты, которую предложил Кирилл Жалдыбин — наш студент. Сравните ее со своей. Если ваша короче, поделитесь, если нет, то вставьте эту. Место вставки вычислите сами.
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.