Drag and Drop файлов на форму/компоненту
В дебрях сети нарыл код принятия списка файлов формой при drag&drop'е. После этого ещё почитал, и ещё, порассматривал его и хочу предложить некий "шаблон" для добавления в проект.
class TForm1 : public TForm
{
private: // User declarations
void virtual __fastcall WMDropFiles(TWMDropFiles &message);
public: // User declarations
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_DROPFILES,TWMDropFiles,WMDropFiles)
END_MESSAGE_MAP(TForm);
};
[Unit1.cpp]
void __fastcall TForm1::FormCreate(TObject *Sender)
{
DragAcceptFiles(Form1->Handle, true);
//вместо Form1 может быть любой компонент, например, ListBox или Memo, тогда файлы будут дропаться только над этим компонентом
//это я выяснил как раз во время написания данного поста, который изначально был вопросом, как это делать :)
}
//---------------------------------------------------------------------------
void __fastcall TForm1::WMDropFiles(TWMDropFiles &message)
{
int filecount, length, i;
filecount = DragQueryFile ((HDROP) message.Drop, 0xFFFFFFFF/*-1*/, NULL, 0); //получаем количество файлов
//подробнее о параметрах и возвращаемых значениях DragQueryFile можно узнать тут или в хелпе Билдера
AnsiString filename;
for (i = 0; i < filecount; ++i) {
filename.SetLength(MAX_PATH);
length = DragQueryFile((HDROP)message.Drop, i, filename.c_str(), filename.Length());
filename.SetLength(length);
//тут можно с файлами творить что угодно, например, имена добавить в StringList или открывать по очереди в Memo
}
DragFinish ((HDROP) message.Drop);
}
//---------------------------------------------------------------------------
Комментируйте. Поправляйте...
И, кстати, назрел вопрос, который я ещё не рассматривал...
Если у меня 2 компонента, которые требуют разных вариантов принятия файлов... например, ЛистБокс, который добавляет в себя имена файлов, и Мемо, которое подгружает текст файла, на неё сброшенного... Как организовать разделение труда?
В дебрях сети нарыл код принятия списка файлов формой при drag&drop'е. После этого ещё почитал, и ещё,
{
bool overlistbox = false, overmemo = false;
int filecount, length, i = 0;
AnsiString filename;
LPPOINT p;
GetCursorPos(p);
if (p->x >= Form1->Left + ListBox1->Left &&
p->x <= Form1->Left + ListBox1->Left + ListBox1->Width &&
p->y >= Form1->Top + ListBox1->Top &&
p->y <= Form1->Top + ListBox1->Top + ListBox1->Height)
overlistbox = true;
if (p->x >= Form1->Left + Memo1->Left &&
p->x <= Form1->Left + Memo1->Left + Memo1->Width &&
p->y >= Form1->Top + Memo1->Top &&
p->y <= Form1->Top + Memo1->Top + Memo1->Height)
overmemo = true;
if (overlistbox) {
filecount = DragQueryFile ((HDROP) message.Drop, 0xFFFFFFFF, NULL, 0);
for (i = 0; i < filecount; ++i) {
filename.SetLength(MAX_PATH);
length = DragQueryFile((HDROP)message.Drop, i, filename.c_str(), filename.Length());
filename.SetLength(length);
ListBox1->Items->Add(filename);
}
}
if (overmemo) {
filename.SetLength(MAX_PATH);
length = DragQueryFile((HDROP)message.Drop, i, filename.c_str(), filename.Length());
filename.SetLength(length);
//тут может быть проверка например на валидность файла
Memo1->Lines->LoadFromFile(filename);
}
и тут заковыка... когда бросаю файлы на мемо - ничего не проиходит :(
а на листбокс - ошибка:
останавливается на строке после
GetCursorPos(p);
что делать? :(
кстати, ещё нашёл на codenet.ru чуток по теме... но всёравно нету о нескольких вариантах приёма файлов...
останавливается на DragQueryPoint((HDROP) message.Drop, p);
Пора, наверное, поспать наконец... :)
Типичный таракан - использование глобального имени экземпляра класса внутри класса. Правильнее писать this->Handle
Типичный таракан - использование глобального имени экземпляра класса внутри класса. Правильнее писать this->Handle
ну да... а конкретнее по вопросу, если можно?
картинку видел?
это - "шаблон",
а у твой вариант "копи-пасте" называется
P.S.
но в FAQ я его добавил
Объясняю. Представь, что в программа является "многооконной". Вполне вероятно, создаётся несколько объектов данного класса и вызывается для одного из них данный метод. Что происходит?
- правильно: обращение к полям, методам и свойствам этого объекта.
- не правильно: обращение к полям и свойствам объекта Form1
::DragAcceptFiles(ListBox1->Handle,true);
Пробовал... Действительно, возможность приёма осуществляется только над теми объектами, чьи хендлы будут указаны при вызове этой функции.
Например,
::DragAcceptFiles(Memo1->Handle,true);
Дропать можно только над Мемо и Листбоксом, над остальными объектами будет запрет.
Но как определить, над каким из объектов дропнули? Этого я не могу сделать без вашей помощи... :(
это - "шаблон",
а у твой вариант "копи-пасте" называется
P.S.
но в FAQ я его добавил
К сожалению и стыду признаю, что шаблонами ещё пользоваться не умею, создавать классы-наследники тоже... Объектно-ориентированное программирование для меня пока почти филькина грамота...
Покажите мне, что, где (и как?) сделать, чтобы определить, какой объект должен обработать событие дропа на нём...
Объясняю. Представь, что в программа является "многооконной". Вполне вероятно, создаётся несколько объектов данного класса и вызывается для одного из них данный метод. Что происходит?
- правильно: обращение к полям, методам и свойствам этого объекта.
- не правильно: обращение к полям и свойствам объекта Form1
А можно частный случай привести как пример? Я из него попробую вычленить нужное... ;)
ИМХО, Вы слишком ударяетесь в теорию, а эта часть мне никогда не давалась легко... :o
З.Ы. Извините, что такой непонятливый...
Значит так. Программа создаёт несколько объектов (в данном случае - окон), но данное действие будет выполняться только для конкретного экземпляра (в данном случае Form1).
Но если указатель Form1 не будет содержать адрес реально существующего объекта, то программа пойдёт на Access Violation :(
Конкретный пример - фрагмент функции WinMain
// Отключаем создание объекта для указателя Form1
// Application->CreateForm (__classid(TForm1), &Form1);
// Создаём окно без присвоения адреса указателю Form1
new TForm1 (Application);
Application->Run();
Объект формы будет удалён объектом приложения (указатель Application), поэтому потеря результата команды new не вызовет "утечку памяти". Но если в методах класса TForm1 где-то будет написано Form1, программа пойдёт лёсом, потому что Form1 указывает в никуда :)
Но я не понимаю, как это объяснит мне, что делать с двумя компонентами, которые могут принимать дроп - как различить на какой компонент дропается?
Сначала прикинь, как будет реализовываться drop для компонентов, положенных на форму - их методы нельзя перекрыть. Скорее всего, придётся задавать WndProc. Чей WndProc задашь - тот и обрабатывать будет. Вопрос снят?
да куда ты денешся:)
см. 27. Как научить DBGrid слушаться колесико мышки?
http://forum.codenet.ru/showthread.php?t=26390&page=2
там пример сабклассинга оконной процедуры
1. Создать потомка листбокс
2. Перехватить оконную функцию листбокс.
2. Перехватить оконную функцию листбокс.
А можно пример?
в ФАк-е это есть, надо только немного по-копипастить...
TWndMethod oldProc;
void __fastcall newProc(TMessage& message);
// in cpp
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
oldProc = ListBox1->WindowProc;
ListBox1->WindowProc = newProc;
::DragAcceptFiles(ListBox1->Handle, true);
}
void __fastcall TForm1::newProc(TMessage& message)
{
if(message.Msg == WM_DROPFILES) {
// cut form FAQ
int filecount, length, i;
filecount = DragQueryFile ((HDROP) message.Drop, 0xFFFFFFFF/*-1*/, NULL, 0); //получаем количество файлов
//подробнее о параметрах и возвращаемых значениях DragQueryFile можно узнать тут или в хелпе Билдера
AnsiString filename;
for (i = 0; i < filecount; ++i) {
filename.SetLength(MAX_PATH);
length = DragQueryFile((HDROP)message.Drop, i, filename.c_str(), filename.Length());
filename.SetLength(length);
//тут можно с файлами творить что угодно, например, имена добавить в StringList или открывать по очереди в Memo
}
DragFinish ((HDROP) message.Drop);
}
oldProc(message);
}