Search code examples
delphivclbidi

How to display minus on the left (Delphi, TListview)


I'm trying to display the minus symbol of a negative number on the left side of an Item in a TListview that has his property BiDiMode = bdRightToLeft.

I tried the code there:

Thousand Separator in DBGrid

Function TfrmProjets.IfSign(floatValue: double; howMuchDecimalAfterComa: 

integer):string;
var
  strResult: string;
  strZero: string;
  i: integer;
begin
  strResult := '';
  strZero := '';

  for i := 0 to howMuchDecimalAfterComa -1 do
  begin
    strZero := strZero + '0';
  end;

  if(Sign(floatValue) = NegativeValue)then
  begin
      strResult := '-' + FormatFloat('0.' + strZero,Abs(floatValue));
    //strResult := '(' + FormatFloat('0.' + strZero,Abs(floatValue)) + ')';
  end
  else
  begin
    strResult := FormatFloat('0.' + strZero,floatValue);
  end;

  result := strResult;
end;

And in fact it work when I use breakpoint to evaluate strResult.

But when it come to TListView display in my item:

lvItem := lvPeriodic[0];
lvItem.SubItems.Add(IfSign(-14.2),1) + '%') ;

It display like this: 14.2%-

Is there a way to tell the TListview not to change the string I'm displaying or something?


Solution

  • Assuming you are using a unicode version of delphi, you can supply explicit RTL and LTR marks to force the position of language neutral or weak characters (like dash -, exclamation !, etc).

    The rules for parsing LTR and RTL text in various contexts are extensive and complex. For reference you can find the bidirectional algorithm specification here. One pass for weak characters does parse European numbers as LTR (which is why 14.2% ends up in the correct direction, but the use of the dash - to indicate minus introduces ambiguity. One would think that substituting a proper minus sign U+2212 would work but this is also a weak character, presumably because arabic numbers can be negative as well (and would need to be rendered in the opposite direction...I'm not sure).

    In any case - moving on, we can construct a function like this to force a string into a strict LTR order :

    function StrictLTR(const s : string) : string;
    const
      LTR_EMBED = Char($202A);
      POP_DIRECTIONAL = Char($202C);
    begin
      result := LTR_EMBED + s + POP_DIRECTIONAL;
    end;
    

    This places an embedded Left-To-Right character (U+202A) before the string and follows it with a Pop-Directional-Formatting (U+202C) character. The latter removes the embedded directional formatting cue and returns the text direction to whatever it was in the previous context. The returned string, therefore, is safe to use in either an RTL or LTR context.

    Calling this, then :

     ListView1.AddItem(StrictLTR(IfSign(-14.2,1) + '%'), nil);
    

    Will produce the desired result.


    As an aside, your IfSign function can be greatly simplified by using the Format function :

    Function IfSign(floatValue: double; numberOfDecimalPlaces:integer):string;
    begin
      result := Format('%.'+IntTostr(numberOfDecimalPlaces)+'f', [floatValue]);
    end;