Data mizí v Gridu pod rukama, co teď?

Většina z vás patrně ví, jak je snadné přetřídit data v Gridu (mřížce) kliknutím na nadpis sloupce pouhým napsáním příkazu SET ORDER TO ... v Click() metodě Headeru (hlavičky).
Velkou nevýhodou tohoto postupu ale je, že řádek na kterém právě jsme, odroluje a uživatel se najednou dívá na zcela jiná data než před kliknutím. Pokud se chceme této nepříjemnosti vyhnout, je třeba k tomuto problému přistoupit systematicky. V Click() metodě sloupce zavoláme metodu Reorder() přidanou k výchozímu Gridu (k třídě):

* Tento objekt(hlavička).Otec(sloupec).Otec(otec sloupce - grid)
This.Parent.Parent.Reorder(This.Parent,.T.)

* Obsah metody Reorder()
* Význam parametrů:
* poCol - reference (odkaz) na sloupec, podle kterého třídíme
* plAscending - .T. = vzestupně, .F. = sestupně
LPARAMETER poCol,plAscending
LOCAL i, j, llLockScreen, lnRecno, lnRelativeRow, lnDirection, lcDescending, lcTag
* Zapamatuje me si, na kterém RELATIVNÍM ŘÁDKU NA OBRAZOVCE jsme
lnRelativeRow = poCol.Parent.RelativeRow
lcDescending =  IIF(! plAscending, "descending", "ascending")
* Do lcTag uložíme jméno položky, podle které chceme třídit
lcTag = NORMALIZE(poCol.ControlSource)
* Zdroj může být ve tvaru TABULKA.POLOŽKA
*  takže je nutné aliasy odstranit
lcTag=STRTRAN(lcTag,NORMALIZE(poCol.Parent.RecordSource)+".","")
* Zjišťujeme, zda existuje tag, který bychom mohli použít
i = 1
DO WHILE NOT EMPTY(TAG(i)) AND NOT NORMALIZE(KEY(i))==lcTag
   i = i+1
ENDDO
* Pokud jsem nenašli potřebný index, pokusíme se najít takový,
*  který nejvíce odpovídá při porovnání zleva
IF EMPTY(TAG(i))
   i = 1
   DO WHILE NOT EMPTY(TAG(i)) AND NOT LEFT(NORMALIZE(KEY(i)),LEN(lcTag))==lcTag
      i = i+1
   ENDDO
ENDIF
IF !EMPTY(TAG(i))
   * Takový tag existuje:
   * Vypneme zobrazení, aby obrazovka zbytečně neblikala
   llLockScreen = Thisform.LockScreen
   Thisform.LockScreen = .T.
   * Zapamatujeme si větu, na které jsme
   lnRecno=RECNO()
   * Nastavíme třídění
   SET ORDER TO (TAG(i)) &lcDescending.
   * Překreslíme Grid (poprvé :))
   poCol.Parent.Refresh()
   * Skočíme zpátky na původní větu
   GO lnRecno
   * Překreslíme Grid (podruhé :):))
   poCol.Parent.Refresh()
   * Pokud nejsme na stejném řádku jako na začátku
   IF (lnRelativeRow != poCol.Parent.RelativeRow)
      * Určíme počet řádků, o které se musíme posunout...
      lnDirection = IIF(lnRelativeRow>poCol.Parent.RelativeRow,0,1)
      j = ABS(lnRelativeRow - poCol.Parent.RelativeRow)
      * ... a posuneme se 
      FOR i = 1 TO j
          poCol.Parent.DoScroll(lnDirection)
      ENDFOR
   ENDIF
   * Překreslíme Grid (potřetí :):):))
   poCol.Parent.Refresh()
   * A uvedeme zobrazení do původního stavu
   Thisform.LockScreen = llLockScreen
ELSE
   * Nenašli jsme tag, který bychom mohli použít
   WAIT WINDOW "Data nelze přetřídit..." NOWAIT TIMEOUT 5
ENDIF

A to je vše. Zdraví vás
Milan Kosina, kosina@vol.cz


Původní zdrojový kód je v SOFTWAROVÝm QUASu 2000/33 na stránkách 17/18.
Zdrojový kód metody lze stáhnout zde.

Součástí zdrojového programu jsou dvě procedury:
Reorder() je totožná s výše uvedeným zdrojovým kódem
ReorderEx() je totožná s výše uvedeným zdrojovým kódem s drobnou úpravou; jako první parametr se dá poslat název tagu

Pozn. přepis.: Původní zdrojový kód v sobě obsahoval dvě drobné chybičky. Navíc nepočítal s tím, že index, ale i zdroj sloupce, může být složen z více položek. Nyní když není nalezen index, který plně odpovídá zdroji dat sloupce, pak se najde první index, který zdroji dat sloupce nejvíce odpovídá při porovnání zleva.
Je zde také zapomenuto, že událost Click() hlavičky sloupce se vyvolá i když uživatel roztáhne sloupec (tím dojde i k nechtěnému vyvolání přetřídění). Tomu lze zabránit přídáním metody SplitLineHitTest() do třídy gridu, která zjišťuje, zda je uživatel nad pravým okrajem sloupce. Vlastní kód metody je v článku Grid - část první (dokončení) (až na konci). Před zavoláním metody Reorder() by se měla volat metoda SplitLineHitTest() s parametrem This (tento sloupec). Pokud metoda vrátí .F. je ukazatel myši mimo pravý okraj sloupce a může se tedy volat metoda Reorder().
Pozn. přepis.: Pokud vám to nebude fungovat, pak vězte, že váš návrh tříd je poněkud prohnilý. (Nezoufejte, nejste sami. V době, kdy čtete tento článek, pravděpodobně stále hledám proč to v mým frameworku nefunguje.)