Dump content of TCustomActionList descendant into human-readable form - delphi

I have foreign object instance and i want to examine properties of the component "manually".
I know about component streaming and how to convert output to text format, but problem is - TCustomActionList (as i figured out my instance is TActionManager) wont stream its actions. Is there any trick to have it to do so?
UPD: Now doing that by creating TDataModule (as container/owner), iterating thru Actions and creating TAction instances and doing Assign. Solution still suffers from not caring about TAction descendants tho.

Simple example that doesn't do anything tricky.
function TMyThing.SaveComponent(a:TComponent):String;
var
Stream2: TStringStream;
Stream1: TMemoryStream;
begin
Stream1 := TMemoryStream.Create;
Stream2 := TStringStream.Create;
try
Stream1.WriteComponent(a);
Stream1.position := 0;
ObjectBinaryToText(Stream1, Stream2);
result := Stream2.DataString;
finally
Stream1.Free;
Stream2.Free;
end;
end;
procedure TMyThing.Dump;
var
n:Integer;
a:TContainedAction;
s:String;
begin
for n := 0 to ActionManager1.ActionCount-1 do begin
a := ActionManager1.Actions[n];
s := '----- '+a.Category+' '+a.Name+' '+a.ClassName+':';
// crude manual way
if a is TAction then begin
s := s+ ' '+TAction(a).Caption;
s := s+ ' '+TAction(a).Hint;
end;
// RTTI stream in DFM text format
s := s+ SaveComponent(a);
Memo1.Lines.Add(s);
end;
end;

Related

In Delphi, use SaveToStream to save ClientDataSets plus other material to a file?

I would like to use SaveToStream to save a ClientDataSet ALONG WITH OTHER MATERIAL. Here is a short sample:
filename := ChangeFileExt(Application.ExeName, '.dat');
FS := TFileStream.Create(filename, fmCreate);
CDS.SaveToStream(FS);
ShowMessage('After save, position is ' + IntToStr(FS.Position));
{now write a longint}
L := 1234;
siz := SizeOf(L);
Write(L, siz);
FS.Free;
But when I try to load this back in using LoadFromStream, and I again display the position after the ClientDataSet has been loaded, I see that the position is now 4 bytes AFTER the clientdataset was originally saved. It seems that CDS.LoadFromStream just plows ahead and consumes whatever follows it. As a result, when I then try to read the longint, I get an end of file error.
It is not sufficient to just use the CDS.SaveToStream at the end of creating a file, because what I'd really like to do is to save TWO clientdatasets to the file, one after the other, plus other material.
Ideas? Thanks.
[NB, this solution is essentially doubling up the work that (TLama's suggestion) "ReadDataPacket/WriteDataPacket" already does internally. I would use TLama's approach i.e. sub-class TClientDataSet to expose the above protected methods, and use the WriteSize parameter.]
Save the datasets to a temporary stream and then copy that to your destination stream with size information:
procedure InternalSaveToStream(AStream: TStream);
var
ATempStream: TMemoryStream;
ASize: Int64;
begin
ATempStream := TMemoryStream.Create;
// Save first dataset:
DataSet1.SaveToStream(ATempStream, dfBinary);
ASize := ATempStream.Size;
AStream.WriteData(ASize);
ATempStream.Position := 0;
AStream.CopyFrom(ATempStream, ALength);
ATempStream.Clear;
// Save second dataset:
DataSet2.SaveToStream(ATempStream, dfBinary);
ASize := ATempStream.Size;
AStream.WriteData(ASize);
ATempStream.Position := 0;
AStream.CopyFrom(ATempStream, ALength);
ATempStream.Clear;
FreeAndNil(ATempStream);
end;
To read back, first read the size and then copy that section of your source to a temporary stream again and load your dataset from that:
procedure InternalLoadFromStream(AStream: TStream);
var
ATempStream: TMemoryStream;
ASize: Int64;
begin
ATempStream := TMemoryStream.Create;
// Load first datset:
AStream.Read(ASize,SizeOf(ASize));
ASize := ATempStream.Size;
ATempStream.CopyFrom(AStream,ASize);
ATempStream.Position := 0;
DataSet1.LoadFromStream(ATempStream);
//...etc.
end;

How to access dataset from custom component linked with Live Bindings?

I am creating my own DBGrid that is derived from TMSFMXGrid. How can I find out which dataset is linked to this grid, if it is linked using Live Bindings? In design time you can see in object inspector property LiveBinding, but is not accessible in run time. Otherwise I will have to publish my own property, where you could define used dataset.
After many hours of searching, I had found out this solution.
function GetDataSet: TDataSet;
var
obj: TColumnDescObject;
dts: TBaseLinkingBindSource;
ds: TDataSet;
begin
Result := nil;
if ColumnDescList.Count > 0 then
begin
obj := TColumnDescObject(ColumnDescList.Items[0]);
if Assigned(obj) then
begin
dts := obj.ColumnDesc.DataSource;
if Assigned(dts) then
begin
if dts is TCustomBindSourceDB then
begin
ds := (dts as TCustomBindSourceDB).DataSet;
if Assigned(ds) then
begin
Result := ds;
end;
end;
end;
end;
end;
end;

How can I get HTML source code from TWebBrowser

How can I get source code from WebBrowser component?
I want to get source code of active page on WebBrowser component and write it to a Memo component.
Thanks.
You can use the IPersistStreamInit Interface and the save method to store the content of the Webbrowser in a Stream.
Uses
ActiveX;
function GetWebBrowserHTML(const WebBrowser: TWebBrowser): String;
var
LStream: TStringStream;
Stream : IStream;
LPersistStreamInit : IPersistStreamInit;
begin
if not Assigned(WebBrowser.Document) then exit;
LStream := TStringStream.Create('');
try
LPersistStreamInit := WebBrowser.Document as IPersistStreamInit;
Stream := TStreamAdapter.Create(LStream,soReference);
LPersistStreamInit.Save(Stream,true);
result := LStream.DataString;
finally
LStream.Free();
end;
end;
That works well too:
uses MSHTML;
function GetHTML(w: TWebBrowser): String;
Var
e: IHTMLElement;
begin
Result := '';
if Assigned(w.Document) then
begin
e := (w.Document as IHTMLDocument2).body;
while e.parentElement <> nil do
begin
e := e.parentElement;
end;
Result := e.outerHTML;
end;
end;
This has been asked and answered many times in the Embarcadero forums, with plenty of code examples posted. Search the archives.
The gist of it is that you Navigate() to the desired URL and wait for the OnDocumentComplete event to fire, then QueryInterface() the Document property for the IPersistStreamInit interface and call its save() method. Create a TStream object instance, such as a TMemoryStream, wrap it in a TStreamAdapter object, and then pass the adapter to save(). You can then load the TStream into the TMemo as needed.
Why not Quick and Dirty:
OnNavigateComplete2()
Form1.RichEdit1.Text:=(WebBrowser1.OleObject.Document.documentElement.outerhtml);

Get richtext from a richedit in Delphi

Is there a way to get the RTF data from a richedit without using savetostream as in
strStream := TStringStream.Create('') ;
try
RichEdit.Lines.SaveToStream(strStream);
Text := strStream.DataString;
strStream.CleanupInstance;
finally
strStream.Free
Tim the only way to get the RTF data from an RichEdit control is using a Stream because the windows message (EM_STREAMOUT) wich retrieve the RTF Data require a EditStreamCallback structure, this is the way used by windows to transfer rtf data into or out of a richedit control.
So you can use your own sample code, or implement the call to the windows message EM_STREAMOUT.
function RichTextToStr(red : TRichEdit) : string;
var ss : TStringStream;
begin
ss := TStringStream.Create('');
try
red.Lines.SaveToStream(ss);
Result := ss.DataString;
finally
ss.Free;
end;
end;
procedure CopyRTF(redFrom,redTo : TRichEdit);
var s : TMemoryStream;
begin
s := TMemoryStream.Create;
try
redFrom.Lines.SaveToStream(s);
s.Position := 0;
redTo.Lines.LoadFromStream(s);
finally
s.Free;
end;
end;
I can attest deviation from the pattern results in frustration....

StringBuilder.Split in Delphi?

Anyone know of a good Split procedure that uses StringBuilder in Delphi?
You might be better off using TStringlist.DelimitedText (or any other non-abstract TStrings sub-class). It's more of the traditional Delphi way of achieving what string.Split does in .Net (assuming I remember correctly).
e.g. To split on a pipe | character
var
SL : TStrings;
i : integer;
begin
SL := TStringList.Create;
try
SL.Delimiter := '|';
SL.StrictDelimiter := True;
SL.DelimitedText := S;
for i := SL.Count - 1 do
begin
// do whatever with sl[i];
end;
finally
SL.Free;
end;
end;
You may need to handle the QuoteChar property as well
You can also Look at my answer to this question for a general purpose utility functions GetStringPart and NumStringParts that allow you to perform split type operations.

Resources