DrawColumnCell

I am trying to convert a comple TDBGrid to a TDBAdvGrid. I am using OnDrawColumnCell to perform some color changes on particular cells based on user change of cell value and also a dataset connected. I cannot find an event to mimic this in DBADVGrid. The code is simple but effective need to know what event and similar parameters I can use with TDBAdvGrid. Your help would be GREATLY appreciated.

CODE SAMPLE:

procedure TfrmInvoiceSalesOrders.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);

var
Field: TField;
begin
Field := Column.Field;
if Assigned(Field) and SameText(Field.FieldName, 'OpenQty') then
begin
if Field.AsInteger = cdsOpenOrders.FieldByName('SOD_TOSHP').Value then
DBGrid1.Canvas.Brush.Color := clAqua
end;

if Assigned(Field) and SameText(Field.FieldName, 'SOD_TOSHP') then
begin
if cdsOpenOrdersSOD_TOSHP.Value > 0 then
begin
DBGrid1.Canvas.Brush.Color := clMoneyGreen ;
DbGrid1.canvas.Font.Color := clBlack ;
end
else
begin
DBGrid1.Canvas.Brush.Color := clMedGray ;
DBGrid1.Canvas.Font.Color := clBlack ;
end;

// if (gdSelected in State) then
// begin
// Dbgrid1.Canvas.Brush.Color := $4bf92e ;
// dbGrid1.Canvas.Font.Style := font.Style + [ fsBold ] ;
// DBGrid1.Canvas.Font.Color := clHighlightText ;
//
// end;

end;

if Assigned(Field) and SameText(Field.FieldName, 'InvOnh') then
begin
if Field.AsInteger <= 0 then
DBGrid1.Canvas.Brush.Color := clRed
end;
if (gdSelected in State) then
begin
Dbgrid1.Canvas.Brush.Color := $4bf92e ;
dbGrid1.Canvas.Font.Style := font.Style + [ fsBold ] ;
DBGrid1.Canvas.Font.Color := clHighlightText ;
end;

if Assigned(Field) and SameText(Field.FieldName, 'SOD_CLOSE') then
begin
DrawCheckBoxes(Sender,Rect,Column);
end
else DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);

end;

To change colors depending on cell values, did you consider the technique demonstrated in the demo ADOSelColor under Demo\DBAdvGrid\ADOSelColor
This is simple to achieve via the OnGetCellColor event.

The change I need to make in color is for the Cell ONLY not the whole row. Also I need to know what is the Column.Tfield Value of the cell (I declare Field : TField and Column : Tcolumn in the procedure, then assign Field := Column.TField) and then I compare that with something else in the dataset and color that cell accordingly (see code and image). if the customer ordered one and 1 was shipped the QTY open goes to clAcqua and the Qty to Ship goes to ClMoneyGreen.. These colors are also used later in a different purpose. Is the TField value of the cell being drawn available in DBAdvGrid ? I cannot find that. I am trying to convert from a TDBGrid to TDBAdvGrid because of the CheckBox but having trouble figuring this part out. Your help is appreciated.

Hope I am explaining well enough.
THANKS !!

CODE SNIP:

procedure TfrmInvoiceSalesOrders.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);

var
Field: TField;
begin
Field := Column.Field;
if Assigned(Field) and SameText(Field.FieldName, 'OpenQty') then
begin
if Field.AsInteger = cdsOpenOrders.FieldByName('SOD_TOSHP').Value then
DBGrid1.Canvas.Brush.Color := clAqua
end;

if Assigned(Field) and SameText(Field.FieldName, 'SOD_TOSHP') then
begin
if cdsOpenOrdersSOD_TOSHP.Value > 0 then
begin
DBGrid1.Canvas.Brush.Color := clMoneyGreen ;
DbGrid1.canvas.Font.Color := clBlack ;
end
else
begin
DBGrid1.Canvas.Brush.Color := clMedGray ;
DBGrid1.Canvas.Font.Color := clBlack ;
end;

end;
end

image

In other words LOL, what would be the best property in DBAdvGrid.SOMETHING.. to refer the column name or the field name associated with that column for this same purpose. I am seeing that there many properties, I can also just refer the fixed column number but if that changes then will cause an error in the future. I cannot figure out a way to refer the cells fieldname without having to use the column number somehow. Is there anyway to extract the fieldname or the column name of the cell being drawn ??

grid.Columns[index].FieldName
or
grid.FieldAtColumn[index]: TField

Bruno yes that works to give me the field name value of column in focus. Something I am not understanding is how the GetCellColor event works and triggers. This is making my head spin and should be very simple. To go back to what I am trying to accomplish. The grid allows the user to edit the ShipQty. When that is changed I need to check the value of that versus the one in the QtyOpen. If those match then I need the cell to change to Aqua otherwise Green its that simple but not happening. What is happening is very strange. It changes the whole column and then when you scroll it changes some cells and others stay aqua, and then scroll again and it changes again without any change to the value of the cell. I also went back to the documentation on TAdvStingGrid and found the Inst[ACol,ARow] property and thought I could get cell value there but that property is not available in DBAdvGrid, why is that ? I am almost thinking that there may be an issue since this is data aware yet the coordination between the actual datasource and the grid or not in sequence. Could this be another issue with DtSequenced or NonSequenced ?

It is my understanding that GetCellColor triggers when redrawing cells, is it perhaps that another event needs to fire some other code when the user changes the value of the QtyShip cell ??

HELP !! I am at a total loss and wondering if its just that this component will not work for this ? I have spent ALOT of time on this and dont understand why I am getting the result I am getting at runtime.

The only reason I am using TDBADVGrid and replacing TDBGrid is for the CheckBox from another cell. You have the code in the original post that I use on TDBGrid to accomplish this change of color and it works perfectly with OnDrawColumnCell. I have a feeling that OnGetCellColor is not the same thing ??

Here is the simple snippet of the code with DBADVGrid that is not working:


procedure TfrmInvoiceSalesOrders.DBAdvGrid1GetCellColor(Sender: TObject; ARow,
ACol: Integer; AState: TGridDrawState; ABrush: TBrush; AFont: TFont);
Var
ColField : String;
begin
ColField := DBAdvgrid1.FieldAtColumn[ACol].FieldName ;

if ColField = 'OpenQty' then
begin
if cdsOPenOrders.FieldByName('OpenQty').AsInteger = cdsOpenOrders.FieldByName('SOD_TOSHP').AsInteger then

// if DBAdvGrid1.Ints[ACol,ARow] then

begin
  ABrush.Color := clAqua ;
end
else
  ABrush.Color := clMoneyGreen ;

end;
if ColField = 'invonh' then
begin
if cdsOPenOrders.FieldByName('invonh').AsInteger = 0 then
begin
ABrush.Color := clRed ;
end
else
ABrush.Color := clGray ;
end;


Here is the Code with DBAdvGrid that works perfectly


procedure TfrmInvoiceSalesOrders.DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);

var
Field: TField;
begin
Field := Column.Field;
if Assigned(Field) and SameText(Field.FieldName, 'OpenQty') then
begin
if Field.AsInteger = cdsOpenOrders.FieldByName('SOD_TOSHP').Value then
DBGrid1.Canvas.Brush.Color := clAqua
end;

if Assigned(Field) and SameText(Field.FieldName, 'SOD_TOSHP') then
begin
if cdsOpenOrdersSOD_TOSHP.Value > 0 then
begin
DBGrid1.Canvas.Brush.Color := clMoneyGreen ;
DbGrid1.canvas.Font.Color := clBlack ;
end
else
begin
DBGrid1.Canvas.Brush.Color := clMedGray ;
DBGrid1.Canvas.Font.Color := clBlack ;
end;

// if (gdSelected in State) then
// begin
// Dbgrid1.Canvas.Brush.Color := $4bf92e ;
// dbGrid1.Canvas.Font.Style := font.Style + [ fsBold ] ;
// DBGrid1.Canvas.Font.Color := clHighlightText ;
//
// end;
end;
end

HELP !!!! LOL

Based on my own use of the TMS grids, I think you would find things much easier if you completely stopped trying to modify colours in other cells every time one changes. If you have a cell which you know should be green when A>B and otherwise acqua, just put that logic into the OnGetCellColor event for that row/column/cell. Then if a change occurs elsewhere which could affect this cell, simply invalidate the row or the column or the grid and it will repaint itself, using the colours which each cell works out for itself, on its own, in the OnGetCellColor event. This may be a change in the way you think about the problem, but is usually the simplest approach.

Tim I hear you but I dont understand why that would be any different. This is a grid with 2 editable columns. One is ShipQty and the other is Close(0 or 1)Checkbox . Whenever the user changes a SPECIFIC cell in COLUMN ShipQty if that amount after editing equals the QtyOrd Column on that row I need the color of ShipQty to change to Aqua, But ONLY that cell not the whole column. So I am not sure what you are suggensting exactly as to code change that is not already being addressed above in the simple matter you explained. Please elaborate or give me an example. I appreciate your input and response.

Eddie

Obviously I do not have all your code, so it is hard to say what is best for you. But I got the impression that you were trying to combine OnGetCellColor and also using DrawColumnCell to specifically draw a cell in a different colour when another cell was changed. The point I was attempting to make is that if OnGetCellColor knows how to colour the cell based on the content of its neighbours, there should never be any need to draw the cell explicitly. It should be sufficient to invalidate the affected grid rectangle when a cell is changed that affects the cell in question, and just allow this affected cell to paint itself based on your logic. If it is convenient to invalidate the whole row, or other cells in the grid rectangle, that know how to paint themselves, they will not change when you invalidate the grid rectangle.

My goal was to help stop your head spinning, that is all. I thought that the confusion was caused by your trying to explicitly paint one cell when another changed. Because of the way that the TMS grids are designed, I have never found a need to do this. My only colour logic is in the onGetCellColor event handler. There is not much more I can say about this, I am afraid; perhaps someone else can put it more clearly.

I greatly appreciate the input dont take me wrong. If you look at my earlier post and the image of the grid I posted that will show you what I was able to accomplish with DBGrid that I cannot seem to make happen with DBAdvGrid. Take a look at that and let me know your thoughts. And yes one cell change needs to affect that cells color and possibly the color of another. Was able to do it easy with DBGriid and using TField. I am sure there is something I am not understanding about the dynamics of DBAdvGrid and hope I find the answer here. Thanks again for your time and input, appreciated !!!!

Sorry, I cannot drill down into the details to help you further. And I have clearly failed in my attempt to suggest you consider the strategy, not the tactics.

Again, if you want to change cell colors, you should use OnGetCellColor. This event passes the column AND row indexes, so using column and row index, you can change the color per cell.
You can get the TField at any column with grid.FieldAtColumn(columnindex): TField;
Once you get OnGetCellColor right, you should see it is actually much simpler than using OnDrawColumnCell.

I realize how simple and easy this is and should be but either I am not understanding or something works different than what I am reading. Let me ask, when the grid redraws I would assume that as it redraws since it is associated with a dataset, it is set to DtSequenced, so the record pointer in the dataset moves during redraw ?? Is that correct ?? So here is a simple snippet of what I am attempting to do, like you said it is simple and this code is simple so please help me understand why the entire column either paints Aqua when I change the column and data matches, but when I move the cursor to the other row if that one doesnt match the entire column either changes to Aqua or the Default color ??? I am also attaching two images of the grid for you to see. HELP !!


procedure TfrmInvoiceSalesOrders.DBAdvGrid1GetCellColor(Sender: TObject; ARow,
ACol: Integer; AState: TGridDrawState; ABrush: TBrush; AFont: TFont);
Var
Field : TField ;
begin
Field := DBAdvGrid1.FieldAtColumn[ACol] ;

if SameText(Field.FieldName, 'OpenQty') then
begin
if Field.AsInteger = cdsOpenOrders.FieldByName('SOD_TOSHP').Value then
ABrush.Color := clAqua
end;
end;


snip1 Snip2

To make this way easier, use grid.Cells[] to retrieve data from other fields (and if you do not want this field to be visible, add the field that is the criteria to set the color in a hidden column.
Again, the ADOSelColor demo application demonstrates this.

WOO HOO So I got it working with the following code. There is one thing I am still having issue with and It has to do with the scrolling with the mouse. When the scroll gets to the bottom of grid it creates an empty row. That causes an error in code when I check the value of that because the value is ''. What causes that empty row at the bottom and can I get rid of that ? I added code to check for an empty row and that fixes the program from crashng out there but It looks strange at the bottom and would rather it not be there. Code and Snip Follow.

image

Heres the Code, you will see at the bottom I had to check for ;= ''' to avoid error due to that blank row at the bottom.


procedure TfrmInvoiceSalesOrders.DBAdvGrid1GetCellColor(Sender: TObject; ARow,
ACol: Integer; AState: TGridDrawState; ABrush: TBrush; AFont: TFont);
begin
if (ARow > 0) then
begin
if (Acol = 9) then
Begin
if (DBAdvGrid1.cells[9, ARow] = DBAdvGrid1.cells[10, ARow]) then
ABrush.Color:= clAqua
else
ABrush.Color:= clWhite
End;
if (Acol = 10) then
Begin
if (DBAdvGrid1.cells[9, ARow] = DBAdvGrid1.cells[10, ARow]) then
ABrush.Color:= clMoneyGreen
else
ABrush.Color:= clGray
End;
if (Acol = 8) then
begin
if DBAdvGrid1.cells[8, ARow] <> '' then *** THIS IS THE LINE TO AVOIDE ERROR
begin
if strtoint(DBAdvGrid1.cells[8, ARow]) = 0 then
ABrush.Color:= clRed
else
ABrush.Color:= clWhite
end;
end;
end;
end;

As you can see, the ADOSelColor is not doing anything special for the last row and it is also accessing grid.Cells[x,ARow]
Do you have any special / different grid settings than these in the ADOSelColor demo?

The only thing I could find after comparing both was Mouse setting for Scroll which I set to 1 based on your recommendation above. Also the look property in mine is glCustom in the demo its glXP. Do any of those 2 you think may be the cause of that ?

There is something with either the mouse or the general grid scroll that is setting the ARow to perhaps nil at some point and causing error. This is my last hurdle, your help is greatly appreciated.

This is what I am getting during various navigation of the grid. The Mouse or Page up or Down or arrow up or down. It seems to draw a blank row temporarily and that is causing the error. That seems to be a class level issue and dont know if its a setting on the grid properties. I changed the code a bit to use integer values and see if that was the problem but the error continues.

image

UPDATED CODE:


procedure TfrmShippingScreen.DBAdvGrid1GetCellColor(Sender: TObject; ARow,
ACol: Integer; AState: TGridDrawState; ABrush: TBrush; AFont: TFont);
begin
if ARow > 0 then
begin
if Acol = 9 then
Begin
if (DBAdvGrid1.cells[9, ARow].ToInteger = DBAdvGrid1.cells[10, ARow].ToInteger) then
ABrush.Color:= clAqua
else
ABrush.Color:= clWhite
End;
if Acol = 10 then
Begin
if (DBAdvGrid1.cells[9, ARow].ToInteger = DBAdvGrid1.cells[10, ARow].ToInteger) then
ABrush.Color:= clMoneyGreen
else
ABrush.Color:= clGray
End;
if Acol = 8 then
begin
if DBAdvGrid1.cells[8, ARow].ToInteger >= 0 then
begin
if DBAdvGrid1.cells[8, ARow].ToInteger = 0 then
ABrush.Color:= clRed
else
ABrush.Color:= clWhite
end;
end;
end;
end;

Also...Here is the debug break point, sending in case you want it although it is obvious that its a cell that is returning a blank when scrolling. I already verified that dataset, it is numeric and no nil records. Please help as this is the last thing holding this up. I can run a check on every comparison to see if there is a blank but that is not the intent of this function. Please advise if this is a possible setting or maybe a bug.

I worked around the issue with the following but would like to know whats causing this. Ill write a do loop to check all cells in the row in the future if this workaround is the only solution.


if not (DBAdvGrid1.cells[9, ARow] = '') and not(DBAdvGrid1.cells[10, ARow] = '') and not (DBAdvGrid1.cells[8, ARow] = '') then
begin


Let me know please.

Eddie