Delphi XE2: Reloading a custom VCL style from file? - delphi

I'm loading a custom style from file using:
TStyleManager.LoadFromFile(filename)
When the file is changed I want to load it again. But if I try that I get a EDuplicateStyleException because the style is already registered.
Is there a way to unload a style so I can load it again? The typical case for this is that you are making changes to a custom style and want to see it in action without restarting the whole application.

After scanning the sources I guess that is not possible in a straight forward way. Your only chance might be to implement some dirty hack.
Whatever you do, you should write a QC for this. Embarcadero could implement to reload the file if the style already exists instead of raising an exception. That would look like a natural behaviour to me.

Check this project vcl styles utils, one of the features exposed is the capacity of unload a vcl style. Just include the Vcl.Styles.Ext unit in your project and then use this code.
TStyleManager.RemoveStyle('Carbon');

Another idea:
This might work. Partial code for simplicity. In the code below, you first get a handle to the already registered Style. I guess then, you can dispose and re-assign the pointer with the one you loaded from the file. I believe the exception only show when you try to apply the style, not when you load it. Forgive me if I am wrong.
var
StyleName: String;
Style : TStyleManager.TStyleServicesHandle;
FileName : String;
begin
StyleName := 'Obsidian'; // or another style name
FileName := 'obsidian.vsf'; // or any other valid style file name
Style := TStyleManager.Style[ StyleName];
if Assigned( Style) then // style already registered
begin
TStyleManager.TrySetStyle( StyleName);
// insert other processing here
end
else // style not registered
begin
if TStyleManager.IsValidStyle( FileName) then
begin
Style := TStyleManager.LoadFromFile( FileName);
if Assigned( Style) then
begin
// insert other processing here, such as
// TStyleManager.SetStyle( Style);
end;
end;
end;
end;

Try this:
procedure TfrmMain.Button11Click(TObject *Sender);
var
MyStyle TCustomStyleServices;
const
usStylePath= 'c:\Users\Public\Documents\Embarcadero\Studio\19.0\Styles\vcl\MINE.vsf';
begin
if TStyleManager.IsValidStyle(usStylePath)
begin
// Get current style
MyStyle:= TStyleManager.Style["Emerald"]; // this will return a TCustomStyleServices obj
if (MyStyle <> NULL)
begin
// Set default Windows style (no style)
TStyleManager.SetStyle(TStyleManager.SystemStyle);
// Remove it
TStyleManager.UnRegisterStyle(MyStyle);
end;
// Load style from disk
TStyleManager.LoadFromFile(usStylePath);
TStyleManager.SetStyle(Emerald");
end;
end;
Note: I never complied the code. But it should work.
Anyway, you should use RRuz's library. He knows a lot about these VCL styles.

You could make a copy of each style in another file with different name of the style. Then you could load it's copy as a workaround. If you really need original then you can load it after loading copy.

Related

Odd behaviour when adding a toolbutton to the delphi ide

I was trying out some things and wanted to make a delphi IDE extension.
My basic idea was expanding the ToDo list feature that is currently in the IDE.
Step one was adding a toolbutton to the IDE which would open a form showing the todo items.
But I noticed some weird things that I hopefully caused myself since that would mean it can be easily fixed.
I am adding my toolbutton to the CustomToolbar, which is the one with the blue questionmark (see screenshot later)
The thing that happens: I install my package and the button is added with the correct image, right next to the existing button.
Now I close the modal form with the installed packages and then the blue questionmark changes.
Don't mind the icon I used, I will use a different one eventually but ok.
So basicly the existing item changes to my own icon but disabled for some reason. And I can't figure out why this happens.
As suggested in the guide I found online I used a TDatamodule to implement my code.
My code:
procedure TDatamoduleToDoList.Initialize;
var
LResource, LhInst: Cardinal;
begin
LhInst := FindClassHInstance(Self.ClassType);
if LhInst > 0 then
begin
LResource := FindResource(LhInst, 'icon', RT_Bitmap);
if LResource > 0 then
begin
FBMP := Vcl.Graphics.TBitmap.Create;
FBMP.LoadFromResourceName(LhInst, 'icon');
end
else
DoRaise('Resource not found');
end
else
DoRaise('HInstance Couldn''t be found');
FToDoAction := TTodoAction.Create(Self);
FToDoAction.Category := actionCat;
FToDoAction.ImageIndex := FIntaServices.ImageList.Add(FBMP, nil);
FToDoAction.Name := 'my_very_own_action_man';
end;
procedure TDatamoduleToDoList.DataModuleCreate(Sender: TObject);
begin
//Create extension
if Supports(BorlandIDEServices, INTAServices, FIntaServices) then
begin
Initialize;
if FToDoAction <> nil then
FCustBut := TSpeedButton(FIntaServices.AddToolButton(sCustomToolBar, 'CstmToDoList', FToDoAction))
else
DoRaise('Initialize failed');
end
else
DoRaise('Something went wrong');
end;
DoRaise is my own procedure that simply destroys all of my objects and raises an exception, did this to prevent mem leaks in the ide.
But, I think, I don't do anything weird but yet this problem occurs.
So I'm hoping someone here might have done something simular and sees the error in my code.
Thanks in advance.
P.s. if you need any more info or see the rest of the unit let me know and ill put the entire unit on github or something like that.
Edit:
Thanks to #Uwe Raabe I managed to solve this problem.
The problem was found in the comments of INTAServices.AddImages
AddImages takes all the images from the given image list and adds them
to the
main application imagelist. It also creates an internal mapping array from the
original image indices to the new indices in the main imagelist. This
mapping is used by AddActionMenu to remap the ImageIndex property of the
action object to the new ImageIndex. This should be the first method
called when adding actions and menu items to the main application window.
The return value is the first index in the main application image list of
the first image in the source list. Call this function with an nil
image list to clear the internal mapping array. Unlike the AddImages function from
the ancestor interface, this version takes an Ident that allows the same base index
to be re-used. This is useful when the IDE implements demand-loading of
personalities so that the images will only get registered once and the same image
indices can be used.
The solution eventually was adding my image to a local imagelist which was added to the imagelist of IntaServices
Code:
procedure TDatamoduleToDoList.DataModuleCreate(Sender: TObject);
begin
//Create extension
if Supports(BorlandIDEServices, INTAServices, FIntaServices) then
begin
Initialize;
if FToDoAction <> nil then
begin
FCustBut := TSpeedButton(FIntaServices.AddToolButton(sCustomToolBar, 'CstmToDoList', FToDoAction));
FToDoAction.ImageIndex := FIntaServices.AddImages(FImages);//This is the fix
end
else
DoRaise('Initialize failed');
end
else
DoRaise('Something went wrong');
end;
You are not supposed to fiddle around with the INTAServices.ImageList directly. Instead use either INTAServices.AddMasked or INTAServices.AddImages (in case you have a local imagelist in your datamodule).
You can safely use the INTAServices.ImageList to be connected to your controls, but you should neither Add nor Delete the images in it directly.

CHM file not displaying correctly when Delphi VCL style active

My Delphi application includes a help file that the user can call from anywhere in the application (well... that is, for all the parts I've written so far...)
It also includes the ability for the user to switch from the regular style to another VCL style from a list.
When no style is applied, the help file displays normally like this :
But as soon as a VCL style is active, the Help file does not display correctly anymore, like this :
Is this due to the way I declare the HelpFile on main Form creation like this (path being a global variable pointing to the main exe folder):
Application.HelpFile := path+'Help\D.R.A.M.A. 2.0 Help.chm';
or is this a known problem that can not be solved ?
SIDE NOTE : the help is called on helpContext should that be important to mention and the HtmlHelpViewer is added to the uses clause.
This answer was taken from https://forums.embarcadero.com/thread.jspa?threadID=227785 and I've confirmed works very well.
Drop a TApplicationEvents component onto the applications main form.
Implement the OnHelp event of that component as this:
function TfmMain.ApplicationEvents1Help(Command: Word; Data: NativeInt; var CallHelp: Boolean): Boolean;
begin
CloseHelpWnd;
Result := ShellExecute(0,'open','hh.exe',
PWideChar('-mapid '+IntToStr(Data)
+' ms-its:'+Application.HelpFile),
nil,SW_SHOW) = 32;
CallHelp := false;
end;
On the main form, implement the CloseHelpWnd method as this:
procedure TfmMain.CloseHelpWnd;
var
HlpWind: HWND;
const
HelpTitle = 'Your help file title';
begin
HlpWind := FindWindow('HH Parent',HelpTitle);
if HlpWind <> 0 then PostMessage(HlpWind,WM_Close,0,0);
end;
You would replace 'Your help file title' with the title of your help file. This is the window caption title when you open the help file directly.
In the FormDestroy event for the main form, include a call to
CloseHelpWnd;
So far we've not seen any issues with the above method, and because we are running the help file in a separate process, it is not affected by the VCL Styles problems evident in Delphi 10.2 Tokyo.
NOTE: It does not have to be the applications main form, but it must be a form that is created before the help system is needed and remains instantiated while the application is running. In our case, we did it on a common resources form and then all programs we rebuilt with the new form had the help problem resolved.
NOTE: You still need to set the Application.HelpFile property as normal, but you don't need to include the HtmlHelpViewer unit in the Uses clause.

how to get the selected text from acropdf component to an edit directly with Delphi 7

I study preparing a dictionary programme with delphi. So far I have solved my problems about Word documents but I've got some problem about PDF documents.
I imported and installed the AcroPdf component with Delphi 7 and I want to get the word (or text) which was selected by dblclicking by user from pdf document which was viewed by the ACROPDF component in Delphi. If I can get it I'll send it the dictionary database directly.
If you help me I'll be glad. Thank you...
Remzi MAKAK
The following shows one way to get the selected text from a Pdf document which is
open in Adobe Acrobat Professional (v.8, English version).
Update The original version of this answer neglected to check the Boolean result of calling MenuItemExecute and specified the wrong argument to it. Both these points are fixed in the updated version of this answer. It turned out that the reason the call to MenuItemExecute was failing was that it is essential to call BringToFront on the Acrobat document before trying to copy the text selected in to to the clipboard.
Create a new Delphi VCL project.
In D7's IDE go to Projects | Import Type Library, and in the Import Type Library pop-up, scroll down until you see something like "Acrobat (Version 1.0) in the list of files, and
"TAcroApp, TAcroAVDoc..." in the Class names box. That is the one you need to import. Click the Create unit button/
In the project's main form file
a. Make sure it USES the Acrobat_Tlb.Pas unit from step 2. You may need to add the path to wherever you saved Acrobat_Tlb.Pas to the SearchPath of your project.
b. Drop a TButton on the form, name it btnGetSel. Drop a TEdit on the form and name it edSelection
Edit the source code of your main form unit as shown below.
Set a debugger breakpoint on Acrobat.MenuItemExecute('File->Copy'); Do not set a breakpoint within the GetSelection procedure as this is likely to defeat the call to BringToFront in it.
Close any running instance of Adobe Acrobat. Check in Task Manager that there are no hidden instances of it running. The reason for these step is to make sure that when you run your app, it "talks" to the instance of Acrobat that it starts, not another one.
Compile and run your app. Once the app and Acrobat are open, switch to Acrobat, select some text, switch back to your app and click the btnGetSel button.
Code:
uses ... Acrobat_Tlb, ClipBrd;
TDefaultForm = class(TForm)
[...]
private
FFileName: String;
procedure GetSelection;
public
Acrobat : CAcroApp;
PDDoc : CAcroPDDoc;
AVDoc : CAcroAVDoc;
end;
[...]
procedure TDefaultForm.FormCreate(Sender: TObject);
begin
// Adjust the following path to suit your system. My application is
// in a folder on drive D:
FFileName := ExtractfilePath(Application.ExeName) + 'Printed.Pdf';
Acrobat := CoAcroApp.Create;
Acrobat.Show;
AVDoc := CoAcroAVDoc.Create;
AVDoc.Open(FileName, FileName); // := Acrobat.GetAVDoc(0) as CAcroAVDoc; //
PDDoc := AVDoc.GetPDDoc as CAcroPDDoc;
end;
procedure TDefaultForm.btnGetSelClick(Sender: TObject);
begin
GetSelection;
end;
procedure TDefaultForm.GetSelection;
begin
// call this once some text is selected in Acrobat
edSelection.Text := '';
if AVDoc.BringToFront then // NB: This call to BringToFront is essential for the call to MenuItemExecute('Copy') to succeed
Caption := 'BringToFront ok'
else
Caption := 'BringToFront failed';
if Acrobat.MenuItemExecute('Copy') then
Caption := 'Copy ok'
else
Caption := 'BringToFront failed';
Sleep(100); // Normally I would avoid ever calling Sleep in a Delphi
// App's main thread. In this case, it is to allow Acrobat time to transfer the selected
// text to the clipboard before we attempt to read it.
try
edSelection.Text := Clipboard.AsText;
except
end;
end;

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;

How to set THTTPRio.Converter.Options to soLiteralParams in OnBeforeExecuteEvent

This refer to the post Delphi SOAP Envelope and WCF.
Could someone please post a sample code which can show me how to set soLiteralParams in THTTPRio.Converter.Options in Delphi 7. I have the following code currently.
I have drag-dropped a HTTPRIO component into the document which has created a line HTTPRIO1: THTTPRIO at the beginning of the code. I basically want to understand how I set soLiteralParams in the above component. Following is the code I am trying to execute which is giving me error.
procedure TForm1.CleanUpSOAP(const MethodName: String; var SOAPRequest: WideString);
var RIO: THTTPRIO;
begin
//The following line is giving error
// RIO.Converter.options := [soLiteralParams];
end;
In the above code I have declared a variable RIO of the type THTTPRIO, which I am not sure is correct.
Just guessing, as you provide very little information in your question.
Use the variable assigned to the component you dropped on your form. Don't declare a new local one (which you never created anyway). To set the Converter.Options in code, you'll need to add OPToSOAPDomConv to your uses clause.
implementation
uses
OPToSOAPDomConv;
// BTW, this name might not be a good one if it's the
// OnBeforeExecute event handler as that isn't
// clear from the name.
procedure TForm1.CleanUpSOAP(const MethodName: String; var SOAPRequest: WideString);
begin
// Note this clears any previous options!
HTTPRIO1.Converter.Options := [soLiteralParams];
// If you want to keep the previous options instead of replacing them
// HTTPRIO1.Converter1.Options := HTTPRIO1.Converter1.Options + [soLiteralParams];
end;
If you've dropped the component on the form, I'm not sure why you're not handling this in the Object Inspector instead, however.
If this doesn't solve the problem, edit your question and provide the exact error message you're receiving, including any memory addresses in the case of an exception being raised.
I have cracked this. The issue was that I didn't refer to OPconvert.pas file, which contained the TSOAPConvertOption enumeration. I don't know whether copying this file into the same folder as my project files and referring to this in the "uses" section is the right way, but it worked fine.

Resources