如何知道用户鼠标点击的位置在窗口(Form.ClientRect)之外-200分求助 ( 积分: 200 )

  • 主题发起人 主题发起人 yhli
  • 开始时间 开始时间
Y

yhli

Unregistered / Unconfirmed
GUEST, unregistred user!
问题描述:<br>用户选择执行画一条直线或选定一个Zoom Window(图形缩放常用),接下来应该用鼠标在当前窗口内选择两点,但此时,用户并没立即这样做,而是又点击了菜单栏上其他控件,如菜单按钮或ComboBox等,此时,程序死了(实际上在等待),因此,我想在用户点击菜单栏上其他控件时,立即结束取点动作。请高手指点迷津!<br><br>以下是两段程序共参考:<br>第一段:取点程序<br>function TMain.GetPoint(csPrompt:String; var pt:TPoint):Boolean;<br>var<br> &nbsp; bResult &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;:Boolean ;<br> &nbsp; mMsg &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; :MSG;<br> &nbsp; i &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;:integer;<br>begin<br> &nbsp; StatusBar1.Panels[2].Text:=csPrompt;<br> &nbsp; bResult &nbsp; &nbsp; &nbsp; := FALSE;<br> &nbsp; bGettingPoint := TRUE;<br> &nbsp; &nbsp;// Wait until the message of TJ_POINT_INPUTTED or TJ_POINT_CANCEL is received.<br> &nbsp; while (GetMessage(mMsg, Handle, 0, 0)) do<br> &nbsp; begin<br> &nbsp; &nbsp; &nbsp;i := mMsg.message;<br> &nbsp; &nbsp; &nbsp;if i = TJ_POINT_INPUTTED then<br> &nbsp; &nbsp; &nbsp;begin<br> &nbsp; &nbsp; &nbsp; &nbsp; if (Ortho=1) and &nbsp;(iCurrentPoint=1) then<br> &nbsp; &nbsp; &nbsp; &nbsp; begin<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pt.x:=Round(ptPrev[1].x);<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pt.y:=Round(ptPrev[1].y);<br> &nbsp; &nbsp; &nbsp; &nbsp; end<br> &nbsp; &nbsp; &nbsp; &nbsp; else pt := MouseDownPoint;<br> &nbsp; &nbsp; &nbsp; &nbsp; bResult := TRUE;<br> &nbsp; &nbsp; &nbsp; &nbsp; break;<br> &nbsp; &nbsp; &nbsp;end;<br> &nbsp; &nbsp; &nbsp;if i = TJ_POINT_CANCEL then<br> &nbsp; &nbsp; &nbsp;begin<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ClearRubber();<br> &nbsp;bResult := FALSE;<br> &nbsp;// post a canceled (not cancel) message<br> &nbsp;PostMessage(Handle,TJ_POINT_CANCELED,WPARAM(0),LPARAM(0));<br> &nbsp;break;<br> &nbsp; &nbsp; &nbsp;end;<br> &nbsp; &nbsp; &nbsp;//如果用户在窗口外点击鼠标(如何判断???),则中止退出,<br> &nbsp; &nbsp; &nbsp;//即 PostMessage(Handle,TJ_POINT_CANCELED,WPARAM(0),LPARAM(0));<br> &nbsp; &nbsp; &nbsp;TranslateMessage(mMsg);<br> &nbsp; &nbsp; &nbsp;DispatchMessage(mMsg);<br> &nbsp; end;<br> &nbsp; bGettingPoint := FALSE;<br> &nbsp; result:=bResult;<br>end;<br><br>第二段:鼠标在当前窗口按键处理程序,按右键结束取点动作<br>procedure TMain.FormMouseDown(Sender: TObject; Button: TMouseButton;<br> &nbsp;Shift: TShiftState; X, Y: Integer);<br>begin<br> &nbsp;MouseDownPoint := Point(X,Y);<br> &nbsp;if Button = mbLeft then<br> &nbsp;begin<br> &nbsp; &nbsp;rMouseDown:=True;<br> &nbsp; &nbsp;ClearRubber();<br> &nbsp; &nbsp;if IsGettingPoint() then<br> &nbsp; &nbsp; &nbsp; &nbsp; PostMessage(handle, TJ_POINT_INPUTTED, WPARAM(0), LPARAM(1))<br> &nbsp;end<br> &nbsp;else if Button = mbRight then<br> &nbsp;begin<br> &nbsp; &nbsp; &nbsp; if bGettingPoint then<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PostMessage(Handle,TJ_POINT_CANCEL, WPARAM(0),LPARAM(0));<br> &nbsp;end;<br>end;
 
问题描述:<br>用户选择执行画一条直线或选定一个Zoom Window(图形缩放常用),接下来应该用鼠标在当前窗口内选择两点,但此时,用户并没立即这样做,而是又点击了菜单栏上其他控件,如菜单按钮或ComboBox等,此时,程序死了(实际上在等待),因此,我想在用户点击菜单栏上其他控件时,立即结束取点动作。请高手指点迷津!<br><br>以下是两段程序共参考:<br>第一段:取点程序<br>function TMain.GetPoint(csPrompt:String; var pt:TPoint):Boolean;<br>var<br> &nbsp; bResult &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;:Boolean ;<br> &nbsp; mMsg &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; :MSG;<br> &nbsp; i &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;:integer;<br>begin<br> &nbsp; StatusBar1.Panels[2].Text:=csPrompt;<br> &nbsp; bResult &nbsp; &nbsp; &nbsp; := FALSE;<br> &nbsp; bGettingPoint := TRUE;<br> &nbsp; &nbsp;// Wait until the message of TJ_POINT_INPUTTED or TJ_POINT_CANCEL is received.<br> &nbsp; while (GetMessage(mMsg, Handle, 0, 0)) do<br> &nbsp; begin<br> &nbsp; &nbsp; &nbsp;i := mMsg.message;<br> &nbsp; &nbsp; &nbsp;if i = TJ_POINT_INPUTTED then<br> &nbsp; &nbsp; &nbsp;begin<br> &nbsp; &nbsp; &nbsp; &nbsp; if (Ortho=1) and &nbsp;(iCurrentPoint=1) then<br> &nbsp; &nbsp; &nbsp; &nbsp; begin<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pt.x:=Round(ptPrev[1].x);<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;pt.y:=Round(ptPrev[1].y);<br> &nbsp; &nbsp; &nbsp; &nbsp; end<br> &nbsp; &nbsp; &nbsp; &nbsp; else pt := MouseDownPoint;<br> &nbsp; &nbsp; &nbsp; &nbsp; bResult := TRUE;<br> &nbsp; &nbsp; &nbsp; &nbsp; break;<br> &nbsp; &nbsp; &nbsp;end;<br> &nbsp; &nbsp; &nbsp;if i = TJ_POINT_CANCEL then<br> &nbsp; &nbsp; &nbsp;begin<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ClearRubber();<br> &nbsp;bResult := FALSE;<br> &nbsp;// post a canceled (not cancel) message<br> &nbsp;PostMessage(Handle,TJ_POINT_CANCELED,WPARAM(0),LPARAM(0));<br> &nbsp;break;<br> &nbsp; &nbsp; &nbsp;end;<br> &nbsp; &nbsp; &nbsp;//如果用户在窗口外点击鼠标(如何判断???),则中止退出,<br> &nbsp; &nbsp; &nbsp;//即 PostMessage(Handle,TJ_POINT_CANCELED,WPARAM(0),LPARAM(0));<br> &nbsp; &nbsp; &nbsp;TranslateMessage(mMsg);<br> &nbsp; &nbsp; &nbsp;DispatchMessage(mMsg);<br> &nbsp; end;<br> &nbsp; bGettingPoint := FALSE;<br> &nbsp; result:=bResult;<br>end;<br><br>第二段:鼠标在当前窗口按键处理程序,按右键结束取点动作<br>procedure TMain.FormMouseDown(Sender: TObject; Button: TMouseButton;<br> &nbsp;Shift: TShiftState; X, Y: Integer);<br>begin<br> &nbsp;MouseDownPoint := Point(X,Y);<br> &nbsp;if Button = mbLeft then<br> &nbsp;begin<br> &nbsp; &nbsp;rMouseDown:=True;<br> &nbsp; &nbsp;ClearRubber();<br> &nbsp; &nbsp;if IsGettingPoint() then<br> &nbsp; &nbsp; &nbsp; &nbsp; PostMessage(handle, TJ_POINT_INPUTTED, WPARAM(0), LPARAM(1))<br> &nbsp;end<br> &nbsp;else if Button = mbRight then<br> &nbsp;begin<br> &nbsp; &nbsp; &nbsp; if bGettingPoint then<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PostMessage(Handle,TJ_POINT_CANCEL, WPARAM(0),LPARAM(0));<br> &nbsp;end;<br>end;
 
用SetCapture跟ReleaseCapture试试看
 
delphibbs_lee,你好!<br>具体如何实现,能否详细说明一下。
 
建议参考Delphi7/Demos/Doc/Graphex<br>换个角度思考<br>在&quot;画布&quot;的MouseDown事件激活上面事件MouseUp事件结束
 
在&quot;画布&quot;的MouseDown事件激活上面事件MouseUp事件结束 <br> 嗯, 这样出来是方便多了。 但是对用户来说是痛苦, ACAD不是这样做的。<br>------------<br>两个方法:<br> &nbsp;1. 用Mouse Hook, 不用什么 MouseDown<br> &nbsp;2. 拦截CM_CancelMode。(就是在你的画布外面单击任何控件产生的)。
 
unit Unit1;<br><br>interface<br><br>uses<br> &nbsp;Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,<br> &nbsp;Dialogs;<br><br>type<br> &nbsp;TForm1 = class(TForm)<br> &nbsp; &nbsp;procedure FormMouseDown(Sender: TObject; Button: TMouseButton;<br> &nbsp; &nbsp; &nbsp;Shift: TShiftState; X, Y: Integer);<br> &nbsp; &nbsp;procedure FormMouseUp(Sender: TObject; Button: TMouseButton;<br> &nbsp; &nbsp; &nbsp;Shift: TShiftState; X, Y: Integer);<br> &nbsp;private<br> &nbsp; &nbsp;{ Private declarations }<br> &nbsp;public<br> &nbsp; &nbsp;{ Public declarations }<br> &nbsp;end;<br><br>var<br> &nbsp;Form1: TForm1;<br><br>implementation<br><br>{$R *.dfm}<br><br>procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;<br> &nbsp;Shift: TShiftState; X, Y: Integer);<br>begin<br> &nbsp;SetCapture(Handle);<br>end;<br><br>procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;<br> &nbsp;Shift: TShiftState; X, Y: Integer);<br>begin<br> &nbsp;//此时鼠标不管是否是在Form1的ClientRect里面都会触发<br> &nbsp;ReleaseCapture;<br> &nbsp;ShowMessage('Up');<br>end;<br><br>end.
 
首先,谢谢大家对这个问题的关注!<br>xuxiaohan:<br> &nbsp;不知如何拦截CM_CancelMode?(就是在你的画布外面单击任何控件产生的)。<br><br>delphibbs_Lee:<br>关键是用户没有在窗口中点击,而是在窗口外面,如ToolBar上点击,所以SetCapture...好像没用。<br><br>我目前采取的方法是,用户一旦选择了画线(取窗)按钮,为避免在窗外点击,将鼠标移动范围限制在窗口内,但我觉得如有其他解决办法,最好不要这样限制,鼠标移动到窗口边缘,被阻挡而不能外移动,有种粘滞或费劲的感觉。
 
你的程序应该有一个主窗体吧, 但你单击主窗体的任何一个位置,主窗体就会发送一个CM_CANCALMODE 给窗体中拥有 Focus 的 控件(只有TWinControl才能拥有Focus, 你的画布也应该用TWinControl),<br><br>这是源码:<br>procedure TCustomForm.SendCancelMode(Sender: TControl);<br>begin<br> &nbsp;if Active and (ActiveControl &lt;&gt; nil) then<br> &nbsp; &nbsp;[blue]ActiveControl.Perform(CM_CANCELMODE, 0, Longint(Sender))[/blue];<br> &nbsp;if (FormStyle = fsMDIForm) and (ActiveMDIChild &lt;&gt; nil) then<br> &nbsp; &nbsp;ActiveMDIChild.SendCancelMode(Sender);<br>end;<br><br>另外的源码:<br>procedure TControl.WMLButtonDown(var Message: TWMLButtonDown);<br>begin<br> &nbsp;SendCancelMode(Self);<br> &nbsp;inherited;<br> &nbsp;if csCaptureMouse in ControlStyle then MouseCapture := True;<br> &nbsp;if csClickEvents in ControlStyle then Include(FControlState, csClicked);<br> &nbsp;DoMouseDown(Message, mbLeft, []);<br>end;<br><br>procedure TControl.WMNCLButtonDown(var Message: TWMNCLButtonDown);<br>begin<br> &nbsp;SendCancelMode(Self);<br> &nbsp;inherited;<br>end;<br><br>procedure TControl.SendCancelMode(Sender: TControl);<br>var<br> &nbsp;Control: TControl;<br>begin<br> &nbsp;Control := Self;<br> &nbsp;while Control &lt;&gt; nil do<br> &nbsp;begin<br> &nbsp; &nbsp;if Control is TCustomForm then<br> &nbsp; &nbsp; &nbsp;TCustomForm(Control).SendCancelMode(Sender);<br> &nbsp; &nbsp;Control := Control.Parent;<br> &nbsp;end;<br>end;<br><br>看到了没有,所以,只要鼠标按下,只要是按在窗口的范围内(不管是什么控件 toolbar,按钮, 菜单-菜单其实是不一样的,下面我再解释), 窗口的activteControl 都会接收到一个 CM_CANCELMODE 消息。<br><br>procedure TControl.WMContextMenu(var Message: TWMContextMenu);<br>var<br> &nbsp;Pt, Temp: TPoint;<br> &nbsp;Handled: Boolean;<br> &nbsp;PopupMenu: TPopupMenu;<br>begin<br> &nbsp;if Message.Result &lt;&gt; 0 then Exit;<br> &nbsp;if csDesigning in ComponentState then<br> &nbsp;begin<br> &nbsp; &nbsp;inherited;<br> &nbsp; &nbsp;Exit;<br> &nbsp;end;<br><br> &nbsp;Pt := SmallPointToPoint(Message.Pos);<br> &nbsp;if InvalidPoint(Pt) then<br> &nbsp; &nbsp;Temp := Pt<br> &nbsp;else<br> &nbsp;begin<br> &nbsp; &nbsp;Temp := ScreenToClient(Pt);<br> &nbsp; &nbsp;if not PtInRect(ClientRect, Temp) then<br> &nbsp; &nbsp;begin<br> &nbsp; &nbsp; &nbsp;inherited;<br> &nbsp; &nbsp; &nbsp;Exit;<br> &nbsp; &nbsp;end;<br> &nbsp;end;<br><br> &nbsp;Handled := False;<br> &nbsp;DoContextPopup(Temp, Handled);<br> &nbsp;Message.Result := Ord(Handled);<br> &nbsp;if Handled then Exit;<br><br> &nbsp;PopupMenu := GetPopupMenu;<br> &nbsp;if (PopupMenu &lt;&gt; nil) and PopupMenu.AutoPopup then<br> &nbsp;begin<br> &nbsp; &nbsp;[gold]SendCancelMode(nil);[/gold] 窗口菜单发出的。<br> &nbsp; &nbsp;PopupMenu.PopupComponent := Self;<br> &nbsp; &nbsp;if InvalidPoint(Pt) then<br> &nbsp; &nbsp; &nbsp;Pt := ClientToScreen(Point(0, 0));<br> &nbsp; &nbsp;PopupMenu.Popup(Pt.X, Pt.Y);<br> &nbsp; &nbsp;Message.Result := 1;<br> &nbsp;end;<br><br> &nbsp;if Message.Result = 0 then<br> &nbsp; &nbsp;inherited;<br>end;<br><br>至于如何拦截这个 CM_CANCELMODE, 你可以用继承新控件做为你的画布,当然也可以用 TWndMethod,将画布的 WindowProc 用新的代替。<br>例子:<br><br>constructor TPopupWindow.Create(AOwner: TComponent);<br>begin<br> &nbsp;inherited Create(AOwner);<br> &nbsp;FButton := TWinControl(AOwner);<br> &nbsp;ControlStyle := ControlStyle + [csNoDesignVisible, csReplicatable,<br> &nbsp; &nbsp;csAcceptsControls];<br> &nbsp;Ctl3D := False;<br> &nbsp;ParentCtl3D := False;<br> &nbsp;Visible := False;<br> &nbsp;Parent := FButton;<br> &nbsp;width:=160;<br> &nbsp;height:=160;<br> &nbsp;FoldBtnWndProc:=FButton.WindowProc;<br> &nbsp;[blue]FButton.WindowProc:=btnWndProc;[/blue]<br>end;<br><br>procedure TPopupWindow.btnWndProc(var message: TMessage);<br>begin<br> &nbsp;with message do<br> &nbsp;if (Msg=CM_CANCELMODE) or (Msg=WM_KILLFOCUS) then hide;<br> &nbsp;FoldBtnWndProc(Message); //这个必须的。<br>end;<br><br>我已经讲的很详细了。。。。。
 

Similar threads

后退
顶部