I am looking for a alternative to Raize components' tab control.
I would like to have the ability to add close button at the top of each page and I want to use slanted tabs and colors on the tabs. Oh, and I'm using FireMonkey 2.
//I know that raize does not support firmonkey.
Firemonkey has it's own TTabControl in the Common Controls page (by default). You can style this using a TStylebook. For example, I'm quite confident it's possible to add a close button onto the tab itself.
After all, FMX is a vector-based framework and so all visual elements must exist within the style. You'll probably want to load a style into a TStylebook for this, as I'm unable to find a way to load the default style into one. Navigate down to tabitemstyle, and from there you'll be able to tweak the visual appearance of it. Simply add a close button as you'd like (alignment, layout, etc).
Back in your application code, you'll be looking to use the FindStyleResource routine in order to setup the code (XE2 uses FindBinding and as such you'd set the BindingName property instead). I'm going to assume your close button is called 'CloseButton' (without the quotes);
var
MyTab : TTabItem;
begin
MyTab := ((TabItem1 as TTabItem).FindStyleResource('CloseButton') as TButton).OnClick := TabClose;
end;
You'd want to add that code when you initially create the tab, or if creating all the tabs at design time, you'll want to run it at FormCreate. You're basically telling it that when CloseButton is clicked, that you want to call the notify event/procedure TabClose. This procedure is identical to a button click.
You could go so far as changing the StyleName property of the tab to be CloseButton+Index_of_tab.
Now, as for the code to close the tab itself, something like this untested example may work, though you'll want to iterate on it.
procedure TForm1.TabClose(Sender: TObject);
var
_mytab : Integer;
_activetab : Integer;
begin
_activetab := ((Sender as TTabItem).Parent as TTabControl).ActiveTab.Index;
_mytab := ((Sender as TTabItem).Parent as TTabControl).ActiveTab.Index;
((Sender as TTabItem).Parent as TTabControl).Tabs[_MyTab].Free;
((Sender as TTabItem).Parent as TTabControl).TabIndex := _activetab;
end;
Now, this is the clever part, and exploits the design of the framework. When you click on a style element that's inside another element, by default, it'll select the parent element. In this example, it'll select the tab that contains the close button the user clicked on. From this, it'll then close that tab (technically, it'll free it, I've not dealt with tabs much in development so you'll want to look into the proper method of 'closing' them).
There is one problem with this though; You'll probably want to find a better way of detecting the previously active tab if you wish to switch back to it. Right now, it'll simply open the tab that was after the tab you just closed (since the tabcount is now 1 less, the active tab index selects the next physical tab). You'll probably be able to do this by splitting the _activetab code off elsewhere.
I've done similar things with some of my own programs, and this is how I usually create 'hybrid' components. You're essentially exploiting the modular design of the framework to make it do what you want it to do, without having to rely on third party components.
Since FMX is a pretty young framework there aren't many 3rd party component vendors that support it yet.
I haven't seen any 3rd party TabControl component for FMX and a quick Google search suggests there aren't any. So you might be out of luck.
I know this is an old query but in case anyone is still looking for tabs with close buttons and slanted tab sides, check out TMSSoftware's TTMSFMXTabSet. I'm using it in a current development project and it is working fine.
Related
So in the program I'm writing, I'm using a TPageControl component with multiple tab sheets. I want to change the color of the tab of the sheet that is currently the active page on the PageControl and then have the color be reset after clicking on another page (then that tab will change due to it being the active page and so on...), to make navigating the page control a bit easier. But I'm not sure how to do it? I did try using the OnChange procedure of the page control and some code like this (I know it's horribly wrong but I ran out of ideas and google searches. It didn't work anyway.):
with pgcTabs.ActivePage do
begin
brush.color:=clBlue;
font.color:=clWhite;
end;
Thanks in advance for the help!
Kind Regards
PrimeBeat
You can't. Apart from using TTabSheet.Highlighted you have no control over how the tabs are displayed.
Or you have to fully draw it yourself, as usual as for most other controls: set TPageControl.OwnerDraw to true, then use the OnDrawTab event to paint whatever you want to. It should be identical to TTabControl.OnDrawTab (see answer). The drawbacks (pun intended) of painting everything yourself is usually all the extra work to do: checking dimensions, interpreting accelerator keys, respecting system colors and settings (i.e. LTR)...
In my opinion "just adding a color" has little to no value compared to how the control by default paints already with all its advantages and features. Consider color blindness: those people might rather have problems.
This is more like a theoretic question: I am curious why the Show method will not also restore the form (like wsNormal).
I know that Show does not set WindowState:
procedure TCustomForm.Show;
begin
Visible := True;
BringToFront;
end;
but why? I mean, I expect the form to actually show up on screen, when I call Show. Obviously, this will not ALWAYS happen. More exactly, it will not happen when the window is minimized.
So, what is the logic behind Show? Why they left out WindowsState?
Visibility and window state are simply independent properties. It is perfectly reasonably that you may wish to change one but not the other.
The design choice made by the VCL designers was to map the underlying Win32 library to the VCL in as fairly direct manner. This means that VCL designers can have flexibility to make their own choices. Were the VCL designed the way that you suggest it would make it much more difficult to change visibility without also changing window state, for example.
We are adding a help button to the toolbar of our application.
When the user clicks on this button, we need to load the help system for the control that they were on
For example, if they are on the address box of the contacts form, I need to load the help system for this using its context id
I was thinking about trying to mimic an F1 keypress which would then take care of the context id element of things and load the help file
However, I cant get this to work because it tries to load the help based on the active control not the one I was on, i.e. the contact address
Is there a way to do this? Essentially I need to send an F1 keypress from previously active control (assuming that the currently active control is my toolbar button)
We are using Delphi 2010
Cheers
Paul
I think you need a tool button OnClick handler that can be as simple as this:
procedure TMyForm.ToolButton1Click(Sender: TObject);
begin
if Assigned(ActiveControl) then begin
Application.HelpContext(ActiveControl.HelpContext);
end;
end;
What makes this work is the fact that the controls on a toolbar do not ever become the active control.
There's something wrong with your toolbar. The system toolbar doesn't ordinarily get the focus — it's never the active control. If you're using a real TToolBar and TToolButton, you won't have this problem. Even TSpeedButton won't have this problem. Use the right control for the job.
Also, don't try to "simulate" a keyboard event. Just call Application.HelpContext directly.
I have been trying to find a good-looking design using Aero in Delphi 2010. One of the obvious uses one sees, is where the glass frame is extended to include the OK/Cancel buttons at the bottom of the screen. I notice though that this doesn't look quite right in Delphi 2010 - there is a white border all around each button.
This image shows the problem: the top 3 buttons are from my app, the bottom two were taken from Paint.NET's Layer Properties dialog.
I tried various combinations of DoubleBuffered and a few combinations of placing the controls on other controls first, but the problem remains. Any ideas?
If no one has a clean solution, as a workaround use TBitBtn with DoubleBuffered = false.
It appears that the only workaround is owner-draw, or a third-party button control Check out the Glass Button by Roy Klever or, as stated in the QC entry linked below, TBitBtn with DoubleBuffered=false, which was the accepted answer above to this question.
This is a bug in Windows Aero DWM or else a bug in the windows common controls, or a bug in the way the VCL class hierachy handles common control window messages and painting when painting on glass. In short, windows common controls do not paint themselves properly on glass, or rather DWM composition (Aero) is broken. Surprise surprise.
The standard VCL button component uses the Window Class BUTTON from Windows Common Controls.
Note that TSpeedButton does not use the windows common control, and does not have this problem. however, it also does not accept focus.
It appears Embarcadero knows about this issue, it is QC # 75246, which is closed because it is really a bug in the common controls library, as Won't Fix, with the suggestion to use TBitBtn. Buttons are not alone, this is part of a group of QC reports including panels, and other common controls.
However I have a commercial TcxButton (part of developer express components) which accepts keyboard focus, and does not draw this glitch. Any code that uses the Win32 common control button control appears to have this problem. It may be possible that a low level Win32 API hacker might find a workaround for this. I am looking into it. This answer will be updated if I figure it out.
One interesting detail: TcxButton has three drawing styles, cxButton.LookAndFeel.Kind = {lfOffice11,lfFlat,lfStandard}. Selecting lfOffice11 adds this glitch back in. It looks like a strange interaction between the glass feature in aero in Vista/Win7 and the common control/xptheme button drawing code.
It may be that the only workaround is to use a completely app-drawn button control and to not use Windows common controls buttons, or any button control that relies upon the XP theme engine to draw buttons, on an aero glass pane.
Edit: July 28, someone at Embarcadero has closed the above QC Entry, which was a mistake. I am urging them to reopen it, if only to clarify if this is indeed a Windows bug in the common controls dll.
If you wish to play around, make a copy of the VCL source code for the TButton and TCustomButton classes from StdCtrls, as I have done here, modify CNCtlColorBtn, so that you force one of three things to happen - PerformEraseBackground, DrawParentBackground or inherited, and see the results. Interesting stuff.
procedure TCustomGlassButton.CNCtlColorBtn(var Message: TWMCtlColorBtn);
begin
PerformEraseBackground(Self, Message.ChildDC);
Message.Result := GetStockObject(NULL_BRUSH);
(*
with ThemeServices do
if ThemesEnabled then
begin
if (Parent <> nil) and Parent.DoubleBuffered then
PerformEraseBackground(Self, Message.ChildDC)
else
DrawParentBackground(Handle, Message.ChildDC, nil, False);
{ Return an empty brush to prevent Windows from overpainting we just have created. }
Message.Result := GetStockObject(NULL_BRUSH);
end
else
inherited;
*)
end;
Some interesting reading on Vista era glass/DWM/aero APIs (C++ developers blog)
Here I'm providing some code that makes TButton look right on Glass. Unfortunately it makes the form "click-throw", so I don't think it's a good idea. But maybe you can find a way to fix the form's "click-throw".
if you are able to use win32 api, try exploiting NM_CUSTOMDRAW notification (not ownerdraw), as i do (yes, buttons DO send it, including radio and checkboxes. For these it is best to use WM_CTLCOLORSTATIC though.). This is how it is done in C++, but the idea is the same. While my idea is good, it so happens that my buttons do DISAPPEAR once per program execution from the window, when they are customdrawn and i need to hover mouse over them so they are visible again. That's why i'm still looking for comments for this. Note that it is really difficult to reproduce disappearing buttons in one-form applications. I hovewer am experiencing this behaviour in every project.
case WM_NOTIFY:
switch(((LPNMHDR)lParam)->code){
case NM_CUSTOMDRAW:
{
NMHDR *nmh=(NMHDR*)lParam;
//these 6000 through 6004 are button identifiers assigned by me
if(nmh->idFrom >= 6000 && nmh->idFrom <= 6004){
switch(((LPNMCUSTOMDRAW)nmh)->dwDrawStage){
case CDDS_PREERASE:
//BackgroundBrush is a HBRUSH used also as window background
FillRect(((LPNMCUSTOMDRAW)nmh)->hdc, &((LPNMCUSTOMDRAW)nmh)->rc, BackgroundBrush);
break;
}
}
break;
}
break;
I have a Delphi 7 application that has two views of a document (e.g. a WYSIWYG HTML edit might have a WYSIWYG view and a source view - not my real application). They can be opened in separate windows, or docked into tabs in the main window.
If I open a modal dialog from one of the separate forms, the main form is brought to the front, and is shown as the selected window in the windows taskbar. Say the main form is the WYSIWYG view, and the source view is poped out. You go to a particular point in the source view and insert an image tag. A dialog appears to allow you to select and enter the properties you want for the image. If the WYSIWYG view and the source view overlap, the WYSIWYG view will be brought to the front and the source view is hidden. Once the dialog is dismissed, the source view comes back into sight.
I've tried setting the owner and the ParentWindow properties to the form it is related to:
dialog := TDialogForm.Create( parentForm );
dialog.ParentWindow := parentForm.Handle;
How can I fix this problem? What else should I be trying?
Given that people seem to be stumbling on my example, perhaps I can try with a better example: a text editor that allows you to have more than one file open at the same time. The files you have open are either in tabs (like in the Delphi IDE) or in its own window. Suppose the user brings up the spell check dialog or the find dialog. What happens, is that if the file is being editing in its own window, that window is sent to below the main form in the z-order when the modal dialog is shown; once the dialog is closed, it is returned to its original z-order.
Note: If you are using Delphi 7 and looking for a solution to this problem, see my answer lower down on the page to see what I ended up doing.
I'd use this code... (Basically what Lars said)
dialog := TDialogForm.Create( parentForm );
dialog.PopupParent := parentForm;
dialog.PopupMode := pmExplicit;
dialog.ShowModal();
I ultimately ended up finding the answer using Google Groups. In a nutshell, all the modal dialogs need to have the following added to them:
procedure TDialogForm.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_POPUP;
Params.WndParent := (Owner as TWinControl).Handle;
end;
I'm guessing this does the equivalent of Lars' and Marius' answers in Delphi 7.
Is the dialog shown using ShowModal or just Show? You should probably set the PopupMode property correct of the your dialog. pmAuto would probably your best choice. Also see if you need to set the PopupParent property.
First of all, I am not completely sure I follow, you might need to provide some additional details to help us understand what is happening and what the problem is. I guess I am not sure I understand exactly what you're trying to accomplish and what the problem is.
Second, you shouldn't need to set the dialog's parent since that is essentially what is happening with the call to Create (passing the parent). The dialogs you're describing sound like they could use some "re-thinking" a bit to be honest. Is this dialog to enter the properties of the image a child of the source window, or the WYSIWYG window?
I'm not sure I quite understand what you are getting at, but here's a few things I can suggest you can try...
This behaviour changes between different versions of Delphi. I'd suggest that this is due to the hoops they jumped through to support Windows Vista in Delphi 2007.
If you are using Delphi 2007, try removing the line from the project source file that sets the Application.MainFormOnTaskBar boolean variable.
With this removed, you should be able to use the various Form's BringToFront / SendToBack methods to achieve the Z-ordering that you are after.
I suspect that what you've discovered has been discussed on this link
Of course, I may have just missed your point entirely, so apologies in advance!