Здравствуйте! Хочу показать работу (обработка базы данных предприятий) ,в которой не знаю как запрограммировать две кнопки: "Удалить" предприятие и "Изменить" данные о предприятии. Помогите, пожалуйста. Кнопку "Удалить" попробовал обработать, но получается какая-то ахинея.
Кнопку "Удалить" попробовал обработать, но получается какая-то ахинея.
Во-первых, не надо удалять ничего из StringGrid-а, там не хранятся данные. Удалять надо выбранную строку из ListBox-а, с одновременным сдвигом всего, что находится в массиве records ближе к началу начиная с той же позиции (Lb1.ItemIndex + 1)... Если у тебя множественный выбор в ListBox-е отключен, то проще всего это сделать так:
procedure TForm1.SpeedButton3Click(Sender: TObject); // удаление строки begin if lb1.Items.Count = 2 then exit; // Не удаляем, если осталось 2 записи
// Иначе переносим все, что располагалось правее выбранной записи в массиве records Move(records[lb1.ItemIndex + 2], records[lb1.ItemIndex + 1], sizeof(TMyRec) * (200 - (lb1.ItemIndex + 1))); // и удаляем саму строку из ListBox-а. Можно, конечно, сделать // и lb1.Items.Delete(lb1.ItemIndex), но зачем писАть больше? lb1.DeleteSelected; end;
число 200 что означает? И ещё; когда создаю, например, десять предприятий, затем половину из них поочереди удаляю и закрываю ехешник, все вроде бы нормально. Но потом когда его снова запускаю, опять появляются эти десять предприятий. Т.е. в базе данных мои действия почему-то не сохраняются! В чем тут может быть загвоздка?
records: array [1..200] of TMyRec; //массив данных, подгружаемых из файла
Задал бы его именованной константой - вопроса бы не было... Но раз задаешь "магическим числом" - то вот тебе и непонимание того, что происходит.
Цитата
И ещё; когда создаю, например, десять предприятий, затем половину из них поочереди удаляю и закрываю ехешник, все вроде бы нормально. Но потом когда его снова запускаю, опять появляются эти десять предприятий. Т.е. в базе данных мои действия почему-то не сохраняются! В чем тут может быть загвоздка?
При закрытии формы (ну, если хочешь - то вообще при каждом удалении: ты ж при каждом добавлении нового предприятия перегружаешь заново базу) вызывай savedb...
Зачем? Есть событие OnClose основной формы. В него впиши вызов savedb. Или добавь его в тот код, который я показал выше. Кстати. Он не совсем корректен. Я забыл кое-что:
procedure TForm1.SpeedButton3Click(Sender: TObject); // удаление строки begin if lb1.Items.Count = 2 then exit; Move(records[lb1.ItemIndex + 2], records[lb1.ItemIndex + 1], sizeof(TMyRec) * (200 - (lb1.ItemIndex + 1))); lb1.DeleteSelected;
Dec(col); // Вот без этого процедура сохранения базы будет работать неправильно end;
Работает, говоришь? А пользователь возьмет и закроет форму крестиком. И что тогда с твоей базой будет? Сохранится она?
Пойми, Дельфи - это событийное программирование, т.е., твоя программа должна отслеживать события, и реагировать на них. Пользователь хочет выйти из программы, что при этом происходит? Правильно, для этого ему надо закрыть форму. Как он будет это делать, нажмет ли на крестик, выберет ли в меню File -> Exit, или нажмет специально положенную тобой кнопку - тебя интересовать не должно. Главное - что при этом произойдет событие OnClose, и выполнится код, который ты напишешь в его обработчике.
Плюс очевиден: если тебе надо изменить реакцию на закрытие формы - ты делаешь это в одном месте, и это работает для любого способа закрытия. Не надо лазить по программе и смотреть, "где ж я еще могу выйти из приложение, надо же базу сохранить".
Да! Век живи - век учись! Спасибо! Сейчас вот сижу и соображаю по поводу обработки события для кнопки "Редактировать" (или ""Изменить") данные предприятия. Как вы считаете, для этого можно просто взять и скопировать часть кода из кнопки "Добавить новое предприятие"? Если нет, подскажите, как это лучше организовать?
Я бы написал универсальную функцию, которая (в зависимости от переданных параметров) выполняет либо добавление к базе, либо изменение. А чтоб код не дублировать... Ну, в общем, смотри:
// Сюда будем передавать: // Append = true, если надо добавить новую запись иначе - False // Index = Col, если надо добавить запись, и lb1.ItemIndex + 1 если править... function MakeAction(Append : boolean; Index : Integer) : Integer; var T : TMyRec; res, curr : Integer; begin // Так. Надо новую запись? Значит, поля очистить, и поставить в заголовок // формы и кнопку соотв. текст, чтоб пользователь знал, что делается. if Append then begin for curr := 1 to 7 do (Form2.FindComponent('Edit' + IntToStr(curr)) as TEdit).Clear; form2.sb1.Caption := 'Добавить предприятие'; Form2.Caption := 'Новое предприятие'; end else // Значит, будем править... Сообщаем пользователю begin T := records[Index]; for curr := 1 to 7 do (Form2.FindComponent('Edit' + IntToStr(curr)) as TEdit).Text := T.Arr[curr]; form2.sb1.Caption := 'Изменить данные'; Form2.Caption := 'Изменение данных'; end;
// Это делается в любом случае form2.sb1.Enabled := true;
// А теперь - внимание: не надо привязываться к состоянию компонентов. // Для того, чтобы узнать результат выполнения модальной формы достаточно // посмотреть на ее ModalResult. Если форма закрыта крестиком - то там будет // mrCancel, если кнопкой - будет то, что присвоено по нажатию кнопки... res := form2.ShowModal; //показываем вторую форму и получаем её результат if res = mrOk then begin // Если нажата кнопка подтверждения - переносим поля из формы в структуру for curr := 1 to 7 do T.Arr[curr] := (Form2.FindComponent('Edit' + IntToStr(curr)) as TEdit).Text;
if Append then inc(index); //увеличили кол-во предприятий в базе на 1
records[index] := T; //добавляем эту новую запись на нужное место savedb;//сохраняем новую базу loaddb;//открываем файл базы заново, чтобы увидеть результат операции end; result := Index; // Вернем новое значение Index-а. Это важно при добавлении... end;
// Тогда реализация нажатия кнопок "Добавить" и "Изменить" будет элементарной: procedure TForm1.SpeedButton1Click(Sender: TObject); // Добавить begin Col := MakeAction(true, Col); end;
procedure TForm1.SpeedButton2Click(Sender: TObject); // Изменить begin MakeAction(false, lb1.ItemIndex + 1); end;
Чувствуешь разницу?
Не надо копировать код с места на место. Дельфи предоставляет достаточные средства, чтоб обойтись без дублирования кода.
Теперь еще кое-что. Заметил, что я обращаюсь в цикле к полям записи? Для того, чтоб это стало возможно - я чуть - чуть поменял ее описание - из обычной записи сделал вариантную:
type Str255 = string[255]; TMyRec = record //тип-запись с 7ю полями case boolean of False : (Enterprise : Str255; Form : Str255; Information : Str255; Products : Str255; Partners : Str255; Competitors : Str255; Industry : Str255; ); True : (Arr : array[1 .. 7] of Str255); end;
Теперь и отдельные 7 полей и массив находятся физически в одном месте памяти, и обращение к Arr[1] аналогично обращению к Enterprise, и т.д. Этот фокус проходит только тогда, когда все поля имеют одинаковый тип. Не пытайся сделать такой финт, если хотя бы одно поле либо короче других, либо вообще не строковое - получишь глюки...
Ну, и, наконец, что надо изменить во второй форме:
procedure TForm2.sb1Click(Sender: TObject); // Подтверждение begin ModalResult := mrOk; end;
procedure TForm2.sb2Click(Sender: TObject); // Отмена begin ModalResult := mrCancel; end;
Извините, мне необходимо было на некоторое время отлучиться. Я , честно говоря не ожидал увидеть такой огромный ответ! Во-первых такое-же огромное спасибо! Во-вторых, чтобы "въехать" во всё это, мне нужно какое-то время, чтобы не задавать глупых вопросов. Сейчас выпью валерьянки, успокоюсь и попробую понять весь этот материал. В-третьих, как я понял (судя из ваших замечаний), мне придется ещё изменить и весь написанный ранее код.
Изо всех сил пытался понять принцип работы вашего кода. В целом понятно. Должен сказать что уровень сложности здесь для меня несколько высоковат. Некоторые моменты в программе, как допустим этот
(Form2.FindComponent('Edit' + IntToStr(curr)) as TEdit).Clear;
я вряд ли смогу сегодня объяснить преподу, но попытаюсь. Вообщем переделал свою работу как подсказали. Всё нормально, только почему-то подчеркивает саму функцию. Посмотрите...
Вместо того, чтобы 7 раз вызывать метод Clear у ЭдитБоксов по одному, сделаем это в цикле. Для этого на форме надо найти последовательно 7 компонентов с именами от Edit1 до Edit7. Этим занимается синяя часть... Но FindComponent возвращает указатель на компонент, то есть этот указатель знает только о тех общих свойствах, которые есть у всех компонентов. О том, что было добавлено потом, в наследниках, он понятия не имеет. Это нас не очень устраивает, ведь надо вызвать-то метод Clear, которого не было у TComponent, он был введен позже.
Постой. Но ведь мы, пытаясь искать компоненты, знаем, что там должен быть TEdit, так? Вот, значит, и надо привести найденный компонент к типу TEdit, чтобы он "узнал" о тех свойствах и методах, которые есть у Эдита. Этим занимается красная часть. Ну, а потом, собственно, вызываем нужный метод, или обращаемся к нужному свойству... Вот и всё...
Очень благодарен вам за очень хорошее объяснение. Должен сказать что такие программы может писать не каждый преподаватель. Сейчас, к сожалению, я должен мчаться на лекцию. Сегодня я пожалуй сильно удивлю учителя, показав ему "свой" труд. Протестировать программу уже не успеваю...
Программа работает безупречно! Благодаря вам! Ни один из студентов не осмелился использовать в коде функции, все как один "сидят" на процедурах. Я и сам честно говоря побаиваюсь их использовать, процедуры как-то родней и проще кажутся. Я ещё вот что хотел спросить по поводу моей работы: 1) В самом начале приведенного кода использованы две переменные
res, curr : Integer;
Можно перевести res как результат, а curr как поля? Это английские слова или...? 2) Можно это
for curr := 1 to 7 do (Form2.FindComponent('Edit' + IntToStr(curr)) as TEdit).Text := T.Arr[curr];
сформулировать так: для полей с 1 по 7-й на форме найти 7 компонентов с именами от Edit1 до Edit7, которым присвоить значение элементов массива?
Поспешил я немного с безупречностью. Программа немного глючит, а именно: при добавлении нового предприятия, и нажатии на кнопку "Добавить новое предприятие" - ничего не происходит! Добавление в поле LiistBox получается только при повторной операции! Да и добавляется не то что вводишь в поле, а то что было перед этим. Вообщем как-то кривовато работает. Посмотрите пожалуйста, может где-то ошибка в коде? По поводу удаления, никаких проблем, вроде все четко.
Причина: Col должно изменяться ДО записи в базу, а не ПОСЛЕ. Отсюда и глюки. Чуть-чуть поправил (заодно подкорректировал интерфейс: теперь после добавления/правки содержимого базы СтрингГрид обновляется автоматически, и фокус в ЛистБоксе остается на скорректированном элементе).
В общем...(Показать/Скрыть)
procedure MakeAction(Append: boolean; Index: Integer); var T : TMyRec; res, curr : Integer; begin // Так. Надо новую запись? Значит, поля очистить, и поставить в заголовок // формы и кнопку соотв. текст, чтоб пользователь знал, что делается. if Append then begin for curr := 1 to 7 do (Form2.FindComponent('Edit' + IntToStr(curr)) as TEdit).Clear; form2.sb1.Caption := 'Добавить предприятие'; Form2.Caption := 'Новое предприятие'; end else // Значит, будем править... Сообщаем пользователю begin T := records[Index]; for curr := 1 to 7 do (Form2.FindComponent('Edit' + IntToStr(curr)) as TEdit).Text := T.Arr[curr]; form2.sb1.Caption := 'Изменить данные'; Form2.Caption := 'Изменение данных'; end;
// Это делается в любом случае form2.sb1.Enabled := true;
// А теперь - внимание: не надо привязываться к состоянию компонентов. // Для того, чтобы узнать результат выполнения модальной формы достаточно // посмотреть на ее ModalResult. Если форма закрыта крестиком - то там будет // mrCancel, если кнопкой - будет то, что присвоено по нажатию кнопки... res := form2.ShowModal; //показываем вторую форму и получаем её результат if res = mrOk then begin // Если нажата кнопка подтверждения - переносим поля из формы в структуру for curr := 1 to 7 do T.Arr[curr] := (Form2.FindComponent('Edit' + IntToStr(curr)) as TEdit).Text;
if Append then begin inc(index); //увеличили кол-во предприятий в базе на 1 col := index; // <--- Вот этого для корректной работы не хватало... end;
records[index] := T; //добавляем эту новую запись на нужное место savedb;//сохраняем новую базу loaddb;//открываем файл базы заново, чтобы увидеть результат операции
with form1 do begin lb1.SetFocus; lb1.ItemIndex := Pred(Index); lb1Click(lb1); end; end;
// Реализация нажатия кнопки "Добавить" тоже изменилась:
procedure TForm1.SpeedButton1Click(Sender: TObject);// добавить новое предпр. с данными begin MakeAction(true, Col); // раньше был вызов функции, теперь - процедура end;
Имеется в виду, как себя ведет программа в работе. Раньше что было? Жал ты на кнопку "Изменить", менял содержимое записи, нажимал на "Применить", и что? Данные в StringGrid-е не обновились, фокус в списке потерялся, приходилось вспоминать, какое предприятие изменялось, и переключаться на него. Кому надо работать с таким приложением? Я сделал так, как должно быть: поменял значение - информация тут же обновилась, и в списке предприятий выбрано именно то, которое было изменено.