procedure TFormMain.CaseListMyShares(uri: String);
var
i: Integer;
begin
myShares := obAkasShareApiAdapter.UserShares(uri);
MySharesGrid.RowCount:= Length(myShares) +1;
MySharesGrid.AddCheckBoxColumn(0, false);
MySharesGrid.AutoFitColumns;
for i := 0 to Length(myShares) -1 do
begin
MySharesGrid.Cells[1, i+1]:= myShares[i].patientCase;
MySharesGrid.Cells[2, i+1]:= myShares[i].sharedUser;
MySharesGrid.Cells[3, i+1]:= myShares[i].creationDate;
MySharesGrid.Cells[4, i+1]:= statusText[StrToInt(myShares[i].situation) -1];
end;
end;
I want to create an invisible column to myself. I have to know row's identifier to make some operations with REST API. But end-user does not need to see this identifier on table. How can i create an invisible column ?
myShares[i].identifier which i want to hide on every row. Is that possible ? Using TAG or anything ?
To assign the identifier:
MySharesGrid.Objects[0, i+1] := TObject(myShares[i].identifier)
To access the identifier (e.g. OnClick):
Integer(MySharesGrid.Objects[0, MySharesGrid.Row])
To answer the question, assign its ColWidths to -1, thusly:
StringGrid1.ColWidths[4] := -1;
You can show it again by setting that back to a positive value.
This is not about using a StringGrid to store data, but for the User Interface to show/hide columns - like a Collapse speedbutton in the title, with a corresponding Show All button to restore it.
Related
When using the OnDblClick event of a TDBGrid, how can i know what column was double clicked ?
This is easy with the OnCellClick as it has a TColumn parameter, but not on OnDblClick.
During TDBGrid.OnDblClick the dataset is positioned to the clicked record and the column can be retrieved with the TDBGrid.SelectedIndex property. If you are interested in the underlying dataset field, you can directly access it with TDBGrid.SelectedField.
The OnDblClick event doesn't give you any information about the click, in particular where the click was performed, let alone which grid cell was clicked on. So, you will have to determine that information manually.
Try this:
Get the current mouse position within the grid, by passing Mouse.CursorPos to TDBGrid.ScreenToClient()
Then, use TDBGrid.MouseCoord() to determine the row/column indexes of the cell that is underneath the mouse.
Then, check if the cell row/column corresponds to a data cell, and if so then use the TDBGrid.SelectedIndex property to index into the TDBGrid.Columns property.
This is basically the same thing that TDBGrid does internally when firing the OnCellClick event, only it does this in response to a MouseUp event, which provides the mouse coordinates within the grid, thus skipping the 1st step above.
For example:
type
TDBGridAccess = class(TDBGrid)
end;
procedure TMyForm1.DBGrid1DblClick(Sender: TObject);
var
TitleOffset: Byte;
Pt: TPoint;
Cell: TGridCoord;
Column: TColumn;
begin
TitleOffset := Ord(dgTitles in DBGrid1.Options);
Pt := DBGrid1.ScreenToClient(Mouse.CursorPos);
Cell := DBGrid1.MouseCoord(Pt.X, Pt.Y);
if (Cell.X >= TDBGridAccess(DBGrid1).IndicatorOffset) and (Cell.Y >= TitleOffset) then
begin
Column := DBGrid1.Columns[DBGrid1.SelectedIndex];
// use Column as needed...
end;
end;
UPDATE: based on #UweRaabe's comments, you should be able to just use TDBGrid.SelectedIndex by itself:
procedure TMyForm1.DBGrid1DblClick(Sender: TObject);
var
Index: Integer;
Column: TColumn;
begin
Index := DBGrid1.SelectedIndex;
if (Index <> -1) then
begin
Column := DBGrid1.Columns[Index];
// use Column as needed...
end;
end;
I have a TListView on my form. I add some columns in it depending on the input like so:
MyItem := StringListView.Columns.Add;
MyItem.Caption := IntToStr(i);
MyItem.Width := -2;
Afterwards I use the onData event to populate the ListView like this:
procedure TMatrixDictViewerFrame.StringListViewData(Sender: TObject;
Item: TListItem);
var
ItemCaption: string;
ItemText: string;`
begin
ItemCaption := Format('[%d]', [Item.Index]);
ItemText := FItems[Item.Index];
Item.Caption := ItemCaption;
Item.SubItems.Add(ItemText);
end;
It works fine since in the First column I have the Itemcaptions and in the second column I get the Itemtexts. What I couldnt figure out tho is how to populate the ListView depending on the data I get.
For example: I have a matrix A which is a 3x3 Matrix and I want its elements to be shown in this ListView so the first row shows the first 3 row elements, the second row shows the second row three elements and so on. Questions: how can I access the third column? How can I populate the view according to the Index I have (i,j)?
Best regards
The Index property of the list item tells you the row. You are expected to populate the entire row. Like this:
procedure TMatrixDictViewerFrame.StringListViewData(Sender: TObject; Item: TListItem);
begin
// A is a 3x3 matrix, that you obtain by means we don't know
Item.Caption := FloatToStr(A[Item.Index, 0]);
Item.SubItems.Add(FloatToStr(A[Item.Index, 1]));
Item.SubItems.Add(FloatToStr(A[Item.Index, 2]));
end;
I have a DevExpress grid where I would like to add an unbound checkbox to be able to select some of the items.
After the selection is made I press a button and I must loop the grid to get all the selected items.
It has to be a checkbox. I have tried with a multiselectable grid, but the users can't work with that.
I have tried all the samples that I have been able to find on the supportsites, but no luck.
I need the unbound approach since it is a multiuser setup and users have been selecting and deselecting for each other.
My question: does anyone have a working sample that shows how this can be done?
I've done this and it was (is!) pretty ugly! Create the grid view with bound columns and add an unbound checkbox column with a field type of boolean.
Basically I handle the OnCellClick of the grid view. I check if the item clicked is the checkbox column - by finding the first unbound column in the view with a checkbox type. Then I toggle its state.
I've set AutoEdit on the dataset to true but Deleting/Editing/Inserting to false and ImmediateEditor is false. Not exactly sure which of those are important.
I think the hardest thing was trying to fathom out the complex hierarchy of grid and view level objects and working out which levels contained which of the needed bits. I'm sure there's a better way of doing it but what we've got now works and I'm not going to touch it again!
This is lifted from my code but modified slightly and not tested as it stands - it also needs a bit more error checking:
procedure TMyForm.ViewCellClick(Sender: TcxCustomGridTableView;
ACellViewInfo: TcxGridTableDataCellViewInfo; AButton: TMouseButton;
AShift: TShiftState; var AHandled: Boolean);
var
col: TcxGridColumn;
begin
// Manually handle the clicking of the checkbox cell - otherwise it seems
// virtually impossible to get the checked count correct.
col := GetViewCheckColumn(Sender);
if (Sender.Controller.FocusedItem = col) then
begin
ToggleRowSelection(TcxCustomGridTableView(TcxGridSite(Sender).GridView), col);
end;
end;
procedure TMyForm.ToggleRowSelection(AView: TcxCustomGridTableView; ACol: TcxGridColumn);
var
rec: TcxCustomGridRecord;
begin
rec := AView.Controller.FocusedRecord;
if (rec = nil) then exit;
if (rec.Values[ACol.Index] = TcxCheckBoxProperties(ACol.Properties).ValueChecked) then
begin
rec.Values[ACol.Index] := TcxCheckBoxProperties(ACol.Properties).ValueUnchecked;
end
else
begin
rec.Values[ACol.Index] := TcxCheckBoxProperties(ACol.Properties).ValueChecked;
end;
end;
function TMyForm.GetViewCheckColumn(AView: TcxCustomGridView): TcxGridColumn;
var
index: integer;
vw: TcxCustomGridTableView;
item: TcxCustomGridTableItem;
begin
// We're looking for an unbound check box column - we'll return the first
// one found.
Assert(AView <> nil);
result := nil;
if (AView is TcxCustomGridTableView) then
begin
vw := TcxCustomGridTableView(AView);
for index := 0 to vw.ItemCount - 1 do
begin
item := vw.Items[index];
if (item.Properties is TcxCustomCheckBoxProperties) then
begin
if (item is TcxGridDBColumn) then
begin
if (TcxGridDBColumn(item).DataBinding.FieldName = '') then
begin
result := TcxGridColumn(item);
break;
end;
end;
end;
end;
end;
end;
I then extended it by checking for a SPACE bar press in the OnKeyUp of the grid and calling ToggleRowSelection and also similar for a double click on a row.
When iterating through the rows you can test if a row is checked using something like the following:
function TMyForm.GetViewIsRowChecked(AView: TcxCustomGridView; ARecord: TcxCustomGridRecord): boolean;
var
col: TcxGridColumn;
begin
result := False;
col := GetViewCheckColumn(AView);
if ((col <> nil) and (ARecord <> nil)) then
begin
result := (ARecord.Values[col.Index] = TcxCheckBoxProperties(col.Properties).ValueChecked);
end;
end;
I think that's it. I've dug it out of a large grid/view helper unit we've built up over a while. Oh, and it's currently working with Delphi 2010 with DXVCL v2011 vol 1.10.
Hope it helps.
I'm using Delphi XE-2 and DBGrid from Jedi component group (TJvDBGrid). Now, I found it to be very easy to define cell color when values are known, for example:
OnGetCellParams event:
if DBGrid.Field.AsInteger = 0
then Background := clYellow;
but in my case, user can define what value will have what color, that is stored on separate table. And my question, is there a way to color cell by looking up if cell value has a color assign to it?
I would appreciate any help or guidance in the matter, thank you.
The easiest way would probably be to use the form's OnCreate to populate an array, and then access that in the OnGetCellParams event. The array should contain as many items as there are possible values, plus a default value for array index 0 in case a color hasn't been assigned. (Untested, off-the-cuff code follows!)
type
TForm1 = class(TForm)
...
procedure FormCreate(Sender: TObject);
private
FColors: array of TColor;
end;
implementation
procedure TForm1.FormCreate(Sender: TObject);
var
NumRows, i: Integer;
begin
// One row for each possible value for the integer column you're
// trying to color the cell for (eg., if the table can hold a value
// from 0-10, you need the same # of items in the array (array[0..10])
NumRows := NumberOfPossibleValues;
SetLength(FColors, NumberOfPossibleValues);
// Pre-fill the array with the default clWindow color,
// in case a custom color isn't assigned to a value
// (for instance, the user doesn't set a color for a value
// of 7).
for i := 0 to High(FColors) do
FColors[i] := clWindow;
// Assumes your color values are in a database called FieldColors,
// in a datamodule called dmAppData, and that there's a
// column named ColValue indicating the `Field.AsInteger`
// value and the corresponding TColor stored as an integer.
dmAppData.FieldColors.First;
while not dmAppData.FieldColors.Eof do
begin
i := dmAppData.FieldColors.FieldByName('ColValue').AsInteger;
// Might want to put a check here to make sure the value isn't
// more than the number of items in the array!!!
FColors[i] := TColor(dmAppData.FieldColors.FieldByName('Color').AsInteger);
dmAppData.FieldColors.Next;
end;
end;
In your OnGetCellParams event:
Background := FColors[DBGrid.Field.AsInteger];
You might want to use a local variable in the OnGetCellParams to make sure you stay within the array bounds:
Background := clWindow;
i := DBGrid.Field.AsInteger;
if (i > 0) and (i < Length(FColors)) then
Background := FColors[i];
The much slower way would be to do a Locate in the OnGetCellParams event for every single row:
In OnGetCellParams:
Background := clWindow;
if dmAppData.FieldColors.Locate('ColValue', DBGrid.Field.AsInteger, []) then
Background := TColor(dmAppData.FieldColors.FieldByName('Color').AsInteger);
I was wondering how you get a position of a certain object in a list that is created.
Lets say it is like a graphical list where you can click on objects.
Lets say you right click on a object and click "Refresh", how do I get the position of that object so that after the whole list is refreshed (refreshes with a clearlist for some reason) i go back to the same position in the list? This is if the list is say 1000 objects long which makes it bothersome to try and scroll down to the same position after the refresh.
The code uses Tobject but can i do something like
position:=integer(TObject."pointerinfo???");
And after that when the program refreshes
like set the position of the view to the pointer
like currentview(pointer) or something like that?
Actually it doesn't have to be the same object, but the same "view" of the list would be even better.
Thanks in advance
If refreshing is going to give you the same list again, and in the same order, then don't bother discovering anything about the object. Just store the list control's ItemIndex property, which indicates the currently selected item.
If refreshing might give you a different list, then the object you want might not be at the same position afterward, so just remembering ItemIndex won't be enough. In that case, you'll need to find the new position of the object. How to do that depends on the capabilities of the list control and how it exposes the objects associated with each position. If you have a TListBox, for example, then the Items property is a TStrings object, and you can inspect each value of the Objects array until you find the object you want. Store the value of the object reference, and then search for that. Something like this:
procedure Refresh;
var
CurrentSelection: TObject;
ObjectPosition: Integer;
i: Integer;
begin
if List.ItemIndex >= 0 then
CurrentSelection := List.Strings.Objects[List.ItemIndex]
else
CurrentSelection = nil;
RefreshList;
ObjectPosition := -1;
if Assigned(CurrentSelection) then begin
for i := 0 to Pred(List.Count) do begin
if List.Strings.Objects[i] = CurrentSelection then begin
ObjectPosition := i;
break;
end;
end;
end;
if ObjectPosition = -1 then
// Object isn't in refreshed list
else
// It is.
end;
A final possibility is that refreshing doesn't actually keep the same objects at all. All the previous objects are destroyed, and a new list of objects is generated. In that case, you'll have to remember certain identifying characteristics of the original object so you can find the new object that represents the same thing. Something like this:
var
CurrentObject, Person: TPerson;
CurrentName: string;
i, ObjectPosition: Integer;
begin
if List.ItemIndex >= 0 then begin
CurrentObject := List.Strings.Objects[List.ItemIndex] as TPerson;
CurrentName := CurrentObject.Name;
end else
CurrentName = '';
RefreshList;
ObjectPosition := -1;
if CurrentName <> '' then begin
for i := 0 to Pred(List.Count) do begin
Person := List.Strings.Objects[i] as TPerson;
if Person.Name = CurrentName then begin
ObjectPosition := i;
break;
end;
end;
end;
if ObjectPosition = -1 then
// Object isn't in refreshed list
else
// It is.
end;
In all these cases, you should realize that the object's position in the list is not actually a property of the object. Rather, it's a property of the list, which is why the list plays such a bigger role than the object in all that code. There isn't really any "pointerinfo" to get from the object because the object, in general, has no idea it's even in a list, so it certainly won't know its position relative to all the other items in the list.
Once you've determined the position of the object in the list, you need to scroll it into view. For TListBox, a simple way is to set its TopIndex property:
List.TopIndex := ObjectPosition;
And finally, if you don't actually care about the current object at all, but just want to restore the current scroll position, then that's even easier:
procedure Refresh;
var
CurrentPosition: Integer;
begin
CurrentPosition := List.TopIndex;
RefreshList;
List.TopIndex := CurrentPosition;
end;
So I think with the help I got I answered my own question.
What I did was write something that took the x and y position of the listview and later after I did the refresh with a clearlist, I used the scroll function to get back to the same function. My program looks something like this.
procedure Refresh(Sender: TObject);
var
horzpos:integer;
vertpos:integer;
begin
horzpos:=ListView1.ViewOrigin.X;
vertpos:=ListView1.ViewOrigin.Y;
RefreshListView(ListView1); //A function that refreshes the list and clears it
ListView1.Scroll(horzpos, vertpos);
end;
Maybe I should've stated earlier that it was a listview type and that I wanted to get back to the same position again after the "clearlist".
Thanks for all the help, this community rocks!