Math for determining an index of an element based on col / row selection in a grid

I and 2 people tried to figure out what a simple formula should be. I have a grid that can have a variable number of columns and rows. When the user clicks on one of the cells, I have to determine which β€œindex” was clicked based on col / row. Indexes start at 0 and go from left to right, and then from top to bottom. No fixed columns / rows.

An example of what I mean: enter image description here

So, if there are 3 columns, and the user clicks on C2 / R1, then he needs to allow index 5. All indexes start at 0 in this example.

My current formula, which is far from working, is as follows:

//I = Calculated index based on row/col (starts from 0) //ACol = Column index user clicked (starts from 0) //ARow = Row index user clicked (starts from 0) //ColCount = Number of columns I:= (C * ColCount) + (R - ColCount); 

EDIT

For reference, here is my entire component block below.

The line with the formula is marked with the comment "FORMULA HERE"

 unit ImageGrid; interface uses Windows, Classes, SysUtils, Grids, Graphics, StdCtrls, ExtCtrls, Controls, Jpeg, PngImage; type TImageGrid = class; TImageGridItem = class; TGridSizing = (gsManual, gsAuto, gsFit); TImageGridItem = class(TObject) private FFilename: TFilename; FOwner: TImageGrid; FBmp: TBitmap; procedure Event; procedure SetFilename(const Value: TFilename); public constructor Create(AOwner: TImageGrid); destructor Destroy; override; procedure LoadFile; published property Filename: TFilename read FFilename write SetFilename; end; TImageGrid = class(TCustomDrawGrid) private FItems: TStringList; FCacheDir: String; FRowHeight: Integer; FColWidth: Integer; FSizing: TGridSizing; FItemIndex: Integer; procedure SetCacheDir(const Value: String); procedure SetColWidth(const Value: Integer); procedure SetRowHeight(const Value: Integer); procedure SetSizing(const Value: TGridSizing); function GetItem(Index: Integer): TImageGridItem; procedure SetItem(Index: Integer; const Value: TImageGridItem); procedure SetItemIndex(const Value: Integer); procedure SelectCell(Sender: TObject; ACol, ARow: Longint; var CanSelect: Boolean); protected procedure Paint; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function Count: Integer; property Items[Index: Integer]: TImageGridItem read GetItem write SetItem; default; function Add: TImageGridItem; procedure Delete(const Index: Integer); procedure Clear; published property CacheDir: String read FCacheDir write SetCacheDir; property ColWidth: Integer read FColWidth write SetColWidth; property RowHeight: Integer read FRowHeight write SetRowHeight; property Sizing: TGridSizing read FSizing write SetSizing; property ItemIndex: Integer read FItemIndex write SetItemIndex; property Align; property Anchors; property BevelEdges; property BevelInner; property BevelKind; property BevelOuter; property BevelWidth; property BiDiMode; property BorderStyle; property Color; property ColCount; property Constraints; property Ctl3D; property DefaultColWidth; property DefaultRowHeight; property DefaultDrawing; property DoubleBuffered; property DragCursor; property DragKind; property DragMode; property DrawingStyle; property Enabled; property Font; property GradientEndColor; property GradientStartColor; property GridLineWidth; property Options; property ParentBiDiMode; property ParentColor; property ParentCtl3D; property ParentDoubleBuffered; property ParentFont; property ParentShowHint; property PopupMenu; property ScrollBars; property ShowHint; property TabOrder; property Touch; property Visible; property OnClick; property OnColumnMoved; property OnContextPopup; property OnDblClick; property OnDragDrop; property OnDragOver; property OnDrawCell; property OnEndDock; property OnEndDrag; property OnEnter; property OnExit; property OnGesture; property OnGetEditMask; property OnGetEditText; property OnKeyDown; property OnKeyPress; property OnKeyUp; property OnMouseActivate; property OnMouseDown; property OnMouseEnter; property OnMouseLeave; property OnMouseMove; property OnMouseUp; property OnMouseWheelDown; property OnMouseWheelUp; property OnRowMoved; property OnSelectCell; property OnSetEditText; property OnStartDock; property OnStartDrag; property OnTopLeftChanged; end; implementation //Register procedure will come later... { TImageGrid } function TImageGrid.Add: TImageGridItem; begin Result:= TImageGridItem.Create(Self); FItems.AddObject('', Result); end; procedure TImageGrid.Clear; begin while Count > 0 do Delete(0); end; function TImageGrid.Count: Integer; begin Result:= FItems.Count; end; constructor TImageGrid.Create(AOwner: TComponent); begin inherited; Options:= [goFixedVertLine,goFixedHorzLine,goVertLine,goHorzLine, goRangeSelect,goThumbTracking]; Parent:= TWinControl(AOwner); FItems:= TStringList.Create; FixedCols:= 0; FixedRows:= 0; RowCount:= 1; ColCount:= 1; FColWidth:= 100; FRowHeight:= 100; ColWidths[0]:= FColWidth; RowHeights[0]:= FRowHeight; FSizing:= gsManual; FItemIndex:= -1; OnSelectCell:= SelectCell; Invalidate; end; procedure TImageGrid.Delete(const Index: Integer); begin if (Index >= 0) and (Index < FItems.Count) then begin TImageGridItem(FItems.Objects[Index]).Free; FItems.Delete(Index); end else begin raise Exception.Create('List index out of bounds ('+IntToStr(Index)+')'); end; end; destructor TImageGrid.Destroy; begin FItems.Free; inherited; end; function TImageGrid.GetItem(Index: Integer): TImageGridItem; begin if (Index >= 0) and (Index < FItems.Count) then begin Result:= TImageGridItem(FItems.Objects[Index]); end else begin Result:= nil; raise Exception.Create('List index out of bounds ('+IntToStr(Index)+')'); end; end; procedure TImageGrid.Paint; var Bmp: TBitmap; C: Integer; RC: Integer; CC: Integer; X, Y: Integer; I: TImageGridItem; R: TRect; procedure DrawImage(B: TBitmap; const R: TRect); begin Canvas.StretchDraw(R, B); end; begin Canvas.Brush.Style:= bsSolid; Canvas.Pen.Style:= psClear; Canvas.Brush.Color:= clWhite; Canvas.FillRect(Canvas.ClipRect); Bmp:= TBitmap.Create; try if Count > 0 then begin case FSizing of gsManual: begin //Draw like regular grid with variable sizes - expand rows as needed inherited; end; gsAuto: begin //Calculate image width based on col count end; gsFit: begin //Calculate col count based on image width CC:= Trunc(ClientWidth / FColWidth); RC:= Trunc(Count / CC); ColCount:= CC; RowCount:= RC; for X := 0 to ColCount - 1 do ColWidths[X]:= FColWidth; for X := 0 to RowCount - 1 do RowHeights[X]:= FRowHeight; Canvas.Brush.Style:= bsSolid; Canvas.Pen.Style:= psSolid; Canvas.Brush.Color:= clWhite; Canvas.Pen.Width:= 1; C:= 0; //C = Count of items to show for X := 0 to ColCount - 1 do begin for Y := 0 to RowCount - 1 do begin I:= Self.Items[C]; if C = FItemIndex then begin Canvas.Pen.Color:= clRed; Canvas.Pen.Width:= 2; end else begin Canvas.Pen.Color:= clNavy; Canvas.Pen.Width:= 1; end; R:= CellRect(X,Y); Canvas.Rectangle(R); InflateRect(R, -4, -4); Canvas.StretchDraw(R, I.FBmp); C:= C + 1; end; end; end; end; end else begin ColCount:= 1; RowCount:= 1; end; finally Bmp.Free; end; end; procedure TImageGrid.SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); var I: Integer; C, R: Integer; begin //Determine item index based on col/row C:= ACol; R:= ARow; //I = Calculated index based on row/col (starts from 0) //ACol = Column index user clicked (starts from 0) //ARow = Row index user clicked (starts from 0) //ColCount = Number of columns I:= (R * ColCount) + C; // <<----- FORMULA HERE if I < Count - 1 then begin FItemIndex:= I; CanSelect:= True; end else begin FItemIndex:= -1; CanSelect:= False; end; Invalidate; end; procedure TImageGrid.SetCacheDir(const Value: String); begin if Value <> FCacheDir then begin FCacheDir := Value; Invalidate; end; end; procedure TImageGrid.SetColWidth(const Value: Integer); begin if Value <> FColWidth then begin FColWidth := Value; Invalidate; end; end; procedure TImageGrid.SetItem(Index: Integer; const Value: TImageGridItem); begin if (Index >= 0) and (Index < FItems.Count) then begin FItems.Objects[Index]:= Value; Invalidate; end else begin raise Exception.Create('List index out of bounds ('+IntToStr(Index)+')'); end; end; procedure TImageGrid.SetItemIndex(const Value: Integer); begin if Value <> FItemIndex then begin FItemIndex := Value; Invalidate; end; end; procedure TImageGrid.SetRowHeight(const Value: Integer); begin if Value <> FRowHeight then begin FRowHeight := Value; Invalidate; end; end; procedure TImageGrid.SetSizing(const Value: TGridSizing); begin if Value <> FSizing then begin FSizing := Value; Invalidate; end; end; { TImageGridItem } constructor TImageGridItem.Create(AOwner: TImageGrid); begin FOwner:= AOwner; FBmp:= TBitmap.Create; end; destructor TImageGridItem.Destroy; begin FBmp.Free; inherited; end; procedure TImageGridItem.Event; begin FOwner.Invalidate; end; procedure TImageGridItem.LoadFile; //Determine file type and load accordingly function GetImage(const Filename: String; B: TBitmap): Bool; var E: String; IJ: TJpegImage; IP: TPngObject; begin Result:= False; if FileExists(Filename) then begin E:= UpperCase(ExtractFileExt(Filename)); if E = '.BMP' then begin B.LoadFromFile(Filename); Result:= True; end else if (E = '.JPG') or (E = '.JPEG') then begin IJ:= TJpegImage.Create; try IJ.LoadFromFile(Filename); B.Assign(IJ); Result:= True; finally IJ.Free; end; end else if E = '.PNG' then begin IP:= TPngObject.Create; try IP.LoadFromFile(Filename); B.Assign(IP); Result:= True; finally IP.Free; end; end else begin raise Exception.Create('Invalid file extension ('+E+')'); end; end; end; begin GetImage(FFilename, FBmp); end; procedure TImageGridItem.SetFilename(const Value: TFilename); begin if Value <> FFilename then begin FFilename := Value; LoadFile; Event; end; end; end. 

RESOLVED

Thanks to the answer below, I am corrected. I thought that at first there was an incorrect formula, but after defining more of my code, I found that in another place it was wrong to iterate over rows and columns. Below is an example of how it should work ...

 for Y := 0 to RowCount - 1 do begin for X := 0 to ColCount - 1 do begin 

But what I had before was ...

 for X := 0 to ColCount - 1 do begin for Y := 0 to RowCount - 1 do begin 
+6
source share
1 answer

The following should do the trick

 (R * ColCount) + C 

For your examples, this will become

 (2 * 3) + 2 = 8 

and ... if there are 3 columns and the user clicks on C2 / R1, then he needs to allow index 5

 (1 * 3) + 2 = 5 
+16
source

Source: https://habr.com/ru/post/906043/


All Articles