Page 1 of 1

Using offscreen TRichViewEdit

Posted: Tue Sep 12, 2006 2:08 pm
by martindholmes
Hi there,

I've been experimenting with using offscreen RichViewEdit and RVStyle components, created on the fly and positioned offscreen (with negative left and top values) on the application's main form. I'm trying to use these components to read blocks of XHTML code and turn it into RVF, which I then store and stream in and out of RichView controls which are visible.

However, I'm having trouble getting the offscreen controls to function properly; when I read in data, then get the RVF, it's empty. In contrast, when I use exactly the same code but targetted at existing RVEs and RVSs that are visible on forms, the reading operations work correctly. I'm wondering if there is some error I'm making in creating and initializing the controls, or perhaps there's something intrinsic to RV controls which makes them function differently when they're offscreen (and therefore invisible, although Visible = True for the RVE). Here's the code I'm using to initialize the controls:

FRVE := TRichViewEdit.Create(Application.MainForm);
FRVE.Parent := TWinControl(Application.MainForm);
FRVS := TRVStyle.Create(Application.MainForm);
with FRVE do
begin
//Put it off-screen
Width := 500;
Height := 500;
Left := -600;
Top := -600;
Style := FRVS;
DoInPaletteMode := rvpaCreateCopies;
ReadOnly := False;
Options := [rvoAllowSelection, rvoScrollToEnd, rvoShowPageBreaks,
rvoTagsArePChars, rvoAutoCopyText, rvoAutoCopyRVF,
rvoAutoCopyImage, rvoAutoCopyRTF, rvoFormatInvalidate,
rvoDblClickSelectsWord, rvoRClickDeselects,
rvoFastFormatting];
RTFReadProperties.UnicodeMode := rvruOnlyUnicode;
RTFReadProperties.TextStyleMode := rvrsAddIfNeeded;
RTFReadProperties.ParaStyleMode := rvrsAddIfNeeded;
RTFReadProperties.UseHypertextStyles := True;
RVFOptions := [rvfoSavePicturesBody, rvfoSaveControlsBody,
rvfoSaveBinary, rvfoSaveTextStyles, rvfoSaveParaStyles,
rvfoSaveDocProperties, rvfoLoadDocProperties];
end;
with FRVS do
begin
if TextStyles.Count > 0 then
for i := 0 to TextStyles.Count-1 do
TFontInfo(TextStyles).Unicode := True;
DefUnicodeStyle := 0;
end;

Posted: Wed Sep 13, 2006 12:07 am
by DickBryant
I'm doing similar operations and just set visible=false to hide the controls rather than postion them offscreen. Seems to work fine.

Posted: Wed Sep 13, 2006 11:59 am
by martindholmes
That doesn't work for me, unfortunately. There must be something else I'm doing wrong.

Could you possibly post the code you're using to create the controls?

Cheers,
Martin

Posted: Thu Sep 14, 2006 1:48 pm
by martindholmes
I've discovered that the content is correctly being read into the offscreen TRichViewEdit (I can see it if I make that control visible), but when it's streamed out of that control and stored as RVF in a string, something goes wrong; no text is stored, and the RVF is only about a fifth the length it should be.

Any idea what might be causing this?

Cheers,
Martin

Posted: Thu Sep 14, 2006 1:49 pm
by martindholmes
Just to clarify, this is the code I'm using to get the RVF string:

function TMRVExporter.GetRVF: string;
var
SS: TStringStream;
strTemp: string;

begin
Result := '';//default return
SS := TStringStream.Create(strTemp);
try
//stream it into the RVE
CurrRVE.SaveRVFToStream(SS, False);
Result := SS.DataString;
finally
FreeAndNil(SS);
end;
end;

Posted: Mon Sep 18, 2006 3:21 am
by DickBryant
Martin,

I'm actually using a TRichViewEdit 'dropped' on a form with the visible set to false. However I'm not using the DataString property of the stream - this is an example of the type of code I'm using:

try
RichViewEdit3.SaveRVFToStream(Stream, False);
Stream.Position := 0;
PrintCardsTable.Cells[r,c].Clear;
PrintCardsTable.Cells[r,c].AppendRVFFromStream(Stream,-1,Dummy1,Dummy2);
finally
Stream.Free;
end;

RVE3 is the invisible control and has had content loaded into it from another RVE. I use either AppendRVF or LoadRVF depending on whether the target is being added to or overwritten. Hope this helps. If you need Sergey's help on this I'd recommend a fresh topic as I don't think he looks at threads that other's have "answered" :-)

Dick

Posted: Mon Sep 18, 2006 6:11 am
by martindholmes
It looks as though there may be a difference between a TRichViewEdit which is instantiated on a form (visible or not) and one which is created entirely in code. If I make my code-built TRVE visible, and make its owner and parent the main form of the application, I can see the documents being loaded into it correctly, but when they're streamed out the stream contains an empty document; when I use an existing TRVE on the form, which was created by dropping components onto the form, then everything works fine. There must be something that happens in the process of creating the TRVE and TRVS components that's different in the two scenarios.

I think the best thing to do is to create a little sample project that demonstrates this, and run it by Sergei to see if he has any ideas. I'd really like to avoid having to use components on an actual form, because it makes my library less portable.

Cheers,
Martin

Posted: Mon Sep 18, 2006 6:21 pm
by Sergey Tkachenko
There is only one difference in TRichView created in code and at design time: initial values of some properties.
All these properties are listed in the component editor (right click in Delphi, "Settings" in the popup menu).

As for your problem, I think it's because of TStringStream. Binary data, such as RVF, cannot be stored in TStringStream. Use TMemoryStream instead.

Code: Select all

function TMRVExporter.GetRVF: string; 
var 
 MS: TMemoryStream; 
begin 
  Result := '';//default return 
  MS := TMemoryStream.Create; 
  try 
    //stream it into the RVE 
    CurrRVE.SaveRVFToStream(MS, False); 
    SetLength(Result, MS.Size);
    Move(MS.Memory^, PChar(Result)^, MS.Size);
  finally 
    FreeAndNil(MS); 
  end; 
end;

Posted: Mon Sep 18, 2006 10:30 pm
by martindholmes
Hi there,

I hadn't realized RVF was actually binary. Using your code above, is it safe to store and retrieve blocks of RVF in a TStringList? I'm associating each block of RVF (a little document) with a GUID in string form:

[GUID]=RVF

so I can retrieve each block of RVF based on its GUID, and stream it back into a TRichViewEdit for display and editing.

What's puzzling, though, is that this all works fine when I use an existing TRVE, even using TStringStream. Weird!

Cheers,
Martin

Posted: Tue Sep 19, 2006 5:55 am
by Sergey Tkachenko
You can store strings returned by this GetRVF in TStringList (one item = one RVF document). But you cannot, for example, use TStringList.SaveToFile to store this string list, because RVF contains line break characters.

RVF can be a text format, if you exclude rvfoSaveBinary from RVFOptions (but the resulting data will be larger and saved/loaded more slowly).

To move RVF string to TMemoryStream, use this code:

Code: Select all

MS.Size := Length(RVFString);
Move(PChar(Result)^, MS.Memory^, MS.Size); 

Posted: Tue Sep 19, 2006 5:58 am
by Sergey Tkachenko
Probably, your RichViews placed on the form in designtime, had rvfoSaveBinary excluded from RVFOptions.
Or may be their RVFOptions do not contain options to save styles and layout, and documents do not have binary data (pictures, tables, controls, Unicode text).
In these cases, RVF is a text format.

Posted: Tue Sep 19, 2006 6:17 am
by martindholmes
Thanks Sergey -- this will solve my problems.

Cheers,
Martin