TSaveTextFileDialog and Vcl Styles - delphi

I'm using the TSaveTextFileDialog component in Delphi XE3, but when a Vcl Style is enabled the encoding combobox is draw using the current vcl style.
How i can fix this, I mean disable the vcl style for the combobox?

The parent class (TOpenTextFileDialog) of the TSaveTextFileDialog component adds a set of Vcl components to implement the Encodings and EncodingIndex properties, you can disable the Vcl styles on these Vcl controls using the StyleElements property. unfortunately these components are private so you need a little hack in order to gain access and disable the Vcl Styles.
Here you have two options.
Using a class helper.
You can introduce a helper function to get the Panel component which contains the Vcl controls of the dialog.
type
TOpenTextFileDialogHelper=class helper for TOpenTextFileDialog
function GetPanel : TPanel;
end;
function TOpenTextFileDialogHelper.GetPanel: TPanel;
begin
Result:=Self.FPanel;
end;
then you can write a method to disable the Vcl Styles, like so :
procedure DisableVclStyles(const Control : TControl);
var
i : Integer;
begin
if Control=nil then
Exit;
Control.StyleElements:=[];
if Control is TWinControl then
for i := 0 to TWinControl(Control).ControlCount-1 do
DisableVclStyles(TWinControl(Control).Controls[i]);
end;
And finally use on this way
DisableVclStyles(SaveTextFileDialog1.GetPanel);
SaveTextFileDialog1.Execute;
RTTI
Another option is use the RTTI to access the private Vcl components.
var
LRttiContext : TRttiContext;
LRttiField :TRttiField;
begin
LRttiContext:=TRttiContext.Create;
for LRttiField in LRttiContext.GetType(SaveTextFileDialog1.ClassType).GetFields do
if LRttiField.FieldType.IsInstance and LRttiField.FieldType.AsInstance.MetaclassType.ClassNameIs('TPanel') then
DisableVclStyles(TPanel(LRttiField.GetValue(SaveTextFileDialog1).AsObject));
SaveTextFileDialog1.Execute;
end;

Related

How to apply a custom style to a custom Firemonkey component using Delphi Seattle

I have a custom Delphi component created for Firemonkey (fmx). I now need to apply my custom style to the component. The style is saved in a resource. Previously this was done in the GetStyleObject method by calling TStyleManager.LoadFromResource.
This method (LoadFromResource) does not exist anymore in Delphi 10 Seattle for the Firemonkey framework.
My code in XE7 was working through the LoadFromResource:
function TFMXPic.GetStyleObject: TFmxObject;
var
style : string;
begin
if (StyleLookup = '') then
begin
style := GetClassStyleName;
Result := TControl(TStyleManager.LoadFromResource(HInstance,
style, RT_RCDATA));
Exit;
end;
Result := inherited GetStyleObject;
end;
How do I achieve this in Delphi 10 Seattle?
first, I don't think it is correct to check the StyleLookup. This property tells the component to look for this specific style name in the stylebook.
Then, you try to load a style file at component level. FMX does work like this. You have a style book, which loads the style file and then each component in a form uses this book to locate the style name as defined by the stylelookup value.
Out of my head, this course of actions should do the job:
Add the style file in the resources of your project as you have already done. Say you have a style called "mycomponent" for your component
Add a stylebook in the form
in the OnCreate even of the form, load the resource file to a TResourceStream and then load the last to the stylebook using TStyleBook.LoadFromStream
Now you can access the style by setting the StyleLookup='mycomponent' property of your component
Hope this helps.
I found a solution. Thank you guys at TMS software. The TStyleStreaming class should be used instead of TStyleManager class. I modified my code as follow (all is now working)
function TMyComponent.GetStyleObject: TFmxObject;
var
style : string;
begin
if (StyleLookup = '') then
begin
style := GetClassStyleName;
Result := TControl(TStyleStreaming.LoadFromResource(HInstance,
style, RT_RCDATA));
Exit;
end;
Result := inherited GetStyleObject;
end;

Display a warning when dropping a component on a form at design time

I'm tidying up components used in a large legacy project, I've eliminated about 90 of 220 custom components, replacing them with standard Delphi controls. Some of the remaining components require a significant amount work to remove which I don't have available. I would like to prevent anyone from making additional use of some of these components and was wondering if there was a way of showing a message if the component is dropped on the form at design time - something like "Don't use this control, use x or y instead".
Another possibility would to hide the control on the component pallet (but still have the control correctly render on the form at design time).
There is protected dynamic method TComponent.PaletteCreated, which is called only in one case: when we add this component to a form from component palette.
Responds when the component is created from the component palette.
PaletteCreated is called automatically at design time when the component has just been created from the component palette. Component writers can override this method to perform adjustments that are required only when the component is created from the component palette.
As implemented in TComponent, PaletteCreated does nothing.
You can override this method to show warning, so it will alert the user just one time, when he tries to put it to form.
UPDATE
I couldn't make this procedure work in Delphi 7, XE2 and Delphi 10 Seattle (trial version), so it seems that call to PaletteCreated from IDE is not implemented.
I sent report to QC:http://qc.embarcadero.com/wc/qcmain.aspx?d=135152
maybe developers will make it work some day.
UPDATE 2
There are some funny workarounds, I've tried them all this time, works normally. Suppose that TOldBadButton is one of components that shouldn't be used. We override 'Loaded' procedure and WMPaint message handler:
TOldBadButton=class(TButton)
private
fNoNeedToShowWarning: Boolean; //false when created
//some other stuff
protected
procedure Loaded; override;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
//some other stuff
end;
and implementation:
procedure TBadOldButton.Loaded;
begin
inherited;
fNoNeedToShowWarning:=true;
end;
procedure TOldBadButton.WMPaint(var Message: TWMPAINT);
begin
inherited;
if (csDesigning in ComponentState) and not fNoNeedToShowWarning then begin
Application.MessageBox('Please, don''t use this component','OldBadButton');
fNoNeedToShowWarning:=true;
end;
end;
The problem is, this works only for visual components. If you have custom dialogs, imagelists etc, they never get WMPaint message. In that case we can add another property, so when it is shown in object inspector, it calls getter and here we display warning. Something like this:
TStupidOpenDialog = class(TOpenDialog)
private
fNoNeedToShowWarning: boolean;
function GetAawPlease: string;
procedure SetAawPlease(value: string);
//some other stuff
protected
procedure Loaded; override;
//some other stuff
published
//with name like this, probably will be on top in property list
property Aaw_please: string read GetAawPlease write SetAawPlease;
end;
implementation:
procedure TStupidOpenDialog.Loaded;
begin
inherited;
fNoNeedToShowWarning:=true; //won't show warning when loading form
end;
procedure TStupidOpenDialog.SetAawPlease(value: string);
begin
//nothing, we need this empty setter, otherwise property won't appear on object
//inspector
end;
function TStupidOpenDialog.GetAawPlease: string;
begin
Result:='Don''t use this component!';
if (csDesigning in ComponentState) and not fNoNeedToShowWarning then begin
Application.MessageBox('Please, don''t use this component','StupidOpenDialog');
fNoNeedToShowWarning:=true;
end;
end;
Older versions of Delphi always scroll object inspector to the top when new component is added from palette, so our Aaw_please property will surely work. Newer versions tend to start with some chosen place in property list, but non-visual components usually have quite a few properties, so it shouldn't be a problem.
To determine when the component is first created (dropped on the form)?
Override "CreateWnd" and use the following if statement in it:
if (csDesigning in ComponentState) and not (csLoading in ComponentState) then
// We have first create
More detail here >>
Link

How to load custom cursor in Firemonkey?

I need to use custom cursor in my Firemonkey desktop project.
I can use LoadCursorFromFile in VCL project to load a custom cursor in my project.
I have tried to do the same for Firemonkey but it is not loading the cursor.
Is there any working way to achieve loading custom cursors in Firemonkey?
uses Winapi.Windows;
procedure Tform1.Button1Click(Sender: TObject);
const mycursor= 1;
begin
Screen.Cursors[mycursor] := LoadCursorFromFile('C:\...\Arrow.cur');
Button1.Cursor := mycursor;
end;
I only did this for the Mac, but the general idea is that you implement your own IFMXCursorService. Keep in mind that this pretty much an all or nothing approach. You'll have to implement the default FMX cursors, too.
type
TWinCursorService = class(TInterfacedObject, IFMXCursorService)
private
class var FWinCursorService: TWinCursorService;
public
class constructor Create;
procedure SetCursor(const ACursor: TCursor);
function GetCursor: TCursor;
end;
{ TWinCursorService }
class constructor TWinCursorService.Create;
begin
FWinCursorService := TWinCursorService.Create;
TPlatformServices.Current.RemovePlatformService(IFMXCursorService);
TPlatformServices.Current.AddPlatformService(IFMXCursorService, FWinCursorService);
end;
function TWinCursorService.GetCursor: TCursor;
begin
// to be implemented
end;
procedure TWinCursorService.SetCursor(const ACursor: TCursor);
begin
Windows.SetCursor(Cursors[ACursor]); // you need to manage the Cursors list that contains the handles for all cursors
end;
It might be a necessary to add a flag to the TWinCursorService so that it will prevent the FMX framework to override your cursor.
Timing is important when registering your own cursor service. It will have to be done after FMX calls TPlatformServices.Current.AddPlatformService(IFMXCursorService, PlatformCocoa);
Unfortunately, FireMonkey does not support custom cursors. This has already been filed as a feature request in Quality Portal:
RSP-17651 Cannot load custom cursors in Firemonkey.
With that said, the code you showed would not work in VCL. LoadCursorFromFile() returns an HCURSOR handle, but the TControl.Cursor property expects an index value from the TCursor enum instead. They are not the same thing. When loading a custom cursor, you must add it to the TScreen.Cursors[] list. This is clearly stated in the documentation:
Vcl.Controls.TControl.Cursor
The value of Cursor is the index of the cursor in the list of cursors maintained by the global variable, Screen. In addition to the built-in cursors provided by TScreen, applications can add custom cursors to the list.
Vcl.Forms.TScreen.Cursors
Custom cursors can be added to the Cursors property for use by the application or any of its controls. To add a custom cursor to an application, you can ...:
...
2. Declare a cursor constant with a value that does not conflict with an existing cursor constant.
...
4. Set the Cursors property, indexed by the newly declared cursor constant, to the handle obtained from LoadCursor.
For example:
const
mycursor: TCursor = 1; // built-in values are <= 0, user-defined values are > 0
procedure Tform1.Button1Click(Sender: TObject);
begin
Screen.Cursors[mycursor] := LoadCursorFromFile('C:\...\Arrow.cur');
Button1.Cursor := mycursor;
end;

Skinning of TOpenDialog and TOpenPictureDialog with VCL styles

Is it possible to skin the TOpenDialog and the TOpenPictureDialog with VCL syles?
The short answer is No, currently using Delphi XE2 or XE3 is not possible apply directly the Vcl Styles to a non VCL form (or to forms created outside of a VCL Application).
Now the long answer, is technically possible apply the Vcl Styles to these kind of dialogs, but this require a lot of work (believe me is a lot of work).
The key is using a WH_CBT Hook, detecting the HCBT_CREATEWND code and then checking if the class of the window is #32770 (the class for a dialog box.) from here you can replace the window procedure using the SetWindowLong function with the GWL_WNDPROC index.
That was the easy part, now which you have the control of the messages sent by the windows dialog
you must iterate over the child controls and replace the window procedure again using the
GWL_WNDPROC index. This can be done creating Wrapper class (this is the hard work) for each control used in a windows dialog (button, syslistview32, Combobox and so on)
This a sample of definition for a Wrapper class for the syslistview32 windows class.
TListViewWnd = class(TCustomListView)
private
FNewWndProc : Pointer;
FOrgWndProc : Pointer;
Fhwnd: THandle;
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
procedure DestroyWnd; override;
procedure WndProc(var Message: TMessage); override;
public
constructor Create(hwnd: THandle);
destructor Destroy;override;
end;
Finally you can use the already existing VCL Styles hooks defined for the Vcl Controls like the TListView on this way
TStyleManager.Engine.RegisterStyleHook(TListViewWnd, TListViewStyleHook);
I' ve already done part of the tasks described above, but is not finished yet, due which this kind of project require a lot of time.
For any interested the VCL Styles Utils Project now supports dialogs
You can find more information about this feature on this blog post.

TJvDockServer and dockable controls

I'm programming in Delphi (BDS 2006) and the JVCL library, using the docking modules. I have one problem - if the control has properties DragKind = dkDock and DragMode = dmAutomatic, then inexplicably TJvDockServer component takes the controls are both clients and provides docking. This is wrong, because, as I found out, JVCL's docking functions normally only control class TForm which contain a component class TJvDockClient. I would like to know whether it is possible in some way to prevent TJvDockServer from docking controls whose class is different from TForm? During a typical docking in Delphi for each event is called OnGetSiteInfo dock and it is possible to filter clients, but there is no such event in TJvDockServer.
The property DragKind and DragMode are standard VCL properties. Docking is built into the VCL, and from looking at it, it seems to work pretty good without any Jedi Code involved.
The ability to dock something other than a form, is already built into the VCL. Therefore that you find this inexplicable suggests to me that you thought Jedi added docking to the VCL. No, it just added some pretty things like "tabbed notebook docking" and "conjoined areas" with fake window titlebars.
That being said, Forms are also inheriting from TCustomControl, and any TCustomControl can in fact, be docked. And just like the VCl lets you drag and dock and land on top of TPanels. Okay it's a quirky feature, that your panel can turn into a form on you at runtime, but if you don't believe me, try it. It's the VCL doing this to you, not Jedi.
If in your wisdom, you want to block anything that is not a TForm, I thought that you can.
Surely you can right? Update. Yes you can. OnDockOver works fine to block docking on any panel you want to block docking on. The trick with the Jedi JvDockPanels is that you don't see them at designtime, so you need to access their events by hooking them up in code, at runtime.
Just like regular TPanels, JvDockPanels have a TPanel.OnDockOver event, and if you want to check the thing you're docking, and set the Accept to false, it will be prevented from docking.
Okay, this works:
type
TCustomControlAccess = class(TCustomControl);
procedure TMainForm.FormCreate(Sender: TObject);
begin
TCustomControlAccess(dockServer.TopDockPanel).OnDockOver := MyDockOverEvent;
TCustomControlAccess(dockServer.CustomDockPanel).OnDockOver := MyDockOverEvent;
...
end;
The JvDockPanel.OnDockOver panel events DO fire, but you need to resort to a hack like the above hack, to actually handle the events yourself.
Update previously thought there was no way to block this. But I was wrong. Figured it out.
while i cannot reproduce exactly your behaviour in Delphi XE2, generally i seem manage to block VCL-frag-n-drop for JediVCL components.
Maybe it is not the best possible way, but i don't know which were original ideas of the framework creator.
http://wiki.delphi-jedi.org/wiki/JVCL_Help:TJvDockServer claims only forms should be docked. Did not enforced that, just hardwired JVCL check routine to be always called.
unit JvDockSupportControl;
....
TJvDockCustomControl = class(TJvCustomControl)
....
protected
procedure GetSiteInfo(Client: TControl; var InfluenceRect: TRect;
MousePos: TPoint; var CanDock: Boolean); override;
...........
function TJvDockCustomControl.GetJvDockManager: IJvDockManager;
begin
// Result := IJvDockManager(DockManager);
DockManager.QueryInterface(IJvDockManager, Result);
end;
procedure TJvDockCustomControl.GetSiteInfo(Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
var jdm: IJvDockManager; idm: IDockManager;
begin
idm := DockManager;
if nil <> idm then
idm.QueryInterface(IJvDockManager, jdm);
if nil = jdm
then CanDock := false
else jdm.GetSiteInfo(Client,InfluenceRect, MousePos, CanDock);
end;
unit JvDockTree;
.....
procedure TJvDockTree.GetSiteInfo(Client: TControl;
var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
CanDock := IsDockable(DockSite, Client);
If CanDock then begin
GetWindowRect(DockSite.Handle, InfluenceRect);
InflateRect(InfluenceRect, DefExpandoRect, DefExpandoRect);
end;
end;
http://issuetracker.delphi-jedi.org/view.php?id=5271
http://issuetracker.delphi-jedi.org/view.php?id=5974

Resources