Firemonkey: TGrid usage on Embarcadero C++ Builder

2020-04-19 05:52发布

问题:

I'm try to build a tool that reads data from a database and displays it as a table using a TGrid in Firemonkey. I need to use different types of columns like TCheckColumn and TPopupColumn but can't find any good guide or example on how to use them in C++ Builder.

Any way, I managed to understand the usage of the TStringColumn,TProgressColumn setting the Value of the cell in the TGrid's event onGetValue.

Does any one of you know how to set the Value for columns of type TCheckColumn, TImageColumn and TPopupColumn?

thanks Daniele

---UPDATE---

I managed to use the TProgressColumn. This is what I do in the Form's constructor:

// TStringColumn
Grid1->AddObject(new TStringColumn(this));
// TCheckColumn
TCheckColumn* c = new TCheckColumn(this);
Grid1->AddObject(c);
// TPopupColumn
// list of values
TStringList * l = new TStringList(NULL);
l->Add(L"First");
l->Add(L"Second");
l->Add(L"Third");
TPopupColumn* p = new TPopupColumn(this);
// adding the list to the PopupColumn
p->Items = l;
Grid1->AddObject(p);
// TProgressColumn
Grid1->AddObject(new TProgressColumn (this));
Grid1->RowCount = 3 ;

and this is the Grid1GetValue method:

// TStringColumn
if(Col == 0) Value = TValue::From<String>(Row);
// TCheckColumn  !! Can't make it work
if(Col == 1) Value = TValue::From<Boolean>(true);
// TPopupColumn
if(Col == 2)    Value = TValue::From<int>(2);
// TProgressColumn
if(Col == 3)    Value = TValue::From<double>(50.0);

---UPDATE---

if I save the value of the column using the method OnSetValue

void __fastcall TForm1::Grid1SetValue(...)
{
if(Col == 1) check = Value;
}

and then set it with the method OnGetValue:

void __fastcall TForm1::Grid1GetValue(...)
{
// TCheckColumn  !! Can't make it work
if(Col == 1) Value = check;// TValue::From<Boolean>(true);
}

After I click on one checkbox all the other checkboxes change state. So the component works correctly... now the point is how to set the Value to true or false in the right way.

回答1:

TGris does not store any data, you should create your own datastorage.

Example: TGrid with TCheckColumn, TStringColumn and TPopupColumn

type
  TField = record
    Checked:  Boolean;
    Name:     string;
    Column:   Byte;
  end;

var
  Fields: TList<TField>;

function SetField(const AChecked: Boolean; const AName: string; const AColumn: Byte): TField;
begin
  with Result do begin
    Checked := AChecked;
    Name    := AName;
    Column  := AColumn;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  I: Integer;
begin
  Fields := TList<TField>.Create;
  Fields.Add(SetField(True, 'Name', 1));
  Fields.Add(SetField(True, 'Login', 2));
  Fields.Add(SetField(True, 'Password', 3));
  for I := 1 to Fields.Count do
    PopupColumn1.Items.Add('Column ' + IntToStr(I));
  gdFields.RowCount := Fields.Count;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Fields.Free;
end;

procedure TFormExport.gdFieldsGetValue(Sender: TObject; const Col, Row: Integer; var Value: TValue);
begin
  case gdFields.Columns[Col].TabOrder of
    0:  Value := Fields[Row].Checked;
    1:  Value := Fields[Row].Name;
    2:  Value := Fields[Row].Column - 1;
  end;
end;

procedure TFormExport.gdFieldsSetValue(Sender: TObject; const Col, Row: Integer; const Value: TValue);
var
  FRec: TField;
begin
  FRec := Fields[Row];
  case gdFields.Columns[Col].TabOrder of
    0:  FRec.Checked := Value.AsBoolean;
    1:  FRec.Name    := Value.AsString;
    2:  FRec.Column  := Value.AsInteger + 1;
  end;
  Fields[Row] := FRec;
end;

Now all data from your datastorage will be changed after editing your TGrid, but possible bug in TGrid - never received OnSetValue after changing PopupColumn



回答2:

I can't give C++ code but a Delphi example should be easy enough to translate.

You get and set all cell values the same way, by listening for the OnGetData and OnSetData events, get take/give values of type TValue (XE3 and later). It's just a case of returning the appropriate type in the TValue:

uses System.RTTI;

procedure Form1.Grid1GetValue(Sender: TObject;const Col, Row: Integer;var Value: TValue);
begin
  if Col = 1 then
    Value := TValue.From<Integer>(1)
  else if Col = 2 then
    Value := TValue.From<String>('Hello')
  else if Col = 3 then
    Value := Tvalue.From<Single>(1.0);
end;

procedure Form1.Grid1SetValue(Sender: TObject;const Col, Row: Integer;const Value: TValue);
begin
  if Col = 1 then
    I := Value.As<Integer>
  else if Col = 2 then
    St := Value.As<String>
  else if Col = 3 then
    Si := Value.As<Single>;
end;

As far as I can tell a popup menu can't accept or give data.



回答3:

In order to solve your problem, redefine the TCheckCell class in the following way:

#include <FMX.Grid.hpp>
#include <boost/dynamic_bitset.hpp>

class CheckCellClass:public TCheckCell
{
public:
 __fastcall virtual CheckCellClass(System::Classes::TComponent*AOwner):TCheckCell(AOwner)
 {
 };

 virtual System::Rtti::TValue __fastcall GetData(void)
 {
  return TValue::From<bool>(this->IsChecked);
 };

 virtual void __fastcall SetData(const TValue&Value)
 {
  TValue V(Value);
  this->IsChecked=V.AsBoolean();
 };
};

//Redifine TCheckColumn class
class CheckColumnClass:public TCheckColumn
{
private:
 virtual Fmx::Controls::TStyledControl*__fastcall CreateCellControl(void)
 {
  CheckCellClass*Cell=new CheckCellClass(this);
  Cell->OnChange     =&(this->DoCheckChanged);
  return Cell;
 };

public:
__fastcall CheckColumnClass(System::Classes::TComponent*AOwner):TCheckColumn(AOwner)
 {
 };
};

//global Data for Save curent State Cell

boost::dynamic_bitset<unsigned char>FullDiscreteInputs;

Add To Grid In Constuctor
 FullDiscreteInputs.resize(100);
 DiscreteInputsGrid->RowCount=FullDiscreteInputs.size();    
 CheckColumnClass* DiscreteInPutsCheckColumn        =new CheckColumnClass(DiscreteInputsGrid);
 DiscreteInputsGrid->AddObject(CoilsCheckColumn);

void __fastcall TForm1::DiscreteInputsGridGetValue(TObject*Sender, const int Col, const int Row,TValue&Value)
{
 //...

 if(DiscreteInputsGrid->ColumnByIndex(Col)==DiscreteInPutsCheckColumn)
 {
 Value=TValue::From<bool>(FullDiscreteInputs[Row]);
 }
 //...
}

//---------------------------------------------------------------------------
void __fastcall TForm1::DiscreteInputsGridSetValue(TObject*Sender, const int Col, const int Row, const TValue&Value)
{
 TValue V(Value);
 if(DiscreteInputsGrid->ColumnByIndex(Col)==DiscreteInPutsCheckColumn)
 {
 FullDiscreteInputs[Row]=V.AsBoolean();
 }
}
//---------------------------------------------------------------------------