Is there a way to use the JVCL's TJvXxxAppStorage to store float values as strings (e.g. "10.152") rather than hex dumps and also control the decimal separator that is being used?

Is there a way to use the JVCL's TJvXxxAppStorage to store float values as strings (e.g. "10.152") rather than hex dumps and also control the decimal separator that is being used?

I know that I can set
AppStorage.StorageOptions.FloatAsString := True;

But then it will use the currently active DecimalSeparator when I store a float property, e.g. "10,152" for standard German settings.

It internally calls

procedure TJvCustomAppStorage.WriteFloatInt(const Path: string; Value: Extended);
begin
if StorageOptions.FloatAsString then
DoWriteString(Path, EncryptPropertyValue(FloatToStr(Value)))
else
DoWriteFloat(Path, Value);
end;

Which calls FloatToStr which uses the DecimalSeparator. Providing an OnEncryptPropertyValue handler won't solve that problem either because then the result will be mime encoded.

(I think it cannot be done without changing the WriteFloatInt code. But I would like a confirmation for that.)

Comments

  1. You can temporarily change global defaults, that FloatToStr uses.

    ReplyDelete
  2. Yes that would work. But might have undesired side effects, especially in multi threaded programs.

    ReplyDelete
  3. I think I'd rather change the JVCL code. But yes, in a single threaded program this would likely work. It just smells.

    ReplyDelete
  4. function TOpenSCADController.FormatDec(Val :Double):string;
    var fs :TFormatSettings;
    begin
    fs := TFormatSettings.Create;
    fs.DecimalSeparator:= '.';
    fs.ThousandSeparator:= ',';
    Result := FormatFloat('#0.00', Val, fs);
    end;

    I use this function to avoid localization.

    As "fs" is a local instance of the record, this only affects the code inside the method.

    ReplyDelete
  5. Juan C. Cilleruelo doesn't solve the problem, though. I'd still need to change the JVCL code.

    ReplyDelete
  6. Sorry!

    Maybe you can use a helper class to override this method, even if it is private.

    stackoverflow.com - How I can patch a private method of a delphi class?

    I don't check it. It's only an idea.

    ReplyDelete
  7. Since we are talking about open source library, so the best idea will be to modify it's sources and send your patch to upstream developers.

    ReplyDelete
  8. Thomas Mueller You may try this:

    type
    TMyStorage = class(TJvAppRegistryStorage)
    protected
    procedure DoWriteFloat(const Path: string; Value: Extended); override;
    end;
    TJvAppRegistryStorage = class(TMyStorage);
    ...
    Implementation

    { TMyStorage }

    procedure TMyStorage.DoWriteFloat(const Path: String; Value: Extended);
    var
    SValue : string;
    FS : TFormatSettings;
    begin
    FS := TFormatSettings.Create;
    FS.DecimalSeparator := '.';
    FS.ThousandSeparator := #0;
    SValue := FloatToStr(Value, FS);
    DoWriteString(Path, SValue);
    end;

    And you need to set
    StorageOptions.FloatAsString := False;

    ReplyDelete
  9. Микола Петрівський
    Before they moved to GitHub, I had write access to the svn repository on sf.net, so I could have simply committed a change. I was not the most active contributor but used to fix bugs once in a while.

    Unfortunately nowadays I'd need a GitHub account (I think I actually have got one) and knowledge about git and pull requests. I remember trying that before and the change being rejected. And since I don't regularly use git / GitHub, I keep forgetting how to do that. Given the required effort, I can't be bothered any longer.

    ReplyDelete
  10. Achim Kalwa Yes, that worked. I used a descendant of TJvAppIniFileStorage though.

    ReplyDelete
  11. Thomas Mueller get me the fix and I will do that for you.

    ReplyDelete

Post a Comment