Delphi: how to handle click on PageControl's empty space? - delphi

I'm using Delphi 7.
I want to react on click(left) on empty space of PageControl -- on area righter than the last tab shown. How can i do that?

You can handle the click at the parent control of the PageControl. F.i. if the PageControl is placed on a form, the form's 'MouseDown' events will be called for that specified region. The reason is that the PageControl returns HTTRANSPARENT for hit test messages for that region, so the mouse messages is directed to the control beneath it.
If that's not OK, you can change how WM_NCHITTEST is handled, for example by subclassing the control, or in a derived control:
type
TMyPageControl = class(TPageControl)
protected
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
end;
procedure TMyPageControl.WMNCHitTest(var Message: TWMNCHitTest);
begin
inherited;
if Message.Result = HTTRANSPARENT then
Message.Result := HTCLIENT;
end;
then, the control's OnMouseDown event will be fired. Of course you could test for the region before modifying the message's return value, this example was only to show how it would work.

Related

How to be notified at the END of Horizontal Scrolling in TListView?

In a VCL Application, I am trying to be notified when I END the horizontal scrolling in a TListView with this interposer class code:
type
TListView = class(Vcl.ComCtrls.TListView)
private
procedure WMNotify(var AMessage: TWMNotify); message WM_NOTIFY; // used for other purposes
procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
procedure WMVScroll(var Msg: TWMHScroll); message WM_VSCROLL;
procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
protected
procedure CreateWnd; override;
end;
implementation
procedure TListView.CNCommand(var Message: TWMCommand);
begin
case Message.NotifyCode of
EN_VSCROLL: CodeSite.Send('TListView.CNCommand: EN_VSCROLL'); // does not work
EN_HSCROLL: CodeSite.Send('TListView.CNCommand: EN_HSCROLL'); // does not work
end;
inherited ;
end;
procedure TListView.WMHScroll(var Msg: TWMHScroll);
begin
CodeSite.Send('TListView.WMHScroll: WM_HSCROLL'); // does work
inherited;
end;
procedure TListView.WMVScroll(var Msg: TWMHScroll);
begin
CodeSite.Send('TListView.WMVScroll: WM_VSCROLL'); // does work
inherited;
end;
However, only WHILE scrolling I get constantly notified by WM_HSCROLL and WM_VSCROLL generating a lot of messages.
But I need to be notified only at the END of the horizontal scrolling! Is this possible?
The comments given to the Q are very relevant.
First, as Remy Lebeau stated, the WM_HSCROLL message tells you if the operation is done:
procedure TListView.WMHScroll(var Msg: TWMHScroll);
begin
inherited;
if Msg.ScrollCode = SB_ENDSCROLL then
ShowMessage('End scroll')
end;
However, this only lets you know when a scrolling operation initiated by the horizontal scroll bar is done. Currently, this includes these scroll-bar operations:
Thumb released
Scroll bar LEFT or RIGHT button clicked
Scroll bar empty area clicked (for page scroll)
Scroll bar context menu item selected
But there are many other ways the list view control can be scrolled, which have nothing to do with the scroll bar:
Using your mouse's horizontal scrolling wheel (or the standard vertical wheel if there is only a horizontal scroll bar and no vertical one)
Using your keyboard's left and right arrow keys (or Ctrl+Left/Right for page scroll)
With MultiSelect = True, making a selection rectangle with the mouse (start dragging outside any list-view item)
Hence, by reacting only to WM_HSCROLL, you will not detect these scrolling events. Almost certainly, you want to react when the scroll position has changed, no matter how it was changed.
And, as AmigoJack wrote, it is not absolutely clear what "end" means (other than when you release the mouse button after having dragging the scroll bar thumb). For instance, if you scroll using your mouse wheel, is the result a single large scroll operation or several small ones? After all, in any case, even thumb tracking, the control repaints itself at every small step.
So probably your best option is to use
procedure TListView.CNNotify(var Message: TMessage);
begin
inherited;
if PNMHDR(Message.lParam).code = LVN_ENDSCROLL then
// Scrolled
end;
According to the documentation,
Notifies a list-view control's parent window when a scrolling operation ends.
Notice that the documentation says that the notification is sent when the operation ends. Still, you will find that it is sent for every small update while you drag the scroll bar thumb. As mentioned above, this is reasonable: scrolling has indeed been performed after every such small step.

Disable form restoring on title doubleclick

Create an empty Delphi VCL project
Remove all BorderIcons of main form
Set WindowState to wsMaximized
Run application. Main window appears maximized.
Double click on window title. Main window restores it's size and there is no possibility to maximize it again.
How to prevent window restoring on title double click without hiding title bar?
You can intercept the restore and additionally the move system commands to prevent restoring by dragging the caption.
type
TForm1 = class(TForm)
protected
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
...
procedure TForm1.WMSysCommand(var Message: TWMSysCommand);
begin
case Message.CmdType and $FFF0 of
SC_MOVE, SC_RESTORE: Exit;
end;
inherited;
end;
I tested some solutions and the one that worked was:
Set Align property to alClient;
Remove biMaximize from BorderIcons property;
Let WindowState wsNormal (default).
Answer has been edited!
If I got you right, you might want to forbid double-click by title bar in order to prevent restoring form to its original size. You can do that by intercepting WM_NCLBUTTONDBLCLK.
In the example below I've overridden WndProc method of main form and hook forementioned message.
procedure TForm1.WndProc(var Message: TMessage);
begin
case Message.Msg of
WM_NCLBUTTONDBLCLK:
begin
case TWMNCHitMessage(Message).HitTest of
HTCAPTION:
Exit
else // Another HitTest-codes are handled here
Inherited WndProc(Message);
end;
end
else
Inherited WndProc(Message);
end;
end;
Important note
Although now you cannot restore maximized form by double-clicking, it still can be restored just by moving it while mouse is captured by title bar. At least, on Windows 7 this effect is presented.
Steps to reproduce:
Run application;
Press left mouse button while it hovered over title bar;
Don't release LMB and move mouse softly - now form restores its size.
Addendum
Fixed bug with incorrect handling another non-client HitTest-codes excepting HTCAPTION (thanks to user Dsm for pointing this out!).

Delphi TrackBar On Stop

I am making a basic music player and am using a TTrackBar as the progress in the song. As well I want to make it so u can drag the bar and fast forward the song.
Currently I have an OnChange event with the following line:
MediaPlayer1.position := TrackBar1.value... (with proper casting)
but what happens is that it skips the song along as I drag making a choppy sound as it plays the song at certain random points along the way.
What I really want is for when the user stops dragging the song should change. What event is this? The onStopDrop even doesn't do the trick..
The scroll notification messages arrive though WM_HSCROLL or WM_VSCROLL, depending on the orientation of your track bar. These surface in the VCL control as CN_HSCROLL and CN_VSCROLL. You need to handle these messages and ignore message for which the scroll code is TB_THUMBTRACK to prevent the control to fire the OnChange event when the user drags the slider.
For example, here is an interposer control that does what you need:
type
TTrackBar = class(Vcl.ComCtrls.TTrackBar)
protected
procedure CNHScroll(var Message: TWMHScroll); message CN_HSCROLL;
procedure CNVScroll(var Message: TWMVScroll); message CN_VSCROLL;
end;
implementation
procedure TTrackBar.CNHScroll(var Message: TWMHScroll);
begin
if Message.ScrollCode = TB_THUMBTRACK then
Message.Result := 0
else
inherited;
end;
procedure TTrackBar.CNVScroll(var Message: TWMVScroll);
begin
if Message.ScrollCode = TB_THUMBTRACK then
Message.Result := 0
else
inherited;
end;

What should I do not to allow a window to be activated? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to prevent form from being activated when the users clicks on it?
What I exactly mean, is that I want to create a popup window that exactly looks like a Tooltip window. I'm using ShowWindow and SetWindowPos to show and set its position. I already tried SW_SHOWNOACTIVE and it works perfect for the time when the popup appears; but when I click on the popup window, it gets focused, and I don't want this to happen. Also, when this popup is visible no matter it's focused or not, no message is sent to the window placed behind it.
I'm actually writing an Object Inspector component and for long values, it has to show a tooltip when mouse hovers a long value. I want this tooltip to look like a common one. I don't want to use Windows Tooltips not Delphi Tooltip directly. I want to use my own window.
P.S. I thought that this problem is common , and I searched, but I couldn't find an answer that exactly matches my question.
Thanks in advance.
Javid
Try this:
TMyTooltipWindow = class(TCustomControl)
private
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
procedure WMMouseActivate(var Message: TWMMouseActivate); message WM_MOUSEACTIVATE;
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
procedure TMyTooltipWindow.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := WS_POPUP or WS_BORDER;
Params.ExStyle := WS_EX_TOOLWINDOW;
Params.WindowClass.style := Params.WindowClass.style + CS_SAVEBITS;
end;
procedure TMyTooltipWindow.WMMouseActivate(var Message: TWMMouseActivate);
begin
inherited;
Message.Result := MA_NOACTIVATE;
end;
procedure TMyTooltipWindow.WMNCHitTest(var Message: TWMNCHitTest);
begin
// this will make your window transparent for clicks
Message.Result := HTTRANSPARENT;
end;
This will not allow to activate the window with mouse.
Also having a look at THintWindow in Controls.pas might be helpful.

TPageControl tab area OnMouseEnter OnMouseLeave events

I need to catch the "OnMouseEnter" and "0nMouseLeave" for a certain area of the TPageControl component. With that specific area I mean the whole "tab header" rectangle.
The problem is, that the page control doesn't catch the messages (I'm catching internal control messages CM_MOUSEENTER and CM_MOUSELEAVE) in the "empty" space.
The aim for me is to draw a small arrow in the right empty side when user hovers in the red framed area (and drawing is just piece of cake) and erase it when leaves this area. And I'm don't care about the overflow of the tabs (which causes to draw scrolling double button) - that will never happen.
Here is the working piece of code, but it's not the clear solution and I don't like it. There must be another (more clean) way to do it.
type
TPageControl = class(ComCtrls.TPageControl)
protected
procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
end;
procedure TPageControl.CMMouseLeave(var Message: TMessage);
begin
inherited;
Canvas.TextOut(Width - 130, 5, 'CMMouseLeave'); // display result
end;
procedure TPageControl.WMNCHitTest(var Message: TWMNCHitTest);
var TabHeaderRect: TRect;
begin
if Message.Result = 0 then // if Message.Result = HTNOWHERE ...
begin
TabHeaderRect := ClientRect;
TabHeaderRect.Bottom := Top + 21;
if PtInRect(TabHeaderRect, ScreenToClient(Point(Message.XPos, Message.YPos))) then
Canvas.TextOut(Width - 130, 5, 'WMNCHitTest '); // display result
Message.Result := HTCLIENT;
end
else
inherited;
end;
Obviously, the empty space does not belong to the control's client area and so the control doesn't get any mouse-related Windows messages for that area. You will have to use the form's mouse events. Or put the page control inside a panel (using alClient) and use the panel's mouse events.
If you need this more than once, you could create a new component that does exactly that (combine a panel and a page control to achieve the desired behaviour).
Are you sure you're handling OnMouseEnter/OnMouseLeave for the page control itself, and not for the TTabSheet instance that it contains?

Resources