пятница, 11 мая 2012 г.

Урок 44. Drag & Drop

В предыдущем уроке я подробно рассказывал, как сделать интерфейс, позволяющий набирать в группу счета или удалять их из группы.

Но, я всегда говорил и говорю теперь, что одну и ту же задачу можно решить множеством разных способов.

Вот в подтверждение своих слов я и хочу еще раз остановиться на том же интерфейсе, но модернизировать его под использование технологии перетаскивания (Drag & Drop).



Для начала нужно удалить некоторые элементы управления:


- DBGridEh3 и пару кнопок со стрелками. Компонент TDataSource по имени DataSourceAcc тоже не понадобится.

Соответственно, удалить и процедуры обработки (я воспользовался символами комментариев, поскольку, это не тот случай, когда "ломать - не строить: душа не болит"):


//    procedure DBGridEh3DblClick(Sender: TObject);
//    procedure Button2Click(Sender: TObject);
//    procedure Button1Click(Sender: TObject);

и

{
procedure TGroupsFrm.Button1Click(Sender: TObject);
begin
  MyAdd();
end;

procedure TGroupsFrm.DBGridEh3DblClick(Sender: TObject);
begin
  MyAdd();
end;

procedure TGroupsFrm.Button2Click(Sender: TObject);
begin
  MyDelete;
end;
}


На место удаленной сетки добавьте компонент TListBox, выровняв его как и прежде по правому краю:


(как в конструктор в детстве играю, ей Богу :-) )

Напишите новую процедуру, которая наполнит ListBox значениями:


Procedure TGroupsFrm.ListBoxRefresh();
Var
  i: Integer;
begin


  // Очистка списка
  For i:=0 to ListBox1.Items.Count-1 do
    ListBox1.Items.Delete(0);             // Это важно - удалять всегда только 0-ой элемент




  // Наполнение ListBox
  With MainFrm.ADOTableAcc do
  begin


    // В источнике произведены изменения (присвоено новое значение
    // признаку Groups, по которому настроен фильтр,
    // поэтому необходимо вновь запросить данные из источника
    Requery();
    First;
    While not eof do
    begin
      ListBox1.Items.Add(FieldByName('Name').AsString);
      next;
    end;
  end;
end;

Обратите внимание, что

1. Подсчет элементов списка ведется от нуля до количества элементов списка минус единица. Например, если в списке 3 элемента, то их порядковые номера будут 0, 1, 2 (а не 1,2,3).
2. Из ListBox1 удаляется в цикле только 0-ой элемент, поскольку после удаления очередного элемента список перестраивается. В нулевой позиции всегда располагается некий Item вплоть до последнего.

В написанную ранее процедуру, обрабатывающую событие создания формы, необходимо добавить ListBoxRefresh, чтобы после появления формы на мониторе, список был наполнен значениями:


procedure TGroupsFrm.FormCreate(Sender: TObject);
Var MyStr: String;
begin

 ...

 MainFrm.ADOTableAcc.Filtered:=True;


  ListBoxRefresh();                // Урок 44


end;

В инспекторе объектов установите свойства DragMode для DbGridEh2 и ListBox1 в dmAutomatic.

Чтобы описываемый механизм Drag & Drop заработал, нужно написать обработчики двух событий для получателя, т.е. для .DBGridEh2 - сетки, отображающей участников группы:

1. onDragOver,
2. onDragDrop

С первым все довольно просто:


procedure TGroupsFrm.DBGridEh2DragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; var Accept: Boolean);
begin
  Accept := Source = ListBox1;
end;



- иными словами - назначается источник данных ListBox1 для сетки DBGridEh2, когда курсор будет находиться над последней.

Для второго события давайте ограничимся пока только сообщением:

procedure TGroupsFrm.DBGridEh2DragDrop(Sender, Source: TObject; X, Y: Integer);
begin
   Showmessage('Обработать перетаскивание');
end;

И посмотрим, как это все работает.
Если прижать мышкой один из пунктов в списке и потащить его на сетку (2), то курсор над сеткой изменится. Рядом с ним появится прямоугольник, что и будет означать, что получатель готов принять данные из источника в результате Drag & Drop. Отпустив кнопку мышки, получим сообщение на экран:


как результат обработки события DragDrop.

Значит, именно в обработчик этого события и нужно встроить механизм добавления нового участника группы.

Если Вы помните, то процедура добавления называется MyAdd. Может быть, вписать ее в обработчик, и - дело с концом? Не получится. Дело в том, что пока у нас была сетка на месте списка, мы были уверены в том, какой ID у выбранного в ней счета. В списке же присутствуют лишь названия счетов. Беда...

Как и всегда, для решения задачи есть несколько способов. Можно создать массив и наполнять его ID тех счетов, что ложатся в ListBox, "отгадывая" на обратном пути по позиции в списке ID счета в массиве.

Но я добавлю на форму компонент TADOQuery, назвав его TempQuery. И перепишу процедуру MyAdd:

procedure TGroupsFrm.MyAdd();
Var
  MyStr: String;
begin

  // Найти счет по его названию                                 // Урок 44
  TempQuery.active:=False;
  TempQuery.SQL.Clear;
  TempQuery.Parameters.Clear;
  MyStr:='SELECT Accounts.ID, Accounts.Groups ';
  MyStr:=MyStr + 'FROM Accounts ';
  MyStr:=MyStr + 'WHERE (((Accounts.Name)='''+ListBox1.Items[ListBox1.ItemIndex]+'''))';
  TempQuery.SQL.Add(MyStr);
  TempQuery.Active:=True;

  If TempQuery.RecordCount>0
  then
  begin

  // Добавление счета в группу
  With ADOQueryMembers Do  // Работаем с запросом
  Begin
      Insert;              // Добавление новой записи
      Try

        // Присвоение значений полям
        FieldByName('IDGroup').Value:=ADOTableGroups.FieldByName('ID').Value;
        FieldByName('IDAcc').Value:=TempQuery.FieldByName('ID').Value;

        // Запись в таблицу-источник
        Post;

        // Фиксирование признака в кодификаторе счетов (через временный запрос!)
        TempQuery.Edit;
        TempQuery.FieldByName('Groups').Value:=True;
        TempQuery.Post;

        // Обновление данных
        Requery();
      finally
        ListBoxRefresh;                                  // Ранее здесь располагался оператор, обновляющий таблицу счетов
      end;
  end;
  end;

Как говорится: "Найдите 10 отличий" :-)

Осталась самая малость: при исключении счета из группы - обновить список (справа), для чего достаточно в процедуру MyDelete вписать одну строку:

  finally
    ADOQueryMembers.Requery();
    ListBoxRefresh();                             
  end;

Ну, а тем, кому не нравится таскать элементы списка, не полюбился им интерфейс на основе технологии Drag & Drop, мы обязаны (ВСЕГДА ПОМНИ О ПОЛЬЗОВАТЕЛЕ!) дать возможность добавлять счета в группу по двойному щелчку на элементе списка:

procedure TGroupsFrm.ListBox1DblClick(Sender: TObject);
begin
  MyAdd;
end;







Комментариев нет:

Отправить комментарий

Примечание. Отправлять комментарии могут только участники этого блога.