问题: 在DELPHI中,应用什么控件来显示GIF文件?如何用程序实现可用鼠标来拖动图片? ( 积分: 100 )
分类: 控件 - 使用
来自: denvennew, 时间: 2004-10-22 1:19:00, ID: 2859699

谢谢!

来自: sonic1234, 时间: 2004-10-22 1:32:30, ID: 2859706

用GIFIMAGE吧,下载后要修改一下才能支持D6
--------------------------------------------------------------------
GIFImage 2.2 在 Delphi 6 下不能使用的情况修改
作者:wxz
此文最初出处在:www.51delphi.com

TGIFImage 是 Delphi 下优秀的 Gif 格式图片支持控件,可以对GIF动画播放、格式转换、动画GIF制作等等。
但是,在我的机器中安装了 Delphi 6 之后, TGIFImage 控件便不能使用了。因为我必须使用它,所以在一番折腾之后,勉强把它安装了上去。但这两天看到还有些朋友在网上问这个问题,所以把它单独写了出来,供大家参考。

1、 情况:
在应用程序中使用Image控件,Use GIFImage 单元,在Image装入图片时死机。如果这时在IDE中强制中断,会发现有个线程在一直死循环。
2、 检查:
查看死循环代码,它在使用 FindWindow函数查找一个叫‘TthreadWindow’的窗口,如果找到了,那么程序继续执行,否则一直循环。
3、 问题分析:
因为这里是线程中的代码,所以猜测它在模拟 Delphi 5中 Tthread 的Synchronize方法:当找到主线程窗口后,发送消息让主线程执行,做为两个线程的同步方案。下面是源代码:

procedure TThread.Synchronize(Method: TThreadMethod);
begin
 FSynchronizeException := nil;
 FMethod := Method;
 SendMessage(ThreadWindow, CM_EXECPROC, 0, Longint(Self));
 if Assigned(FSynchronizeException) then raise FSynchronizeException;
end;

但是在 Delphi 6 中,已经没有主线程窗口了。因为要向 Linux 移植,所以所有通过 SendMessage 等函数同步线程的方法已经被事件和关键区所代替,所以 GIFImage 在找主窗口时,再也找不到了。

procedure TThread.Synchronize(Method: TThreadMethod);
var
 SyncProc: TSyncProc;
begin
 if GetCurrentThreadID = MainThreadID then
   Method
 else
 begin
{$IFDEF MSWINDOWS}
   SyncProc.Signal := CreateEvent(nil, True, False, nil);
   try
{$ENDIF}
{$IFDEF LINUX}
     FillChar(SyncProc, SizeOf(SyncProc), 0);  // This also initializes the cond_var
{$ENDIF}
     EnterCriticalSection(ThreadLock);
     try
       FSynchronizeException := nil;
       FMethod := Method;
       SyncProc.Thread := Self;
       SyncList.Add(@SyncProc);
       ProcPosted := True;
       if Assigned(WakeMainThread) then
         WakeMainThread(Self);
{$IFDEF MSWINDOWS}
       LeaveCriticalSection(ThreadLock);
       try
         WaitForSingleObject(SyncProc.Signal, INFINITE);
       finally
         EnterCriticalSection(ThreadLock);
       end;
{$ENDIF}
{$IFDEF LINUX}
       pthread_cond_wait(SyncProc.Signal, ThreadLock);
{$ENDIF}
     finally
       LeaveCriticalSection(ThreadLock);
     end;
{$IFDEF MSWINDOWS}
   finally
     CloseHandle(SyncProc.Signal);
   end;
{$ENDIF}
   if Assigned(FSynchronizeException) then raise FSynchronizeException;
 end;
end;

4、 解决办法:
按照常规,应该修改 GIFImage 的源代码,达到与Delphi 6 兼容。但是,我们只是要  GIFImage 正常运行。所以更简单的办法是将 Delphi 5 中的线程部分拷贝出来,拷贝时要注意,包括声明、实现、变量、初始化、终止等处。然后在GIFImage 单元的接口部分 Uses 部分的最后,加上 Delphi5Thread 单元即可,实际内容如下:


unit Delphi5Thread platform;
{
从 Borland 的源代码中剪切、修改。
 wxz 2002.3 版权在 Borland 公司.
}
interface

uses
 SysUtils, Windows, ActiveX, Classes;

type
{ TThread }
 EThread = class(Exception);

 TThreadMethod = procedure of object;
 TThreadPriority = (tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest,
   tpTimeCritical);

 TThread = class
 private
   FHandle: THandle;
   FThreadID: THandle;
   FTerminated: Boolean;
   FSuspended: Boolean;
   FFreeOnTerminate: Boolean;
   FFinished: Boolean;
   FReturnValue: Integer;
   FOnTerminate: TNotifyEvent;
   FMethod: TThreadMethod;
   FSynchronizeException: TObject;
   procedure CallOnTerminate;
   function GetPriority: TThreadPriority;
   procedure SetPriority(Value: TThreadPriority);
   procedure SetSuspended(Value: Boolean);
 protected
   procedure DoTerminate; virtual;
   procedure Execute; virtual; abstract;
   procedure Synchronize(Method: TThreadMethod);
   property ReturnValue: Integer read FReturnValue write FReturnValue;
   property Terminated: Boolean read FTerminated;
 public
   constructor Create(CreateSuspended: Boolean);
   destructor Destroy; override;
   procedure Resume;
   procedure Suspend;
   procedure Terminate;
   function WaitFor: LongWord;
   property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
   property Handle: THandle read FHandle;
   property Priority: TThreadPriority read GetPriority write SetPriority;
   property Suspended: Boolean read FSuspended write SetSuspended;
   property ThreadID: THandle read FThreadID;
   property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
 end;

implementation

{ Thread management routines }

const
 CM_EXECPROC = $8FFF;
 CM_DESTROYWINDOW = $8FFE;

type
 PRaiseFrame = ^TRaiseFrame;
 TRaiseFrame = record
   NextRaise: PRaiseFrame;
   ExceptAddr: Pointer;
   ExceptObject: TObject;
   ExceptionRecord: PExceptionRecord;
 end;

var
 ThreadLock: TRTLCriticalSection;
 ThreadWindow: HWND;
 ThreadCount: Integer;

procedure FreeThreadWindow;
begin
 if ThreadWindow <> 0 then
 begin
   DestroyWindow(ThreadWindow);
   ThreadWindow := 0;
 end;
end;

function ThreadWndProc(Window: HWND; Message, wParam, lParam: Longint): Longint; stdcall;
begin
 case Message of
   CM_EXECPROC:
     with TThread(lParam) do
     begin
       Result := 0;
       try
         FSynchronizeException := nil;
         FMethod;
       except
         if RaiseList <> nil then
         begin
           FSynchronizeException := PRaiseFrame(RaiseList)^.ExceptObject;
           PRaiseFrame(RaiseList)^.ExceptObject := nil;
         end;
       end;
     end;
   CM_DESTROYWINDOW:
     begin
       EnterCriticalSection(ThreadLock);
       try
         Dec(ThreadCount);
         if ThreadCount = 0 then
           FreeThreadWindow;
       finally
         LeaveCriticalSection(ThreadLock);
       end;
       Result := 0;
     end;
 else
   Result := DefWindowProc(Window, Message, wParam, lParam);
 end;
end;

var
 ThreadWindowClass: TWndClass = (
   style: 0;
   lpfnWndProc: @ThreadWndProc;
   cbClsExtra: 0;
   cbWndExtra: 0;
   hInstance: 0;
   hIcon: 0;
   hCursor: 0;
   hbrBackground: 0;
   lpszMenuName: nil;
   lpszClassName: 'TThreadWindow');

procedure AddThread;

 function AllocateWindow: HWND;
 var
   TempClass: TWndClass;
   ClassRegistered: Boolean;
 begin
   ThreadWindowClass.hInstance := HInstance;
   ClassRegistered := GetClassInfo(HInstance, ThreadWindowClass.lpszClassName,
     TempClass);
   if not ClassRegistered or (TempClass.lpfnWndProc <> @ThreadWndProc) then
   begin
     if ClassRegistered then
       Windows.UnregisterClass(ThreadWindowClass.lpszClassName, HInstance);
     Windows.RegisterClass(ThreadWindowClass);
   end;
   Result := CreateWindow(ThreadWindowClass.lpszClassName, '', 0,
     0, 0, 0, 0, 0, 0, HInstance, nil);
 end;

begin
 EnterCriticalSection(ThreadLock);
 try
   if ThreadCount = 0 then
     ThreadWindow := AllocateWindow;
   Inc(ThreadCount);
 finally
   LeaveCriticalSection(ThreadLock);
 end;
end;

procedure RemoveThread;
begin
 EnterCriticalSection(ThreadLock);
 try
   if ThreadCount = 1 then
     PostMessage(ThreadWindow, CM_DESTROYWINDOW, 0, 0);
 finally
   LeaveCriticalSection(ThreadLock);
 end;
end;

{ TThread }

function ThreadProc(Thread: TThread): Integer;
var
 FreeThread: Boolean;
begin
 try
   Thread.Execute;
 finally
   FreeThread := Thread.FFreeOnTerminate;
   Result := Thread.FReturnValue;
   Thread.FFinished := True;
   Thread.DoTerminate;
   if FreeThread then Thread.Free;
   EndThread(Result);
 end;
end;

constructor TThread.Create(CreateSuspended: Boolean);
var
 Flags: DWORD;
begin
 inherited Create;
 AddThread;
 FSuspended := CreateSuspended;
 Flags := 0;
 if CreateSuspended then Flags := CREATE_SUSPENDED;
 FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), Flags, FThreadID);
end;

destructor TThread.Destroy;
begin
 if not FFinished and not Suspended then
 begin
   Terminate;
   WaitFor;
 end;
 if FHandle <> 0 then CloseHandle(FHandle);
 inherited Destroy;
 RemoveThread;
end;

procedure TThread.CallOnTerminate;
begin
 if Assigned(FOnTerminate) then FOnTerminate(Self);
end;

procedure TThread.DoTerminate;
begin
 if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end;

const
 Priorities: array [TThreadPriority] of Integer =
  (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL,
   THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL,
   THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL);

function TThread.GetPriority: TThreadPriority;
var
 P: Integer;
 I: TThreadPriority;
begin
 P := GetThreadPriority(FHandle);
 Result := tpNormal;
 for I := Low(TThreadPriority) to High(TThreadPriority) do
   if Priorities[I] = P then Result := I;
end;

procedure TThread.SetPriority(Value: TThreadPriority);
begin
 SetThreadPriority(FHandle, Priorities[Value]);
end;

procedure TThread.Synchronize(Method: TThreadMethod);
begin
 FSynchronizeException := nil;
 FMethod := Method;
 SendMessage(ThreadWindow, CM_EXECPROC, 0, Longint(Self));
 if Assigned(FSynchronizeException) then raise FSynchronizeException;
end;

procedure TThread.SetSuspended(Value: Boolean);
begin
 if Value <> FSuspended then
   if Value then
     Suspend else
     Resume;
end;

procedure TThread.Suspend;
begin
 FSuspended := True;
 SuspendThread(FHandle);
end;

procedure TThread.Resume;
begin
 if ResumeThread(FHandle) = 1 then FSuspended := False;
end;

procedure TThread.Terminate;
begin
 FTerminated := True;
end;

function TThread.WaitFor: LongWord;
var
 Msg: TMsg;
 H: THandle;
begin
 H := FHandle;
 if GetCurrentThreadID = MainThreadID then
   while MsgWaitForMultipleObjects(1, H, False, INFINITE,
     QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE)
 else WaitForSingleObject(H, INFINITE);
 GetExitCodeThread(H, Result);
end;

initialization
 InitializeCriticalSection(ThreadLock);

finalization
 FreeThreadWindow;
 DeleteCriticalSection(ThreadLock);
end.

来自: sonic1234, 时间: 2004-10-22 1:45:04, ID: 2859710

关于拖动图片,转下HD_COPY的文字
===============================
事先装好鼠标钩子,再设一个全局变量,当鼠标在图片上单击触发了MouseDown时,设为true,
        在鼠标钩子检测鼠标移动的代码中判断这个变量,若为true,进行拖动操作,当鼠标的MouseUp
        和Click事件被触发时将这个变量设为false,这样就什么都不耽误了,如果想逼真一点,可以
        在拖动时改变鼠标的形状,我做的想ACDSEE一样,是一只握紧的小手将图象拖来拖去。
       
  代码:
  鼠标钩子的DLL代码:
 
---------------------------------------------------------------------------------------
library HookDLL;

uses WinTypes, WinProcs, Messages;


{ Global variables }
{$IFDEF WIN32}

{ For a system-wide hook, global variables must be Memory Mapped to be
 accessible by all processes because the data segment of 32-bit DLL is private
 to each process using it. }
type
 PSharedData = ^TSharedData;
 TSharedData = record
   { In this record, mirror the global variables of the 16-bit version }
   HookCount: integer;
   HookHandle: HHook;
   MonitorWnd: HWND;
   WM_MONITORMOUSEMOVE: UINT;
 end;

var
 { global data for the DLL for a single process }
 hMapObject: THandle;
 SharedData: PSharedData;

{$ELSE}

{ 16 bit Windows }
type
 UINT = Longint;

var
 HookCount: integer;
 HookHandle: HHook;
 MonitorWnd: HWND;
 WM_MONITORMOUSEMOVE: UINT;
{$ENDIF}

const
 MESSAGE_MONITOR_MOUSE_MOVE = 'DFSHookDLLMonitorMouseMoveMessage';


function GetMonitorMouseMoveMsg: UINT; export;
begin
 {$IFDEF WIN32}
 if SharedData = NIL then
   Result := 0
 else
   Result := SharedData^.WM_MONITORMOUSEMOVE;
 {$ELSE}
 Result := WM_MONITORMOUSEMOVE;
 {$ENDIF}
end;

{ This is where you do your special processing. }
{$IFDEF WIN32}
function MouseHookCallBack(Code: integer; Msg: WPARAM; MouseHook: LPARAM): LRESULT; stdcall;
{$ELSE}
function MouseHookCallBack(Code: integer; Msg: word; MouseHook: longint): longint; export;
{$ENDIF}
var
 {$IFDEF WIN32}
 HookHandle: HHOOK;
 MonitorWnd: HWND;
 WM_MONITORMOUSEMOVE: UINT;
 {$ENDIF}
 MouseHookStruct: PMouseHookStruct absolute MouseHook;
begin
 {$IFDEF WIN32}
 { Access the shared data. Do check if SharedData is assigned, because under
   some circumstances the hook filter can be called after all processes have
   detached }
 if SharedData <> NIL then
 begin
   MonitorWnd := SharedData^.MonitorWnd;
   HookHandle := SharedData^.HookHandle;
   WM_MONITORMOUSEMOVE := SharedData^.WM_MONITORMOUSEMOVE;
 end
 else
 begin
   WM_MONITORMOUSEMOVE := 0;
   MonitorWnd := 0;
   HookHandle := 0; { It seems that this handle is not used in the CallNextHookEx
                      function anyway. Several sources on the microsoft web site
                      indicate this. }
 end;
 {$ENDIF}

 { If the value of Code is less than 0, we are not allowed to do anything }
 { except pass it on to the next hook procedure immediately.              }
 if (Code >= 0) and (MonitorWnd <> 0) then
 begin
   { This example sends the coordinates of all mouse move messages to the
     monitoring app (the one that installed the hook). }
   if (Msg = WM_MOUSEMOVE) and (MouseHookStruct <> NIL) then
     PostMessage(MonitorWnd, WM_MONITORMOUSEMOVE, MouseHookStruct^.pt.x,
       MouseHookStruct^.pt.y);

   { You could do any number of things here based on the different mouse
     messages...
   case Msg of:
     WM_LBUTTONDOWN:
       begin
       end;
     WM_LBUTTONUP:
       begin
       end;
     WM_LBUTTONDBLCLK:
       begin
       end;
     WM_RBUTTONDOWN:
       begin
       end;
     WM_RBUTTONUP:
       begin
       end;
     WM_RBUTTONDBLCLK:
       begin
       end;
     WM_MBUTTONDOWN:
       begin
       end;
     WM_MBUTTONUP:
       begin
       end;
     WM_MBUTTONDBLCLK:
       begin
       end;
     WM_MOUSEMOVE:
       begin
       end;
   end;}

   { If you handled the situation, and don't want Windows to process the }
   { message, do *NOT* execute the next line.  Be very sure this is what }
   { want, though.  If you don't pass on stuff like WM_MOUSEMOVE, you    }
   { will NOT like the results you get.                                  }
   Result := CallNextHookEx(HookHandle, Code, Msg, MouseHook);
 end
 else
   Result := CallNextHookEx(HookHandle, Code, Msg, MouseHook);
end;

{ Call InstallHook to set the hook. }
function InstallHook(SystemHook: boolean; TaskHandle: THandle;
 AMonitorWnd: HWND) : boolean; export;
 { This is really silly, but that's the way it goes.  The only way to get the  }
 { module handle, *not* instance, is from the filename.  The Microsoft example }
 { just hard-codes the DLL filename.  I think this is a little bit better.     }
 function GetModuleHandleFromInstance: THandle;
 var
   s: array[0..512] of char;
 begin
   { Find the DLL filename from the instance value. }
   GetModuleFileName(hInstance, s, sizeof(s)-1);
   { Find the handle from the filename. }
   Result := GetModuleHandle(s);
 end;
begin
 { Technically, this procedure could do nothing but call SetWindowsHookEx(),  }
 { but it is probably better to be sure about things, and not set the hook    }
 { more than once.  You definitely don't want your callback being called more }
 { than once per message, do you?                                             }
 {$IFDEF WIN32}
 Result := FALSE;
 if SharedData = NIL then
   exit
 else
   with SharedData^ do
   begin
 {$ENDIF}
     Result := TRUE;
     if HookCount = 0 then
     begin
       MonitorWnd := AMonitorWnd;
       if SystemHook then
         HookHandle := SetWindowsHookEx(WH_MOUSE, MouseHookCallBack, HInstance, 0)
       else
         { See the Microsoft KnowledgeBase, PSS ID Number: Q92659, for a
           discussion of the Windows bug that requires GetModuleHandle() to be
           used.                 }
         HookHandle := SetWindowsHookEx(WH_MOUSE, MouseHookCallBack,
                                        GetModuleHandleFromInstance, TaskHandle);
       if HookHandle <> 0 then
         inc(HookCount)
       else
         Result := FALSE;
     end
     else
       inc(HookCount);
 {$IFDEF WIN32}
   end;
 {$ENDIF}
end;

{ Call RemoveHook to remove the system hook. }
function RemoveHook: boolean; export;
begin
 { See if our reference count is down to 0, and if so then unhook. }
 Result := FALSE;
 {$IFDEF WIN32}
 if SharedData = NIL then
   exit
 else
   with SharedData^ do
   begin
 {$ENDIF}
     if HookCount < 1 then exit;
     Result := TRUE;
     dec(HookCount);
     if HookCount = 0 then
       Result := UnhookWindowsHookEx(HookHandle);
 {$IFDEF WIN32}
   end;
 {$ENDIF}
end;

{ Have we hooked into the system? }
function IsHookSet: boolean; export;
begin
 {$IFDEF WIN32}
 if SharedData = NIL then
   Result := FALSE
 else
   with SharedData^ do
 {$ENDIF}
     Result := (HookCount > 0) and (HookHandle <> 0);
end;

{$IFDEF WIN32}
{ Shared data management }
procedure AllocSharedData;
var
 Init: boolean;
begin
 if hMapObject = 0 then
 begin
   // Create a named file mapping object.
   hMapObject := CreateFileMapping(
     THandle($FFFFFFFF),  // use paging file
     NIL,                 // no security attributes
     PAGE_READWRITE,      // read/write access
     0,                   // size: high 32-bits
     SizeOf(TSharedData), // size: low 32-bits
     'DFSHookDLLSharedDataBlock'); // name of map object, THIS MUST BE UNIQUE!
   // The first process to attach initializes memory.
   Init := GetLastError <> ERROR_ALREADY_EXISTS;
   if hMapObject = 0 then exit;
   // Get a pointer to the file-mapped shared memory.
   SharedData := MapViewOfFile(
     hMapObject,     // object to map view of
     FILE_MAP_WRITE, // read/write access
     0,              // high offset:  map from
     0,              // low offset:   beginning
     0);             // default: map entire file
   if SharedData = NIL then exit;
   // Initialize memory if this is the first process.
   if Init then
     FillChar(SharedData^, SizeOf(TSharedData), 0);
   SharedData^.WM_MONITORMOUSEMOVE := RegisterWindowMessage(
     MESSAGE_MONITOR_MOUSE_MOVE);
 end;
end;

procedure FreeSharedData;
begin
 if hMapObject <> 0 then
   try
     try
       UnMapViewOfFile(SharedData);
       CloseHandle(hMapObject);
     except
     end;
   finally
     SharedData := NIL;
     hMapObject := 0;
   end;
end;

procedure EntryPointProc(Reason: Integer);
begin
 case Reason of
   DLL_PROCESS_DETACH: FreeSharedData;
   DLL_PROCESS_ATTACH: AllocSharedData;
   DLL_THREAD_ATTACH: {} ;
   DLL_THREAD_DETACH: {} ;
 end;
end;
{$ENDIF}


exports
 GetMonitorMouseMoveMsg,
 InstallHook,
 RemoveHook,
 IsHookSet,
 MouseHookCallBack;


{ Initialize DLL data. }
begin
 {$IFDEF WIN32}
 SharedData := NIL;
 hMapObject := 0;
 DllProc := @EntryPointProc;
 EntryPointProc(DLL_PROCESS_ATTACH); // Must call manually in the Delphi world
 {$ELSE}
 HookCount := 0;
 HookHandle := 0;
 WM_MONITORMOUSEMOVE := RegisterWindowMessage(MESSAGE_MONITOR_MOUSE_MOVE);
 {$ENDIF}
end.
--------------------------------------------------------------------------------

引用代码:

--------------------------------------------------------------------------------
unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 ExtCtrls;

type
 TForm1 = class(TForm)
   Image1: TImage;
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
   procedure Image1MouseUp(Sender: TObject; Button: TMouseButton;
     Shift: TShiftState; X, Y: Integer);
   procedure Image1Click(Sender: TObject);
   procedure Image1DblClick(Sender: TObject);
   procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
     Shift: TShiftState; X, Y: Integer);
 private
   { Private declarations }
 public
   { Public declarations }
   procedure WndProc(var Message: TMessage); override;
   procedure UpdateMousePosInfo(X, Y: integer);
 end;

var
 Form1: TForm1;
 ImageX : Integer; //图片的Left和Top距离鼠标按下点的长度
 ImageY : Integer;
 bIsDrag : boolean; //为true,表示允许拖动

implementation

uses Hookunit;

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
 bIsDrag := false; //初始状态为不可拖动
 InstallTaskHook(Handle); //安装鼠标钩子
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 RemoveHook; //卸载钩子
end;

procedure TForm1.WndProc(var Message: TMessage);
begin
 if Message.Msg = GetMonitorMouseMoveMsg then
   UpdateMousePosInfo(Message.WParam, Message.LParam)
 else
   inherited WndProc(Message);
end;

procedure TForm1.UpdateMousePosInfo(X, Y: integer);
var
 Temp : TPoint;
begin
 if bIsDrag then  //拖动图片
 begin
   Temp := ScreenToClient(Point(X,Y));
   Image1.Left := Temp.x - ImageX;
   Image1.Top := Temp.y - ImageY;
 end;
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
begin
 bIsDrag := false;
end;

procedure TForm1.Image1Click(Sender: TObject);
begin
   bIsDrag := false;
   Caption := 'Click';
end;

procedure TForm1.Image1DblClick(Sender: TObject);
begin
   bIsDrag := false;
   Caption := 'DoubleClick';
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
begin
 ImageX := X;
 ImageY := Y;
 bIsDrag := true;
end;

end.

来自: denvennew, 时间: 2004-10-25 0:18:55, ID: 2863977

我下载了gifimage,在所用到GIF文件的程序中添加了以上单元代码,但好象静态的GIF文件可以,动画的一样是死机,不知为何?谢谢!

问题讨论没有结束 ...

问题反馈 到论坛逛逛

上一个页面