Jump to content


Photo

TNextDBGrid - TNxDBCheckBoxColumn Issues


  • Please log in to reply
5 replies to this topic

#1 FourWhey

FourWhey
  • Members
  • 165 posts

Posted 10 April 2014 - 06:05 PM

I'm using Delphi 6 and I ran into a few problems while using TNxDBCheckBoxColumn, in conjuction with my data provider, which is a custom descendant of TADOConnection and the dataset a descendant of TCustomClientDataSet.

I've set ValueChecked and ValueUnChecked to True / False respectively.

When using TNextDBGrid and TNxDBCheckBoxColumn an exception is thrown when clicking the checkbox. The method TNxDBCheckBoxColumn.ApplyField attempts to cast a True/False string Value using StrToBool. However, StrToBool, at least in Delphi 6 is raising an exception even though TrueBoolStrs contains 'True'.
Here is my work-around for that.
procedure TNxDBCheckBoxColumn.ApplyField(Value: WideString);
var
  B: Boolean;
begin
  TryStrToBool(Value, <img src='http://www.bergsoft.net/forum/public/style_emoticons/<#EMO_DIR#>/cool.png' class='bbc_emoticon' alt='B)' />;

  case B of
    True: if FField.DataType = ftBoolean then
	  FField.AsBoolean := True else FField.Text := ValueChecked;
    False: if FField.DataType = ftBoolean then
      FField.AsBoolean := False else FField.Text := ValueUnchecked;
  end;
end;

When the grid is loaded and a TNxDBCheckBoxColumn is present in a column the checkbox isn't painted until the column (even though the checkbox isn't visible) is clicked at least once. After doing so the ValueChecked and ValueUnChecked text values are assigned. The checkbox isn't painted since the text value is an empty string.
Here is my work around for this, which uses the DefaultValue to represent a False condition. The column's DefaultValue must be empty.
procedure TNxDBCheckBoxColumnDisplay.Paint;
var
  Value: Boolean;
begin
  with Column as TNxDBCheckBoxColumn do
  begin
    if WideSameText(AsString, ValueChecked) then Value := True
    else if WideSameText(AsString, ValueUnchecked) then Value := False
    else if WideSameText(AsString, DefaultValue) then Value := False
    else Exit;
    DrawCheckBoxState(Self.ClientRect, Value, False, False);
  end;
end;


#2 Boki (Berg)

Boki (Berg)

    Boki (Berg)

  • Forum Admin
  • PipPipPipPipPip
  • 8,191 posts
  • Gender:Male

Posted 11 April 2014 - 07:34 AM

Hi,

Maybe it will be better to disable click on checkbox rather than painting it even if string doesn't match?

Maybe somebody want to hide checkboxes in some situations? With this some interesting things may be done.

Please tell me what you think.

Best regards
boki@bergsoft.net | LinkedIn Profile
--
BergSoft Home Page: www.bergsoft.net
Users Section: users.bergsoft.net
Articles and Tutorials: help.bergsoft.net (Developers Network)
--
BergSoft Facebook page
--
Send us applications made with our components and we will submit them on: www.bergsoft.net/apps.htm. Link to this page will be also set on home page too.

#3 FourWhey

FourWhey
  • Members
  • 165 posts

Posted 11 April 2014 - 05:12 PM

I could see there being some value in hiding the Checkbox, but isn't the purpose of painting the Checkbox to cue the user into to the fact that they can do something there? How would they know they can click it if all they see is an empty column?

Also, it doesn't get rid of the Checkbox, so if they accidentally click in the column it would suddenly appear unless like you said, one were to disable the click event or at least intercept it. You could easily make it hide the Checkbox using the code I provided simply by using the DefaultValue and adding the word "hide" or something in it.

The issue I had was due to the data provider assigning an empty string when loaded -- meaning there were three potential Checked states (checked, unchecked, emptystr), which can't be expressed with just Checked/UnChecked values which is why I introduced the DefaultValue.

I was just letting others know in case they encountered a similar issue.

Thanks for the follow up!

Hi,

Maybe it will be better to disable click on checkbox rather than painting it even if string doesn't match?

Maybe somebody want to hide checkboxes in some situations? With this some interesting things may be done.

Please tell me what you think.

Best regards



#4 Boki (Berg)

Boki (Berg)

    Boki (Berg)

  • Forum Admin
  • PipPipPipPipPip
  • 8,191 posts
  • Gender:Male

Posted 21 April 2014 - 08:26 PM

Hi,

Please tell me if this code is fine:

procedure TNxDBCheckBoxColumn.ApplyField(Value: WideString);
var
  B: Boolean;
begin
  B := StrToBoolDef(Value, False); // <<<<<<<< changed line
  case B of
    True: if FField.DataType = ftBoolean then
            FField.AsBoolean := True else FField.Text := ValueChecked;

    False: if FField.DataType = ftBoolean then
             FField.AsBoolean := False else FField.Text := ValueUnchecked;
  end;
end;

It will be included in update.
boki@bergsoft.net | LinkedIn Profile
--
BergSoft Home Page: www.bergsoft.net
Users Section: users.bergsoft.net
Articles and Tutorials: help.bergsoft.net (Developers Network)
--
BergSoft Facebook page
--
Send us applications made with our components and we will submit them on: www.bergsoft.net/apps.htm. Link to this page will be also set on home page too.

#5 FourWhey

FourWhey
  • Members
  • 165 posts

Posted 23 April 2014 - 03:45 PM

This doesn't work in Delphi 6. There's a bug in TryStrToBool, which is used by SysUtils.StrToBoolDef and StrToBool. You might consider using directives and use the following in D6.
{$IfDef VER140}
 TryBoolToStr(Value, <img src='http://www.bergsoft.net/forum/public/style_emoticons/<#EMO_DIR#>/cool.png' class='bbc_emoticon' alt='B)' />;
{$Else}
 B := StrToBoolDef(Value, False);
{$EndIf}

See below if you would like to see what the problem in D6.

function StrToBoolDef(const S: string; const Default: Boolean): Boolean;
begin
 if not TryStrToBool(S, Result) then
  Result := Default;
end;

function StrToBool(const S: string): Boolean;
begin
 if not TryStrToBool(S, Result) then
  ConvertErrorFmt(@SInvalidBoolean, [S]);
end;

function TryStrToBool(const S: string; out Value: Boolean): Boolean;
 function CompareWith(const aArray: array of string): Boolean;
 var
  I: Integer;
 begin
  Result := False;
  for I := Low(aArray) to High(aArray) do
   if AnsiSameText(S, aArray[I]) then
   begin
    Result := True;
    Break;
  end;
end;
var
 LResult: Extended;
begin
 Result := TryStrToFloat(S, LResult);
 if Result then
  Value := LResult <> 0
 else
 begin
  VerifyBoolStrArray;
  if CompareWith(TrueBoolStrs) then
   Value := True
  else if CompareWith(FalseBoolStrs) then
   Value := False
  else
   Result := False;
 end;
end;

If you don't see the problem, it this... TryBoolToStr's Result is never assigned after the first TryStrToFloat fails, which means that any non-numeric cast using TryBoolToStr will always return false for the function. Which is why StrToBool raises an exception, and StrToBoolDef will spuriously return the default.

The only usable value in this case is output Value from TryBoolToStr.

Wow, what a huge bug.

Hi,

Please tell me if this code is fine:

procedure TNxDBCheckBoxColumn.ApplyField(Value: WideString);
var
B: Boolean;
begin
B := StrToBoolDef(Value, False); // <<<<<<<< changed line
case B of
True: if FField.DataType = ftBoolean then
FField.AsBoolean := True else FField.Text := ValueChecked;

False: if FField.DataType = ftBoolean then
FField.AsBoolean := False else FField.Text := ValueUnchecked;
end;
end;

It will be included in update.



#6 Boki (Berg)

Boki (Berg)

    Boki (Berg)

  • Forum Admin
  • PipPipPipPipPip
  • 8,191 posts
  • Gender:Male

Posted 24 April 2014 - 09:07 AM

I think that you are correct, I remember now this problem (also discovered this bug)

I have created something like this for v6:

function StrToBoolEx(const S: string): Boolean;
var
F: Extended;
begin
Result := TryStrToFloat(S, F);

{ Not a 1 or 0? }
if not Result then
begin
{ Build BoolStrs, if needed }
AppendBoolStrArray;

{ Check in arrays }
Result := AnsiMatchText(S, TrueBoolStrs)
and not AnsiMatchText(S, FalseBoolStrs);
end;
end;

I think that I will import it to v5 too.
boki@bergsoft.net | LinkedIn Profile
--
BergSoft Home Page: www.bergsoft.net
Users Section: users.bergsoft.net
Articles and Tutorials: help.bergsoft.net (Developers Network)
--
BergSoft Facebook page
--
Send us applications made with our components and we will submit them on: www.bergsoft.net/apps.htm. Link to this page will be also set on home page too.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users