Помощь - Поиск - Пользователи - Календарь
Полная версия: Выдача ошибки при компиляции
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Делфи
Искатель
Здравствуйте! Хочу показать работу (обработка базы данных предприятий) ,в которой не знаю как запрограммировать две кнопки: "Удалить" предприятие и "Изменить" данные о предприятии. Помогите, пожалуйста.
Кнопку "Удалить" попробовал обработать, но получается какая-то ахинея.
IUnknown
Цитата
Кнопку "Удалить" попробовал обработать, но получается какая-то ахинея.
Во-первых, не надо удалять ничего из 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;
Искатель
Здравствуйте. Спасибо за подсказку.
А вот здесь
sizeof(TMyRec) * (200 - (lb1.ItemIndex + 1)));

число 200 что означает?
И ещё; когда создаю, например, десять предприятий, затем половину из них поочереди удаляю и закрываю ехешник, все вроде бы нормально. Но потом когда его снова запускаю, опять появляются эти десять предприятий. Т.е. в базе данных мои действия почему-то не сохраняются! В чем тут может быть загвоздка?
IUnknown
Цитата
число 200 что означает?
Как что? Размер твоего массива:
Цитата
  records: array [1..200] of TMyRec; //массив данных, подгружаемых из файла
Задал бы его именованной константой - вопроса бы не было... Но раз задаешь "магическим числом" - то вот тебе и непонимание того, что происходит.

Цитата
И ещё; когда создаю, например, десять предприятий, затем половину из них поочереди удаляю и закрываю ехешник, все вроде бы нормально. Но потом когда его снова запускаю, опять появляются эти десять предприятий. Т.е. в базе данных мои действия почему-то не сохраняются! В чем тут может быть загвоздка?
При закрытии формы (ну, если хочешь - то вообще при каждом удалении: ты ж при каждом добавлении нового предприятия перегружаешь заново базу) вызывай savedb...
Искатель
вызывай savedb..

Т.е. мне нужно создать новую кнопку -"Сохранить"?
IUnknown
Зачем? Есть событие 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;
Искатель
Владимир. А я уже успел нафантазировать. Создал кнопку"Закрыть" и вписал процедуру
procedure TForm1.Button1Click(Sender: TObject);
begin
savedb;
form1.Close;
end;

И ведь, мать честная, работает ! Но если вы сказали нельзя, значит удалю этот бред к едрени фени и сделаю по вашему
IUnknown
Работает, говоришь? А пользователь возьмет и закроет форму крестиком. И что тогда с твоей базой будет? Сохранится она?

Пойми, Дельфи - это событийное программирование, т.е., твоя программа должна отслеживать события, и реагировать на них. Пользователь хочет выйти из программы, что при этом происходит? Правильно, для этого ему надо закрыть форму. Как он будет это делать, нажмет ли на крестик, выберет ли в меню File -> Exit, или нажмет специально положенную тобой кнопку - тебя интересовать не должно. Главное - что при этом произойдет событие OnClose, и выполнится код, который ты напишешь в его обработчике.

Плюс очевиден: если тебе надо изменить реакцию на закрытие формы - ты делаешь это в одном месте, и это работает для любого способа закрытия. Не надо лазить по программе и смотреть, "где ж я еще могу выйти из приложение, надо же базу сохранить".
Искатель
Да! Век живи - век учись! Спасибо!
Сейчас вот сижу и соображаю по поводу обработки события для кнопки "Редактировать" (или ""Изменить") данные предприятия. Как вы считаете, для этого можно просто взять и скопировать часть кода из кнопки "Добавить новое предприятие"? Если нет, подскажите, как это лучше организовать?
IUnknown
Цитата
как это лучше организовать?
Я бы написал универсальную функцию, которая (в зависимости от переданных параметров) выполняет либо добавление к базе, либо изменение. А чтоб код не дублировать... Ну, в общем, смотри:

// Сюда будем передавать:
// 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;
я вряд ли смогу сегодня объяснить преподу, но попытаюсь.
Вообщем переделал свою работу как подсказали. Всё нормально, только почему-то подчеркивает саму функцию. Посмотрите...
IUnknown
MakeAction - это обычная функция, не метод класса формы, убери ее описание из класса.

Цитата
Некоторые моменты в программе, как допустим этот
(Form2.FindComponent('Edit' + IntToStr(curr)) as TEdit).Clear;

я вряд ли смогу сегодня объяснить преподу, но попытаюсь.
Все очень просто... Смотри:

(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 получается только при повторной операции! Да и добавляется не то что вводишь в поле, а то что было перед этим. Вообщем как-то кривовато работает. Посмотрите пожалуйста, может где-то ошибка в коде?
По поводу удаления, никаких проблем, вроде все четко.
IUnknown
Цитата
Программа немного глючит
Причина: Col должно изменяться ДО записи в базу, а не ПОСЛЕ. Отсюда и глюки. Чуть-чуть поправил (заодно подкорректировал интерфейс: теперь после добавления/правки содержимого базы СтрингГрид обновляется автоматически, и фокус в ЛистБоксе остается на скорректированном элементе).

В общем... (Показать/Скрыть)
Искатель
Спасибочки! А можно перевести это
 и фокус в ЛистБоксе остается на скорректированном элементе
на русский язык. Имеется ввиду когда все поля имеют одинаковый тип?
IUnknown
Имеется в виду, как себя ведет программа в работе. Раньше что было? Жал ты на кнопку "Изменить", менял содержимое записи, нажимал на "Применить", и что? Данные в StringGrid-е не обновились, фокус в списке потерялся, приходилось вспоминать, какое предприятие изменялось, и переключаться на него. Кому надо работать с таким приложением? Я сделал так, как должно быть: поменял значение - информация тут же обновилась, и в списке предприятий выбрано именно то, которое было изменено.
Искатель
Благодарю вас. Надеюсь, что в ближайшее время вас не побеспокою.
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.