The approach is based on a simple principle. LeftColumn property of the grid represents a column that is currently the leftmost visible column in the grid with current horizontal scrolling. As we scroll the grid horizontally, this property changes. We can assign this number to the ColumnOrder property of the column we want to hold and that’s all:
Code:
this.Columns(1).ColumnOrder = this.LeftColumn
You can use this in the Scrolled and AfterRowColChange events of the grid. However, there is a little problem with this: order of columns spoiled after few scrolling forward and back. This is because column order changes using principle "replace older column by newer on that place", so older column is placed on place of newer column. Column orders of all other columns are not changed. This causes the problem. This FAQ describes an approach that allows to organize freezing of the column and workaround most issues.
The code is a very basic and simple to give the basics of the approach. Code samples could be improved to work correctly in case user change order of columns, as well as improve the logic related to the focus change.
Description
Following is a code that updates columns in grid to make first column always leftmost. Use it in the Scrolled event when grid is scrolled horizontally and in the AfterRowColChange event when active column is changed in the grid.
Code:
local i
if this.columns(1).ColumnOrder < this.LeftColumn
&& we scroll forward
for i=this.columns(1).ColumnOrder to this.LeftColumn-1
this.columns(1).ColumnOrder = this.columns(1).ColumnOrder + 1
endfor
else
&& we scroll backward
for i=this.LeftColumn+1 to this.columns(1).ColumnOrder
this.columns(1).ColumnOrder = this.columns(1).ColumnOrder - 1
endfor
endif
The above code is very simple because it is written only for the column 1 and only one column frozen. The approach change ColumnOrder of the column by 1. This cause correct moving of column to required place like if you move the column by dragging it by header. This save the original order of columns and solve any problems with confusing order change after scrolling. Use this code in the AfterRowColChange event (when column changed) or in the Scrolled event (when grid is scrolled horizontally, in other words, when nDirection parameter of the Scrolled event is greater or equal 4).
For more complex case (more than one column) you will require to do similar things. For example, following is for 2 columns:
Code:
local i, lnCurrOrder
m.lnCurrOrder = this.columns(1).ColumnOrder
if this.columns(1).ColumnOrder < this.LeftColumn
&& we scroll forward
for i=m.lnCurrOrder to this.LeftColumn-1
this.columns(2).ColumnOrder = this.columns(2).ColumnOrder + 1
endfor
for i=m.lnCurrOrder to this.LeftColumn-1
this.columns(1).ColumnOrder = this.columns(1).ColumnOrder + 1
endfor
else
&& we scroll backward
for i=this.LeftColumn+1 to m.lnCurrOrder
this.columns(1).ColumnOrder = this.columns(1).ColumnOrder - 1
endfor
for i=this.LeftColumn+1 to m.lnCurrOrder
this.columns(2).ColumnOrder = this.columns(2).ColumnOrder - 1
endfor
endif
Finally, when doing manipulations with column objects on the form, the current focused column might be lost. The workaround for this is to remember current focused column and set the focus back after moving "locked" columns. So, wrap the column(s) moving code (above sample code) into the following code:
Code:
lnCol = this.ActiveColumn
lnColToFocus = 0
if lnCol > 0
for i=1 to this.ColumnCount
if this.columns(m.i).ColumnOrder = m.lnCol
lnColToFocus = m.i
exit
endif
endfor
endif
* Check if we do not need to set focus to the last column in grid
* This will happen when we did not focused column explicitly by
* mouse and pressed either Shift+TAB, left arrow key or Ctrl+left
* arrow key. In the last condition we check if column focused is
* the locked column. When you have 2 columns, you should check for
* both (for example,
* "AND (m.lnColToFocus = 1 OR m.lnColToFocus = 2)")
if not MDOWN() AND inlist(lastkey(),15,19,26) AND (m.lnColToFocus = 1)
this.Columns(this.ColumnCount).SetFocus
m.lnColToFocus = this.ColumnCount
endif
* ....... Put here code from previous samples
if lnColToFocus > 0
if this.Columns(m.lnColToFocus).ColumnOrder > this.LeftColumn
this.Columns(m.lnColToFocus).SetFocus
else
* Determine the next column after locked and focus it
for i=1 to this.ColumnCount
if this.columns(m.i).ColumnOrder = this.Columns(1).ColumnOrder + 2
lnColToFocus = m.i
exit
endif
endfor
this.Columns(m.lnColToFocus).SetFocus
endif
endif
The side effect of the above code is that "TAB" key pressing in last column when it is active will cause moving to the first column that is not locked. This is a good thing also, because usually locked column is used as information column when others are for data entry. To do opposite (SHIFT+TAB in the first not locked column will focus the last column), we should add some code in the AfterRowColChange event to fix the active column - appropriate section of the code in above sample is marked by comment "Check if we do not need to set focus to the last column in grid".
There is also a problem - when scrolling horizontally and focused column goes out of scrolled area, we just cannot restore active column because this will scroll the grid back. In such case it is good idea to set the focus to the column just after locked column(s) in current scrolling. This column will have next ColumnOrder value after the locked column. This workaround is after the comment "Determine the next column after locked and focus it".
Vlad Grynchyshyn