Recent

Author Topic: [Solved]Using a description instead of an enumerated type  (Read 17090 times)

Knipfty

  • Full Member
  • ***
  • Posts: 232
[Solved]Using a description instead of an enumerated type
« on: August 27, 2012, 06:53:32 pm »
Hi,

I have an enumerated type defined as
Code: [Select]
Type TBookReverse = (brNew, brReverse);
In the DB table, the enumerated values are stored as integers, so using the type above that would be a 0 or 1.

On a form I have a DBGrid that is connected to the TTable object.  The DBGrid's column shows the 0 or 1, however, I would like it to display 'New' or 'Reverse' instead.

I looked at the OnGetText/OnSetText event handlers that are associated with TField, however, the documentation states:
Quote
It is not allowed to change the state of the dataset or the contents of the field during the execution of this event handler.

Is there another way to do this?  The user is not allowed to edit the data directly on the DBGrid.
« Last Edit: August 28, 2012, 03:02:30 am by Knipfty »
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

ezlage

  • Guest
Re: Using a description instead of an enumerated type
« Reply #1 on: August 27, 2012, 06:58:03 pm »
You can do this way:

Code: [Select]
var
  t:string;
begin
  WriteStr(t,brNew); //same of t:='brNew';
end;[code]

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Using a description instead of an enumerated type
« Reply #2 on: August 27, 2012, 07:16:49 pm »
Hi ezlage,

I'm trying to display 'New' in cell in a DBGrid.  I don't understand how a Writeln will work or where to put it if it does.

If I could use the onGetText event, I would expect the code to look something like this:

OnGetTExt procedure definition
Code: [Select]
   procedure enumBR(Sender: TField; var aText: string; DisplayText: boolean);
   begin
     case integer(aText) of
       0: aText := 'New';
       1: aText := 'Reverse'
     end
   end;

And then later on its assigned
Code: [Select]
  AdsTable1.FieldByName('EntryType').OnGetText:=@enumBR;

BTW, the above code does not compile.  I get the following error msg:
Quote
main.pas(239,49) Error: Incompatible types: got "<address of procedure(TField,var AnsiString,Boolean) is nested;Register>" expected "<procedure variable type of procedure(TField,var AnsiString,Boolean) of object;Register>"
  So, I'm not even sure how to get OnGetText to work.
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

ludob

  • Hero Member
  • *****
  • Posts: 1173
Re: Using a description instead of an enumerated type
« Reply #3 on: August 27, 2012, 07:24:17 pm »
OnGetText is the correct way of changing the text displayed in a dbcontrol. You don't change the dataset when modifying the text displayed. In your case you would do something like
Code: [Select]
procedure TForm1.SQLQuery1idGetText(Sender: TField; var aText: string;
  DisplayText: Boolean);
begin
  writestr(aText,TBookReverse(Sender.AsInteger));
end;
This will display 'brNew' or 'brReverse'. If you want to to customise the string do something like:
Code: [Select]
const
  REVSTRINGS:array [TBookReverse] of string = ('New','Reverse');
...
aText:=REVSTRINGS[TBookReverse(Sender.AsInteger)];

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Using a description instead of an enumerated type
« Reply #4 on: August 27, 2012, 07:30:04 pm »
Hi ezlage,

I'm trying to display 'New' in cell in a DBGrid.  I don't understand how a Writeln will work or where to put it if it does.

If I could use the onGetText event, I would expect the code to look something like this:

OnGetTExt procedure definition
Code: [Select]
   procedure enumBR(Sender: TField; var aText: string; DisplayText: boolean);
   begin
     case integer(aText) of
       0: aText := 'New';
       1: aText := 'Reverse'
     end
   end;

type casting a string to an integer does not return the value of the string as integer you need to use strtoint function
or simple
  if aText = '0' then atext := 'New'

also although you are not allowed to change the state of the dataset or fields data it doesn't mean you can read the data from inside this event eg

  if Sender.AsInteger = 0 then atext := 'New';


And then later on its assigned
Code: [Select]
  AdsTable1.FieldByName('EntryType').OnGetText:=@enumBR;

BTW, the above code does not compile.  I get the following error msg:
Quote
main.pas(239,49) Error: Incompatible types: got "<address of procedure(TField,var AnsiString,Boolean) is nested;Register>" expected "<procedure variable type of procedure(TField,var AnsiString,Boolean) of object;Register>"
  So, I'm not even sure how to get OnGetText to work.

well this error tells me that the enumBR procedure is not a method of an object or it has been declared with a call type other than register eg
  procedure enumBR(Sender: TField; var aText: string; DisplayText: boolean); cdecl;

from your code I assume that the procedure is not part of an object.


Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

ezlage

  • Guest
Re: Using a description instead of an enumerated type
« Reply #5 on: August 27, 2012, 07:38:14 pm »
Sorry, I didn't understood well.

WriteStr(not WriteLn) stores name of enumerated value in a var as string.
So, your field must be ftString.

But, my suggestion maybe not helpful...

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Using a description instead of an enumerated type
« Reply #6 on: August 27, 2012, 08:02:04 pm »
Working off if taazz and ludob, I created a procedure as part of teh TForm1 object:
Code: [Select]
procedure TForm1.AdsTable1OnGetText(Sender: TField; var aText: string; DisplayText: boolean);
begin
  case Sender.AsInteger of
    0: aText := 'New';
    1: aText := 'Reverse';
    otherwise aText := 'undefined'
  end
end;

That seems to work as far as the compiler is concerned.

But now when I try to assign the procedure to the event handler with this code in the TForm1.ForShow method: (And where should I make this assignment?)
Code: [Select]
AdsTable1.FieldByName('EntryType').OnGetText:=@TForm1.AdsTable1GetText;
I ge tthe following compiler error msg
Quote
main.pas(284,59) Error: identifier idents no member "AdsTable1GetText"



64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

typo

  • Hero Member
  • *****
  • Posts: 3051
Re: Using a description instead of an enumerated type
« Reply #7 on: August 27, 2012, 08:09:09 pm »
You have mistyped the method name: AdsTable1OnGetText.

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Using a description instead of an enumerated type
« Reply #8 on: August 27, 2012, 08:13:58 pm »
Well now, that's embarrassing... :-[

And it now compiles...  :-[

I'll test this all when I get home this evening.  Thanks
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Using a description instead of an enumerated type
« Reply #9 on: August 27, 2012, 08:21:21 pm »
Code: [Select]
AdsTable1.FieldByName('EntryType').OnGetText:=@TForm1.AdsTable1GetText;
I ge tthe following compiler error msg
Quote
main.pas(284,59) Error: identifier idents no member "AdsTable1GetText"

You do know that you can't use the class to assign a non class method to an event right?

You need to to use a created object (or the variable that hold the reference to that object) in this case I assume that form1 is the global variable of this test case and if the code above is part of the form code then self would be a better choice.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Using a description instead of an enumerated type
« Reply #10 on: August 27, 2012, 08:41:08 pm »
Hi taazz,

In my test code that I set up to figure this out, I used the TForm1.FormShow event handler
Code: [Select]
procedure TForm1.FormShow(Sender: TObject);
begin
    AdsTable1.FieldByName('EntryType').OnGetText:=@TForm1.AdsTable1OnGetText;
end;

Is this incorrect?

Are you suggesting:
Code: [Select]
procedure TForm1.FormShow(Sender: TObject);
begin
    AdsTable1.FieldByName('EntryType').OnGetText:=@Self.AdsTable1OnGetText;
end;
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Using a description instead of an enumerated type
« Reply #11 on: August 27, 2012, 08:52:32 pm »
One more question:

Do I need to also create an OnSetText event handler?  Such as

Code: [Select]
procedure TForm1.AdsTable1OnSetText(Sender: TField; var aText: string; DisplayText: boolean);
begin
  case Sender.AsString of
    'New':     aText := '0';
    'Reverse': aText := '1'
  end
end;

The user cannot edit directly in the DBGrid, so I don;t know if its needed.  I believe I have the DBGrid set to nto allow editting.

Thanks
« Last Edit: August 27, 2012, 09:25:35 pm by Knipfty »
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Using a description instead of an enumerated type
« Reply #12 on: August 27, 2012, 09:48:23 pm »
Hi taazz,

In my test code that I set up to figure this out, I used the TForm1.FormShow event handler
Code: [Select]
procedure TForm1.FormShow(Sender: TObject);
begin
    AdsTable1.FieldByName('EntryType').OnGetText:=@TForm1.AdsTable1OnGetText;
end;

Is this incorrect?

I am not sure how FPC handles this kind of things but in my mind Tform1 is a class not an object I do not know if an object has been created for that class unless I use a variable that has a reference to an object of TForm1 class. This means that it needs extra checking in order to make sure that everything is in place or you will get various SIGSEGV errors.

Are you suggesting:
Code: [Select]
procedure TForm1.FormShow(Sender: TObject);
begin
    AdsTable1.FieldByName('EntryType').OnGetText:=@Self.AdsTable1OnGetText;
end;

although self in this context is not needed it makes more sense to me and does not raise any red flags when I read it.
So yes its a lot better from my point of view

One more question:

Do I need to also create an OnSetText event handler?  Such as

Code: [Select]
procedure TForm1.AdsTable1OnSetText(Sender: TField; var aText: string; DisplayText: boolean);
begin
  case Sender.AsString of
    'New':     aText := '0';
    'Reverse': aText := '1'
  end
end;

The user cannot edit directly in the DBGrid, so I don;t know if its needed.  I believe I have the DBGrid set to nto allow editting.

Thanks


You do create a OnSetText event if you want to allow the user to enter something other than the expected value for example
if you want the end user to be able to type in New, N or Reverse, R and translate it to 0 or 1 then you use the OnSetText event.

If you use a DBcomboBox for the selection then you ignore the onsetText and work with the combobox it self. In any case when the OnSetText event is fired Sender.asstring will contain the existing value not the new one so you can't use it to check for the new value in this case you need to use the aText variable.

eg
Code: [Select]
procedure TForm1.AdsTable1OnSetText(Sender: TField; var aText: string; DisplayText: boolean);
begin
  case UpperCase(aText) of
    'NEW', 'N'     : aText := '0';
    'REVERSE', 'R' : aText := '1';
  end;
end;

In my defense I have only used those 2 events on password fields where I encrypt and decrypt them on the fly on the user edit form and the ongettext only to show some data inside a grid for a memo fields.

Regards.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Knipfty

  • Full Member
  • ***
  • Posts: 232
Re: Using a description instead of an enumerated type
« Reply #13 on: August 27, 2012, 10:06:06 pm »
Hi taazz,

I see what you are saying about TForm1 vs. Self.  I've changed the code to 'Self' and will test it out this evening when I am home.  (I don't have my project here at work to play with  :( ).

Since the user is not going to be entering any data on the DBGrid, I will skip the OnSetText for now.  I have them coded, just in case, and can bring them in pretty quickly.

And finally, no need to get defensive, it compiles in my little test environment and that was a major hurdle.  Now I can at least test it and see what happens.


From where I am sitting, if you want to make good use of Enumerated types, then you need a good way to make it transparent all the way thru to the DB.  So hopefully this will work and I can expand my usage of them.

I really appreciate your (really everyone's) help and will post what I find over the next couple of days,

Knipfty
64-bit Lazarus 2.2.0 FPC 3.2.2, 64-bit Win 11

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Using a description instead of an enumerated type
« Reply #14 on: August 27, 2012, 10:19:42 pm »
Enumerated types are great and you can use a number of visual components to help the user choose (combo boxes, radio groups etc) which can be automated using rtti.
In most cases I prefer a master detail relationship where I can define any number of enumeration after the application has been written and delivered but this makes the code a lot trickier to write and test for various conditions.

Glad I could be of help.

Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

 

TinyPortal © 2005-2018