I want a TPageControl and some TTabSheets, with 'per tabsheet' tooltip hints visible as I hover over each tab in turn.
Is there any way of getting this effect in Delphi 2009?
Just hook the Page Control's Mouse Move event and use the TabAtPos property to determine which tab the mouse is hovering over. Then assign that tab's Hint to the Page Control's hint property.
procedure TForm.PageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
var
tabindex: integer;
begin
tabindex := PageControl.IndexOfTabAt(X, Y);
if (tabindex >= 0) and (PageControl.Hint <> PageControl.Pages[tabindex].Hint) then
begin
Application.CancelHint;
PageControl.Hint := PageControl.Pages[tabindex].Hint;
PageControl.ShowHint := true;
end;
end;
CancelHint/ShowHint will take care of updating the hint window when mouse moves directly from one tab to another.
Improved but ugly version below also temporarily changes HintPause to 0 when mouse is moved directly from tab to tab so that the hint is redisplayed immediately. (The "ugly" part of the solution goes to the Application.ProcessMessages call which forces hint messages to be processed before HintPause is restored.)
procedure TForm.PagesMouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
var
hintPause: integer;
tabindex: integer;
begin
tabindex := PageControl.IndexOfTabAt(X, Y);
if (tabindex >= 0) and (PageControl.Hint <> PageControl.Pages[tabindex].Hint) then
begin
hintPause := Application.HintPause;
try
if PageControl.Hint <> '' then
Application.HintPause := 0;
Application.CancelHint;
PageControl.Hint := PageControl.Pages[tabindex].Hint;
PageControl.ShowHint := true;
Application.ProcessMessages; // force hint to appear
finally Application.HintPause := hintPause; end;
end;
end;
To hide the hint on the main page body, assign the following method to the page control's OnMouseLeave event.
procedure TForm.PageMouseLeave(Sender: TObject);
begin
PageControl.Hint := '';
PageControl.ShowHint := false;
end;
In Raize Components, this can be accomplished by setting the trzpagecontrol.tabhints property to true. Good components can save you a lot of time (therefore money).
(just a happy customer, btw)
Update (in response to comment from #Rigel) from raize.com FAQ (Raize Components tab):
What happened to Raize Components?
Back in 2015 Embarcadero acquired Raize Components from us and
rebranded the product as the Konopka Signature VCL Controls (KSVC).
Initially they sold the product separately, but for the past several
releases of RAD Studio, the components have been available for free
through the GetIt Package Manager. Simply open the GetIt Package
Manager from the Delphi or C++Builder Tools menu and search for
“Konopka” to locate the installer. The component names, units, and
packages are the same as they were in Raize Components, just the
product name is different.
1 - fill in the .Hint property, and set the .ShowHint property to True for the PageControl (assuming each tabsheet has ParentShowHint set to true; otherwise you'll have to set each page individually).
2 - Assign this event to the PageControl's OnChange event handler:
procedure TForm1.PageControl1Change(Sender: TObject);
begin
PageControl1.Hint := PageControl1.ActivePage.Hint;
end;
After you do that, the hint will be whatever the active tab is. I am not sure how to make it change the hint based on where the mouse is hovering - that's an interesting phenomenon I've never noticed before, actually.
On the tPageControl.OnMouseMove find TabIndex by Pgctrl.IndexOfTabAt( X, Y ) and assign TabSheet hint to the tPageControl hint
Look here:
http://www.delphigroups.info/2/9/321680.html
Originally working on a C++ Builder 6 (!) project (so please forgive any typo in this transcript), I started with the answer of Gerard[1] and reduced the code as much as possible. To better control the calls of Application.CancelHint, I introduced the member FLastHintTabIndex, it must be initialized with -1.
procedure TForm1.PageControl1MouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
TabIndex: Integer;
begin
TabIndex := PageControl1.IndexOfTabAt(X, Y);
if FLastHintTabIndex <> TabIndex then
Application.CancelHint;
if TabIndex <> -1 then
PageControl1.Hint = PageControl1.Pages[TabIndex].Hint;
FLastHintTabIndex := TabIndex;
end;
[1]
my answer doesn't contain much new, but I find all that code and text too distracting.
Related
I would like to create a non visual component (like TTimer for example) that I can drop on the form and that I can set up directly from the Object Inspector, but I don't want to see its icon on the form (it'd just obstruct anything). For example TFloatAnimation works like this but I don't understand how.
The GExperts library (http://www.gexperts.org/) has a plug-in which can toggle the visibility
of non-visual components on a form, and it is apparently not Delphi-version-specific but it is
not exactly trivial.
The method which does this is
procedure THideNonVisualCompsExpert.ToggleNonVisualVisible(Form: TCustomForm);
const
NonVisualClassName = 'TContainer';
var
VisibleState: Boolean;
FormHandle: THandle;
CompHandle: THandle;
WindowClass: string;
FirstCompFound: Boolean;
WinControl: TWinControl;
ChildControl: TWinControl;
i: Integer;
begin
Assert(Assigned(Form));
Assert(Form.Handle > 0);
FirstCompFound := False;
WinControl := Form;
if InheritsFromClass(WinControl.ClassType, 'TWinControlForm') then
begin
for i := WinControl.ComponentCount - 1 downto 0 do
begin
if WinControl.Controls[i] is TWinControl then
begin
ChildControl := WinControl.Controls[i] as TWinControl;
if InheritsFromClass(ChildControl.ClassType, 'TCustomFrame') then
begin
WinControl := ChildControl;
Break;
end;
end;
end;
end;
FormHandle := GetWindow(WinControl.Handle, GW_CHILD);
CompHandle := GetWindow(FormHandle, GW_HWNDLAST);
VisibleState := False;
GxOtaClearSelectionOnCurrentForm;
while (CompHandle <> 0) do
begin
WindowClass := GetWindowClassName(CompHandle);
if AnsiSameText(WindowClass, NonVisualClassName) then
begin
if not FirstCompFound then
begin
VisibleState := not IsWindowVisible(CompHandle);
FirstCompFound := True;
end;
if VisibleState then
ShowWindow(CompHandle, SW_SHOW)
else
ShowWindow(CompHandle, SW_HIDE);
end;
CompHandle := GetWindow(CompHandle, GW_HWNDPREV);
end;
end;
in the unit GX_HideNonVisualComps.Pas.
As written, it toggles the visibility of all the non-visual components on the
target form, but looking at the code of the ToggleNonVisualVisible method it looks like it
ought to be possible (but I have not tried) to adapt it to operate on a selected component class and
force instances of the class to a non-visible state. Once you have done that, you would probably
need to experiment with how and when to invoke the method at design-time; if I was doing it, I would probably start
with somewhere like the target component's Loaded method.
(I would feel more comfortable posting this "answer" as a comment but obviously it would be too long)
I have thought about this. A Non Visual Component does not do any painting, in a Windows environment (like the IDE) it has no Window, and therefore cannot influence how the IDE chooses to render it.
One approach would be to derive from TWinControl, making your component a Visual Component, and then to ensure that it is not drawn. Try setting the positioning properties to be non-published, and when you are parented, always set your position outside the parent window. This means that your control is always clipped and never painted.
I haven't tried this, but I can see no reason why it wouldn't work.
You can also use this approach to have an apparently non visual component that renders information in the IDE at designtime, but not at runtime.
I wanna display hint just on mouse move, like in Winamp. No need to have focus on app. Thanks for help.
You can make the hint popup, but I'm not sure if you can do that if the application is not the focussed application.
This will show the hint for anything where the hint is set and ShowHint = True. But only if it is the focusseed Application. (As Sertac Akyuz said in a comment on the original post, VCL only does this for the currently active form).
procedure TForm1.ControlMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
aPoint: TPoint;
aControl: TControl;
begin
aControl := TControl(Sender);
if aControl.ShowHint = true then
begin
aPoint.X := X;
aPoint.Y := Y;
if Assigned(aControl.Parent) then
aPoint := aControl.ClientToParent(aPoint);
aPoint := ClientToScreen(aPoint);
Application.ActivateHint(aPoint);
end;
end;
Hope this helps.
There is a way you can detect if mouse cursor position is over some controll by periodically checking mouse cursor position in relation of that controls client rectangle. You can do this using Timer and next code:
procedure TForm4.Timer1Timer(Sender: TObject);
if Panel1.ClientRect.Contains(Panel1.ScreenToClient(Mouse.CursorPos)) then
begin
Form4.Caption := 'Panel1';
end
else if Panel2.ClientRect.Contains(Panel2.ScreenToClient(Mouse.CursorPos)) then
begin
Form4.Caption := 'Panel2';
end
else if Panel3.ClientRect.Contains(Panel3.ScreenToClient(Mouse.CursorPos)) then
begin
Form4.Caption := 'Panel3';
end
else if Panel4.ClientRect.Contains(Panel4.ScreenToClient(Mouse.CursorPos)) then
begin
Form4.Caption := 'Panel4';
end
else Form4.Caption := 'None';
There is probably some better solution by iterating through your forms component list or even better creating your own specific list for this.
Now the only problem is that hint is shown only for active applications. So if you want for hints to be shown even when your application isn't active you will have to make your own hint system (Creating a small form with hint text shown).
Finally it works now. I copied VCL.Forms.pas to project directory
removed there ForegroundTaskCheck like Sertac Akyuz said
var
HintInfoMsg: TCMHintInfo;
{$ENDIF}
begin
FHintActive := False;
HintInfo.ReshowTimeout := 0;
if FShowHint and (FHintControl <> nil) {and ForegroundTaskCheck(EnumAllWindowsOnActivateHint)} and
and most important thing is to add {$B-} in VCL.Forms.pas (without it many AV and crash)
unit Vcl.Forms;
{$B-}
Some background info. I work at a very small company who has recently upgraded Delphi from version 6 (!!!) to Rad Studio XE5 and things have certainly changed a lot in 10+ years. Most things seems to have been improved in the IDE and framework, but we're having big problems with the new VCL Styles feature. It's just very buggy and not up to par with the quality we were used to from Borland back in the day. We have done lots of tweaks and work arounds to get things working but one issue is really bugging me at the moment and it has to do with the preview form in FastReport 4.
The toolbar gets a white border around it.
Controls in the print dialog and others are misaligned or wrongly positioned
We really want to use VCL Styles to give our software a new fresh look, so we hope there is a solution to these problems.
Steps to reproduce the issues:
Create a new VCL Forms Application
Check a VCL Style in Project > Options > Application > Appearance, e.g. Sapphire Kamri.
Add a TfrxReport report Component to the form
Double click the component frxReport1 and add a Page Header band just to have some content
Add a TButton and in OnClick event, call frxReport1.ShowReport();
Run the program and click on the button. In the preview form you now see that the toolbar is surrounded by a white border which looks weird.
Click the leftmost print button to bring up the print dialog and you can see how the group boxes and cancel button is positioned outside of the client area.
Do you have any solutions or suggestions to solve the issues?
Edit: RRUZ gave a good answer, but there were some side effects to his solution to problem #1 so I decided to simplify the code and just paint the border around the toolbar manually. Like this:
procedure TToolBarStyleHookEx.PaintNC(Canvas: TCanvas);
begin
if TToolBar(Control).BorderWidth>0 then
begin
Canvas.Pen.Width := 4;
Canvas.Pen.Color := StyleServices.GetStyleColor(scWindow);
Canvas.Brush.Style := bsClear;
Canvas.Rectangle(2,2,Control.Width-2,Control.Height-1);
end;
inherited;
end;
Effectively both issues it seems VCL Styles bugs.
1) Q: The toolbar gets a white border around it.
A: The TToolBarStyleHook Style hook in not handling the BorderWidth property. so you must create a new style hook and override the PaintNC to overcome this issue.
type
TToolBarStyleHookEx = class(TToolBarStyleHook)
protected
procedure PaintNC(Canvas: TCanvas); override;
end;
{ TToolBarStyleHookEx }
procedure TToolBarStyleHookEx.PaintNC(Canvas: TCanvas);
var
Details: TThemedElementDetails;
LStyle: TCustomStyleServices;
R: TRect;
begin
if TToolBar(Control).BorderWidth>0 then
begin
LStyle := StyleServices;
R := Rect(0, 0, Control.Width, Control.Height);
Details.Element := teToolBar;
Details.Part := 0;
Details.State := 0;
if LStyle.HasTransparentParts(Details) then
LStyle.DrawParentBackground(Handle, Canvas.Handle, Details, False);
LStyle.DrawElement(Canvas.Handle, Details, R);
end;
inherited;
end;
and register like so
initialization
TCustomStyleEngine.RegisterStyleHook(TToolBar, TToolBarStyleHookEx);
2) Q : Controls in the print dialog and others are misaligned or wrongly positioned
A: It seems a issue related with the TFormStyleHook, you had 3 alternatives.
1) you can edit the frxPrintDialog unit and increase the width of the form.
2) you can patch the form style hook.
3) You can change the width of the print dialog in run-time.
Check this code which changes the width of the dialog in run-time using a HCBT_ACTIVATE hook
var
hhk: HHOOK;
function CBT_FUNC(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
const
ClassNameBufferSize = 1024;
var
hWindow: HWND;
RetVal : Integer;
ClassNameBuffer: Array[0..ClassNameBufferSize-1] of Char;
i : integer;
begin
Result := CallNextHookEx(hhk, nCode, wParam, lParam);
if nCode<0 then exit;
case nCode of
HCBT_ACTIVATE:
begin
hWindow := HWND(wParam);
if (hWindow>0) then
begin
RetVal := GetClassName(wParam, ClassNameBuffer, SizeOf(ClassNameBuffer));
if (RetVal>0) and SameText(ClassNameBuffer, 'TfrxPrintDialog') then
for i:= 0 to Screen.FormCount-1 do
if (SameText(Screen.Forms[i].ClassName, 'TfrxPrintDialog')) and (Screen.Forms[i].Width<=563) then
Screen.Forms[i].Width:=Screen.Forms[i].Width+8;
end;
end;
end;
end;
Procedure InitHook();
var
dwThreadID : DWORD;
begin
dwThreadID := GetCurrentThreadId;
hhk := SetWindowsHookEx(WH_CBT, #CBT_FUNC, hInstance, dwThreadID);
if hhk=0 then RaiseLastOSError;
end;
Procedure KillHook();
begin
if (hhk <> 0) then
UnhookWindowsHookEx(hhk);
end;
initialization
InitHook();
finalization
KillHook();
After of apply both fixes this will be the result
Note: please report these issues to the QC page of Embarcadero.
I have a TChart (Steema TeeChart included in Delphi IDE) component which may have up to 64 Chart Series (Stacked Area in my case). I need to display all existent series in chart, but Legend, unfortunately, doesn't show all of existing series, only some firsts of them 10-16 (see picture).
Is it possible somehow to Scroll Legend for viewing all existing series?
If not directly maybe some workaround?
used Delphi7, Chart v4
Here is my own implementation, based on TChart.OnMouseWheel event, simulating Legend scrolling (it is scrolled, but without any scroll bar - maybe it will be task for future):
procedure TForm1.Chart1MouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
function GetChartActiveSeriesCount(aChart: TChart): Integer;
var
iIdx: Integer;
begin
Result := 0;
for iIdx := 0 to aChart.SeriesCount-1 do
begin
if aChart.Series[iIdx].Active = True then
Inc(Result);
end;
end;
var
lCliMousePos: TPoint;
lActiveCount: Integer;
lChart: TChart;
begin
lChart := TChart(Sender);
lCliMousePos := lChart.ScreenToClient(MousePos);
if PtInRect(lChart.Legend.RectLegend, lCliMousePos) then
begin
if WheelDelta > 0 then
begin
if lChart.Legend.FirstValue > 0 then
lChart.Legend.FirstValue := lChart.Legend.FirstValue-1;
end
else
begin
lActiveCount := GetChartActiveSeriesCount(lChart);
if (lChart.Legend.FirstValue + lChart.Legend.NumRows) < lActiveCount then
lChart.Legend.FirstValue := lChart.Legend.FirstValue+1;
end;
end;
Handled := True;
end;
Also there are some tricks how to trigger TChart.OnMouseWheel event, because Tchart cannot get focus it is needed to play with Main Form OnMouseWheel event or WM_MOUSEWHEEL windows message. HowTos here:
http://delphi.about.com/od/delphitips2010/qt/delphi-redirect-mouse-wheel-control-under-the-mouse.htm or here: http://delphi.about.com/od/delphitips2010/qt/timage-handling-mouse-wheel-messages.htm
This is only possible with the Professional edition of TeeChart. It includes the Legend ScrollBar tool (TLegendScrollBar) for this purpose. Fully functional evaluation versions can be downloaded here.
I want to avoid mouse right click on the edit boxes of my application which I am doing in BDS 2006.
I googled about it and i found a code as follows.
noPopUp := TPopupMenu.create(Edit1);
Edit1.PopupMenu := noPopup;
This is written on form activate. It works fine for edit1, but there are many edit boxes on the form so i wrote a for loop,
for i := 0 to Self.ControlCount-1 do
begin
if Self.Controls[i].ClassName = 'TEdit' then
begin
noPopUp := TPopupMenu.create(Self.Controls[i]);
TEdit(Self.Controls[i]).PopupMenu := noPopup;
end;
end;
This works fine for the edit boxes whose parent is Form. But if there are edit boxes on groupboxes or panels then, these panels and groupboxes in turn children of the form.
So my question is how to disable mouse right click on the edit boxes when the parent is not the form?
This accepted answer allocate unnecessary memory . You can think then it causes memory leaks too, because the created TPopupMenu are never released. But the Create( AOwner) of each TPopupMenu prevent this, releasing this memory on TEdit's Free.
To avoid unnecessary memory alloc, try this:
procedure TForm1.MyContextPopup(Sender: TObject; MousePos: TPoint;
var Handled: Boolean);
begin
Handled := True;
end;
and in the loop:
for i := 0 to Self.ComponentCount-1 do
if Self.Components[i] is TEdit then
TEdit(Self.Components[i]).OnContextPopUp := MyContextPopup;
This is enought to do what you want!
Best regards!
The solution in not that far: substitute control with component, like this
for i := 0 to Self.ComponentCount-1 do
begin
if Self.Components[i].ClassName = 'TEdit' then
begin
noPopUp := TPopupMenu.create(Self.Components[i]);
TEdit(Self.Components[i]).PopupMenu := noPopup;
end;
end;