Перемещение изображения по форме с помощью мыши

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

Итак, задача. На форме размещены несколько изображений, загружаемых из внешних файлов (их имена 1.bmp, 2.bmp и т.д.).

Изображения должны быть перемещаемыми с помощью мыши.

Первое решение, пришедшее мне в голову - это решение "в лоб". Разместив на форме несколько Image, заставим их перемещаться вместе с мышью. Разместим на форме в нужных нам местах несколько (n) пустых Image, присвоим их свойству Tag значения от 1 до n - это пригодится при создании массива из них. Объявим следующие переменные:


implementation  
  var Pic: array[1..n] of TImage;//Сюда мы занесём наши Image  
  x0,y0:integer;//Это будут координаты нажатия мыши  
  flag:boolean;//а это тоже полезная переменная - флажок  

Для первого из наших Image создадим обработчики следующих событий


{Как вы уже догадались наша форма называется MainForm}  
  
procedure TMainForm.Image1MouseDown(Sender: TObject; Button: TMouseButton;  
  Shift: TShiftState; X, Y: Integer);  
begin  
 If button<>mbLeft Then   
  flag:=false  
 Else begin  
  flag:=true;  
  x0:=x;  
  y0:=y  
 end  
end;  
{ При нажатии левой клавиши мыши над нашим Image запомним координаты нажатия 
и установим флажок. Это делается для того, чтобы Image перемещался только при 
опущенной левой кнопке мыши}  
  
procedure TMainForm.Image1MouseUp(Sender: TObject; Button: TMouseButton;  
  Shift: TShiftState; X, Y: Integer);  
begin  
 flag:=false  
end;  
{При отпускании кнопки мыши, не важно какой, сбросим флажок}  
  
procedure TMainForm.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,  
  Y: Integer);  
begin  
 If flag Then begin //Если флажок установлен, т.е. нажата левая копка мыши  
  (Sender As TImage).Left:=(Sender As TImage).Left+x-x0;  
  (Sender As TImage).Top:=(Sender As TImage).Top+y-y0  
 //Наш Image начинает перемещаться  
 end;  
end;  

Обратите внимание, что перемещается не Image1, а Sender. Созданные нами процедуры будут применены для обработки перемещений всех изображений на форме. Для этого в процедуре создания формы запишем все Image на форме в массив Pic


procedure TMainForm.FormCreate(Sender: TObject);  
var i:byte;  
begin  
For i:=0 To MainForm.ComponentCount-1 Do  
 If (MainForm.Components[i] Is TImage) Then  
  Pic[MainForm.Components[i].Tag]:=(MainForm.Components[i] As TImage);  
{Здесь мы просматриваем компоненты формы и если рассматриваемый компонент 
- TImage присваеваем его в массив Pic c индексом Tag}  
For i:=1 To n Do  begin  
 Pic[i].Picture.LoadFromFile(IntToStr(i)+'.bmp');//Загружаем изображение  
 Pic[i].OnMouseDown:=Image1MouseDown;  
 Pic[i].OnMouseMove:=Image1MouseMove;  
 Pic[i].OnMouseUp:= Image1MouseUp  
{Присваеваем нужные процедуры нужным событиям}  
end  
end;  
{В принципе можно было бы обойтись одним циклом For, но, на мой взгляд 
два цикла наглядней и проще для понимания}  

Итак, полученный код позволяет разместить на форме n изображений и перемещать их с помощью мыши. Можно удовлетвориться полученным решением,если бы не одна страшная проблема - МЕРЦАНИЕ.

Я не большой мастер DELPHI, и я не знаю общего способа, как победить мерцание. Вообще, у меня возникло ощущение, что при перемещеннии Image по форме мерцания не избежать. Буду благодарен тому, кто покажет обратное. Ну в общем применение известных способов, например


MainForm.ControlStyle:=MainForm.ControlStyle+[csOpaque];  

или процедуры Invalidate мне не помогло.

Следующим моим шагом было посещение Мастеров Дельфи, где я прочёл статью Михаила Христосенко "Перемещение Image'a по форме во время работы программы". Применение метода


(Sender As TImage).SetBounds((Sender As TImage).Left+x-x0,(Sender As TImage).Top+  
y-y0,(Sender As TImage).width,(Sender As TImage).height);  

в процедуре Image1MouseMove, рекомендованое Михаилом привело к снижению мерцания, но не избавило от него. Более того, в взрослых программах, таких как например само DELPHI,применяется третий из описанных Михаилом способов - перемещение не изображения, а его рамки.

Тогда я задумался, а не является ли применение TImage для перемещения изображения по форме тупиком. И тут я понял, что знаю компонент, на котором можно разместить изображение, и который не мерцает "по определению". Этот компонент (да простят меня Мастера Дельфи) - форма.

Итак следующий проект состоит из двух форм - FormMain и ImageForm. На ImageForm размещён пустой Image1, занимающий всю клиентскую область ImageForm. ImageForm относится к Available forms - это действие не принципиально, но экономит во время запуска приложения около 100 кб памяти. Свойство BorderStyle для ImageForm устанавливаем bsNone.

Для того, чтобы ImageForm перемещалась за Image1 создаём следующую процедуру:


procedure TImageForm.Image1MouseDown(Sender: TObject; Button: TMouseButton;  
  Shift: TShiftState; X, Y: Integer);  
const  SC_DragMove = $F012;  
begin  
  ReleaseCapture;  
  perform(WM_SysCommand, SC_DragMove, 0);  
end;  

На этом работа над ImageForm заканчивается.

Возвращаемся к FormMain.
Сделаем следующие объявления


implementation  
const n=4; // Сколько нам нужно изображений  
uses Unit2;  
var Fa:array[1..n]of TImageForm;  

Теперь нам нужно создать массив Fa, разместить его на форме и заполнить его изображениями. Всё это делается вручную - фокус с Tag тут уже не пройдёт. Делать это лучше не во время создания формы, а например во время активации.


procedure TFormMain.FormActivate(Sender: TObject);  
var i:byte;  
begin  
for i:=1 to n do begin  
Fa[i]:=TImageForm.Create(Self);// Создание формы  
Fa[i].Parent:=Self;//Без этой строки наши формы будут бегать по всему экрану  
Fa[i].Visible:=True; //Вывод формы на экран  
Fa[i].Image1.Picture.LoadFromFile(IntToStr(i)+'.bmp');// Загрузка картинки  
Fa[i].Top:=i*50 //Выбор места расположения (здесь ставятся ваши значения)  
 end;  
end;  

Другой вариант - разместить на форме Timer с незначительным интервалом и разместить вышеприведённый код в процедуре OnTimer, указав в конце Timer1.Enabled:=false;

Последний штрих - установите "Отображать содержимое окна при его перетаскивании" с помощью следующей процедуры


B:Bool;//Объявите B где-нибудь после implementation  

В FormCreate включите следующее


B:=True;  
SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, 0, @B,  SPIF_SENDCHANGE)// Не проверял.  

Ура! В созданная таким образом программе перемещаемые изображения не мерцают.

Однако объём памяти, занимаемый ею во время работы весьма велик. Программа состоящая из вышеприведённых процедур занимает на диске около 400 кб, а в ОП - порядка 2 мб. Попробую поискать менее ресурсоёмкое решение.

Автор: Ижогин Ян Валерьевич
Теги:
TImage
Добавлено: 04 Августа 2018 08:21:00 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...