Drag&Drop из дочерней таблицы в родительскую
Дата: 30.10.2011 | Комментариев: 0 | Просмотров: 4918
В этой статье я расскажу как организовать перетаскивание записи мышью из дочернего DBGrid-а в родительский. Проблема заключается в том, что при установке курсора за другую запись в родительском DataSet-е, получаем другое содержимое в дочернем (соответственно теряется запись, которую нужно переместить). Не всегда есть возможность использовать SQL-запросы. Попробуем сделать drag&drop без использования SQL с подсветкой фона Grid-а во время перемещения.Что понадобится
1. IDE (я использую Delphi 2010)
2. Установленный компонент DBGridEh. Почему ..Eh? Еще начиная работать в Delphi 6-7 меня всегда удивляло, что Borland даже не пытается расширить возможности стандартного DBGrida. Поэтому стандартным DBGrid-ом я никогда не пользовался, что и вам советую. Есть много хороших компонентов: EhLib, X-Grid, TopGrid, cxGrid и многие другие.
Нам понадобятся пару DataSet-ов. Чтобы долго не мудрствовать, используем для тестирования обычные TClientDataSet.
Создаем проект
1. Создайте и сохраните новый проект.
2. Положите на форму следеющие компоненты:
TDBGridEh (2 шт.)
TClientDataSet (2 шт.)
TDataSource (2 шт.)
TButton (1 шт.)
3. Задайте следующие имена компонентам и форме:
- Form1 => frmMain
- ClientDataSet1 => Master
- ClientDataSet2 => Detail
- DataSource1 => DSMaster
- DataSource2 => DSDetail
- DBGridEh1 => gridMaster
- DBGridEh2 => gridDetail
4. Свяжите компоненты в свойствах так:
- gridMaster.DataSource:= DSMaster;
- gridDetail.DataSource:= DSDetail;
- DSMaster.DataSet:= Master;
- DSDetail.DataSet:= Detail;
Я не буду вас мучить созданием DataSet-ов. Не в их создании состоит цель статьи. Поэтому сделайте следующее:
1. Добавьте в глобальные константы это
const
В В {Имена файлов}
В В fMaster: string = 'Master.cds';
В В fDetail: string = 'Detail.cds';
2. Скопируйте для кнопки Button1 следующий код
procedure TfrmMain.Button1Click(Sender: TObject);
var
В В m, d: Integer;
begin
В В if FileExists(fMaster) then
В В В В DeleteFile(fMaster);
В В if FileExists(fDetail) then
В В В В DeleteFile(fDetail);
В В with Master do
В В begin
В В В В FieldDefs.Clear;
В В В В FieldDefs.Add('ID', ftAutoInc, 0, False);
В В В В FieldDefs.Add('Number', ftInteger, 0, False);
В В В В FieldDefs.Add('Name', ftString, 20, False);
В В В В Master.CreateDataSet;
В В end;
В В with Detail do
В В begin
В В В В FieldDefs.Clear;
В В В В FieldDefs.Add('ID', ftAutoInc, 0, False);
В В В В FieldDefs.Add('Number_master', ftInteger, 0, False);
В В В В FieldDefs.Add('name', ftString, 20, False);
В В В В CreateDataSet;
В В В В MasterSource := DSMaster;
В В В В MasterFields := 'number';
В В В В IndexFieldNames := 'Number_master';
В В end;
В В with Master do
В В begin
В В В В for m := 1 to 5 do
В В В В begin
В В В В В В Insert;
В В В В В В FieldByName('Number').AsInteger := m;
В В В В В В FieldByName('Name').AsString := 'Мастер ' + IntToStr(m);
В В В В В В Post;
В В В В В В with Detail do
В В В В В В begin
В В В В В В В В for d := 1 to 5 do
В В В В В В В В begin
В В В В В В В В В В Insert;
В В В В В В В В В В FieldByName('Number_master').AsInteger := m;
В В В В В В В В В В FieldByName('Name').AsString := 'Деталь ' + IntToStr(d);
В В В В В В В В В В Post;
В В В В В В В В end;
В В В В В В end;
В В В В end;
В В end;
В В Master.SaveToFile(fMaster);
В В Master.LoadFromFile(fMaster);
В В Detail.SaveToFile(fDetail);
В В Detail.LoadFromFile(fDetail);
end;
3. Скомпилируйте приложение, один раз нажмите на кноку и закройте приложение.
4. В папке приложения создались два файла таблиц с именами Master.cds и Detail.cds
5. Удалите код для кнопки и саму кнопку. Они нам больше не понадобятся.
Добавьте несколько переменных в глобальные
var
В В frmMain: TfrmMain;
В В {Будем запоминать координаты мыши}
В В ScrPt, GrdPt: TPoint;
В В Cell: TGridCoord;
В В {...И номера записей}
В В aRecNo, current_rec: Integer;
При событии OnDragDrop нам нужно изменить значение поля Number_Master в таблице Detail. Но перемещая мышь над родительским Grid-ом (событие OnDragOver) мы не можем получить значение ключевого поля записи над которой висит мышь без установки курсора на эту запись. Зато мы можем получить порядковый номер (RecNo) записи. Именно на этом и построим все дальшейшие действия.
Для события OnCreate формы напишите такой код
procedure TfrmMain.FormCreate(Sender: TObject);
begin
В В { Загрузим DataSet-ы из файлов }
В В if FileExists(fMaster) then
В В В В Master.LoadFromFile(fMaster);
В В if FileExists(fDetail) then
В В В В Detail.LoadFromFile(fDetail);
В В { Сделаем выделение всей строки в Гридах,
В В В В чтобы было лучше видно выделение цветом }
В В gridMaster.Options := gridMaster.Options + [dgRowSelect];
В В gridDetail.Options := gridDetail.Options + [dgRowSelect];
end;
Для события OnClose формы напишите такой код
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
В В { Сохраним DataSet-ы в файлы }
В В Master.SaveToFile(fMaster);
В В Detail.SaveToFile(fDetail);
end;
Итак, у нас есть два связанных DataSet-а, содержимое которых загружается из файлов при создании формы, и записывается в файлы при закрытии формы. Все готово для перемещения записей посредством drag&drop.
Напишите такой код для события onmousedown дочернего Grid-а (описание действий в комментариях):
procedure TfrmMain.gridDetailMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
В В if Button = mbleft then
В В begin
В В В В { Если в дочерней таблице нет записей
В В В В В В или в родительской всего одна, то ничего не делаем }
В В В В if (Detail.RecordCount = 0) or (Master.RecordCount < 2) then
В В В В В В exit;
В В В В { Запомним текущую запись в родительской таблице }
В В В В current_rec := Master.RecNo;
В В В В { ...И начнем перетаскивание }
В В В В gridDetail.BeginDrag(False);
В В end;
end;
Напишите такой код для события OnDragOver родительского Grid-а (описание действий в комментариях):
procedure TfrmMain.gridMasterDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
В В { По координатам мыши определим номер записи }
В В ScrPt := Mouse.CursorPos;
В В GrdPt := gridMaster.ScreenToClient(ScrPt);
В В Cell := gridMaster.MouseCoord(GrdPt.X, GrdPt.Y);
В В aRecNo := Cell.Y;
В В { Refresh делаем для обновления сетки, т.к. подсветка
В В В В цветами могла измениться при перемещении мыши на другую запись }
В В gridMaster.Refresh;
В В { Нет смысла принимать запись из той же категории }
В В Accept := current_rec <> aRecNo;
end;
Напишите такой код для события OnDragDrop родительского Grid-а (описание действий в комментариях):
procedure TfrmMain.gridMasterDragDrop(Sender, Source: TObject; X, Y: Integer);
var
В В L, Old_RecNo, New_Number_Master: Integer;
begin
В В { Запомним номера записей в обеих таблицах }
В В L := Detail.RecNo;
В В Old_RecNo := Master.RecNo;
В В { Переместимся на указанную запись
В В В В и запомним значение поля Number }
В В Master.RecNo := aRecNo;
В В New_Number_Master := Master.FieldByName('Number').AsInteger;
В В { Вернемся на предыдущую запись }
В В Master.RecNo := Old_RecNo;
В В with Detail do
В В begin
В В В В { Вернемся на предыдущую запись
В В В В В В и запишем в поле Number_Master новое значение }
В В В В RecNo := L;
В В В В Edit;
В В В В FieldByName('Number_Master').AsInteger := New_Number_Master;
В В В В Post;
В В В В { Для того чтобы курсор не убегал на последнюю запись
В В В В В В попробуем оставить номер записи прежним }
В В В В if RecordCount > 2 then
В В В В В В RecNo := L;
В В end;
В В { Перерисуем грид, чтобы удалить цвета }
В В aRecNo := -1;
В В gridMaster.Refresh;
end;
У DBGridEh есть отличное событие OnGetCellParams. Код срабатывает при получении ячейкой параметров. Этот метод работает гораздо быстрее, чем стандарнтый метод OnDrawDataCell. Его мы и будем использовать. Напишите для OnGetCellParams родительского Grid-а такой код:
procedure TfrmMain.gridMasterGetCellParams(Sender: TObject; Column: TColumnEh; AFont: TFont; var Background: TColor; State: TGridDrawState);
begin
В В { Если это та самая запись, то она
В В В В недоступна для приема. Подсветим ее красным цветом }
В В if aRecNo > -1 then
В В begin
В В В В if (Master.RecNo = aRecNo) then
В В В В В В Background := clRed;
В В В В { Если запись готова для приема - пусть она будет зеленой }
В В В В if (Master.RecNo = aRecNo) and not(gdSelected in State) then
В В В В В В Background := clGreen;
В В end;
end;
Все. Drag&Drop из дочерней таблицы в родительскую готов.
Если у кого-то есть другие идеи по Drag&Drop между Grid-ами - напишите.
Удачи.
Delphi_Coder
Октябрь, 2011
При перепечатке любых статей ссылка на сайт delphi-z.ru обязательна.
Все используемые на сайте статьи, файлы и логотипы компаний принадлежат их законным владельцам. Если вы являетесь правообладателем "Drag&Drop из дочерней таблицы в родительскую" и не желаете, чтобы ваша информация находилась на нашем сайте, напишите нам и эта информация будет удалена.
Категория: Публикации » Публикации - Мои статьи
Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.