Search  
Friday, February 10, 2012 ..:: Forum ::.. Register  Login
 Forum Minimize
Pentru a putea posta mesaje trebuie să vă înregistraţi.
Notă: Mesajele cu conţinut jignitor sau ilegal (inclusiv cereri de soft piratat) nu sunt acceptate şi vor fi şterse imediat .

Pentru a primi raspunsuri rapide si corecte, scrieti in mesaj ce intentionati sa faceti, ce mesaj de eroare primiti, in ce context si in urma caror actiuni. De asemenea, mentionati versiunea de FoxPro in care lucrati!
Dacă nu specificați versiunea, se consideră VFP 9.0 SP2.

SearchForum Home
  Visual FoxPro  Visual FoxPro in general  Cum pot sa fac ...
 Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 
 8/7/2010 4:17:28 PM
User is offlinevicos
137 posts
5th


Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Am 2 CursorAdaptere cu ADO atasate de grid, unul pt Antet altul pt Detali.
Am un timer care la 1 secunda executa Requery (sau CursorRefresh) pe gridul ANTET, pt. a-mi actualiza in grid modificarile facute pe server de alti utilizatori .

In Timer am urmatorul cod, in care incerc sa fac requery si sa ma intorc pe inregistrarea pe care am fost:
ThisForm.LockScreen = .T.
IF !ThisForm.Editez
LOCAL nPos
SELECT (ThisForm.cTabelaAntet)
nPos = IIF(EOF(), -1, RECNO())
REQUERY()
IF nPos>0
IF EOF()
GOTO BOTTOM
ELSE
GOTO (nPos)
ENDI
ENDI
ENDI
ThisForm.LockScreen = .F.

In Gridul Antet am pe AfterRowColChance:
Thisform.LockScreen = .T.
m.IdDoc = IdDoc
SELECT (ThisForm.cTabelaDetalii)
REQUERY()
Thisform.LockScreen = .F.


Ce ma deranjeaza este ca GRIDUL Detalii "clipoceste" intr-una (probabil VFP face refresh automat), chiar daca eu folosesc LockScreen = .T.

Cum pot rezolva problema asta ?
 8/7/2010 8:38:48 PM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Cursorul din Antet se misca de 2-3 ori la fiecare eveniment timer.
Comanda thisform.LockScreen =.f.  din grid.AfterRowColChange anuleaza LockScreen pus in timer.
Memoreaza intr-o proprietate iddoc din antet si executa requery doar daca s-a modificat
Ceva de genul asta:

Undeva in form.init sau in designer:
Thisform.AddProperty('iddoc',null)

In AfterRowColChange din antet:
Private iddoc
iddoc=eval(this.Recordsource+'.iddoc')
if thisform.iddoc!==m.iddoc
   return
endif
local llockscreen
llockscreen=thisform.lockscreen
if !llockscreen
   thisform.lockscreen=.t.
endif
requery(thisform.cTabelaDetalii)
go top in (thisform.cTabelaDetalii)
if !llockscreen
   thisform.lockscreen=.f.
endif


Daniel Buduru
 8/8/2010 12:58:16 AM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Unde memorez ThisForm.IdDoc? In BeforeRowColChange?
Linia : if thisform.iddoc!==m.iddoc, ce inseamna? Egal sau diferit? (IdDoc este de tip Int Autoincrementabil)
 8/8/2010 11:52:02 AM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A) Modified By Daniel Buduru  on 8/8/2010 11:04:38 AM)
Scuze, mi-a scapat.
Se pune dupa verificare, astfel incat sa pastreze ultiam valoare cu care s-a facut requery

if thisform.iddoc!==m.iddoc
   return
endif
Thisform.iddoc=m.iddoc

== e echivalent cu SET EXACT ON - comparatia se face pe intreaga lungime a ambelor siruri.
! inseamna NOT - negatie
!==  ar insemna "nu este exact la fel"

Daca iddoc nu e caracter, se poate pune != sau  #

In alta ordine de idei, conexiunile realizate din VFP cu un driver ODBC se misca mai repede decat cele realizate cu ADO.
Daca este disponibil un driver ODBC pentru serverul respectiv, e de pereferat driverului ADO.

Daniel Buduru
 8/8/2010 1:28:20 PM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A) Modified By Daniel Buduru  on 8/8/2010 12:29:02 PM)
De fapt, codul pe care l-am postat nu va rezolva in totalitate problema - depinde ce obiect are focusul in momentul in care se declanseaza timerul.
Dupa requery in antet, pointerul se pozitioneaza pe prima inregistrare.
Trebuie evitata repopularea gridului detalii inainte de repozitionarea pe inregistrarea pe care era inainte de requery.
Ar fi doua variante:
1. Utilizarea unei proprietati, setata in evenimentul timer, care sa blocheze requery pe detalii pana nu se face pozitionarea.
2. Trecerea comenzilor de requery din metoda gridului intr-o metoda a formului, un bindevent intre Grid.AfterRowColChange si aceasta metoda si UnbindEvent in timer.
Pentru varianta 1:
Se adauga o proprietate in form, sa zicem thisform.lAllowRequeryDetalii.
In timer:

ThisForm.LockScreen = .T.
IF ThisForm.Editez
    Return
Endif
thisform.lAllowRequeryDetalii=.f.
LOCAL nPos
nPos = RECNO(thisform.cTabelaAntet)
REQUERY(thisform.cTabelaAntet)
thisform.lAllowRequery=.t.
TRY
   GO nPos IN (thisform.cTabelaAntet)
CATCH
  GO BOTT IN (thisform.cTabelaAntet)
ENDTRY
ThisForm.LockScreen = .F.

In Grid.AfterRowColChange:

if thisform.iddoc<> m.iddoc OR !thisform.lAllowRequeryDetalii
   return
endif
.....
...



Daniel Buduru
 8/9/2010 4:59:46 PM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Multumesc, mi-a fost de mare ajutor!
 8/9/2010 5:22:06 PM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Ar mai fi cate ceva de zis.
Daca vei face requery pe cursorul antet - pe care inteleg ca il folosesti si in editare, deci are toate campurile - la interval de 1 secunda (sau chiar de cateva secunde), mai devreme sau mai tarziu vei incepe sa ai probleme cu reteaua si cu serverul.
Pentru a reduce atat traficul in retea, cat si incarcarea serverului, trebuie limitat volumul de date transferat. 
Poti, spre exemplu, sa verifici daca au aparut pe server inregistrari noi, si doar atunci sa aduci un nou set de date.
Avand o cheie primara autoincrement, o varianta e sa aduci numai ultima cheie din tabela si sa o compari cu o valoare stocata local.
Pentru asta, poti face o interogare cu SPT de genul asta:

Select TOP 1 iddoc FROM tabela_antet ORDER BY iddoc DESC

Daca iddoc returnat este mai mare decat valoarea stocata local - sau decat cea mai mare valoare din cursorul antet local - atunci faci requery in antet.

O alta varianta ar fi sa nu aduci toata tabela antet, ci un numar mimin de campuri - cum ar fi numar document, client, eventual data si suma, si iddoc  - pe care sa le afisezi intr-o lista
Poti pune comanda SPT in list.RowSource si sa pui list.RowSourceType= 3- SQL Statement. E suficinet sa apelezi metoda list.requery pentru a regenera cursorul. Salvezi inainte list.value, apoi repozitionezi pointerul cu list.value = cListValue. Daca pui in list.BoundColumn numarul coloanei in care ai iddoc, vei avea in list.value iddoc.
Pentru a edita antetul, aduci intr-un cursor doar inregistrarea necesara, la fel cum aduci si detaliile. Poti edita antetul fie intr-un grid, fie in textbox-uri legate in controlsource la cursorul cTabelaAntet.

Daniel Buduru
 8/9/2010 9:02:34 PM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Daniel, nu este suficient sa verific inregistrarile noi de pe server cu [Select TOP 1 iddoc FROM tabela_antet ORDER BY iddoc DESC], pt. ca in gridul ANTET vreau sa-mi afiseze ce user lucreaza pe documentul respectiv, si eventualele modificari pe inregistrarile deja EXISTENTE. Ar fi ca si o MONITORIZARE combinata cu posibilitatea de a adauga si modifica documente.
De ex.: daca ordonez dupa campul UserWorking (capul ia o valoare in functie de utilizatorul care lucreaza documentul: 1 - Admin, 2 - Operator 1, 3 - Operator 2, etc.) o sa vad INTERACTIV cati useri lucreaza si pe ce documente.

Referitor la traficul de RETEA m-ai pus pe ganduri. Tabela adusa de pe server are 28 de campuri. Crezi ca ar ingenuchea serverul? Sa incerc pe Timer sa inchid conexiunea din CURSORAdapter apoi sa o redeschid si sa fac CursorRefresh?
 8/9/2010 11:24:55 PM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A) Modified By Daniel Buduru  on 8/10/2010 12:58:52 AM)
Daca lucrezi corect - CA cu optimistic tablebuffering - nu trebuie sa vezi ce user lucreaza la un moment dat, vei vedea doar modificarile dupa tableupdate.
Daca vrei sa vezi ce s-a modificat si la ce s-a lucrat, mai pune un camp timestamp. Daca lucrezi pe un server sql si folosesti un camp de tip timestamp, el este actualizat automat de catre server la fiecare modificare a inregistrarii. Faci verificarea pe timestamp in loc de id. Desigur, pui si un index pe timestamp.
Solutia cu Select top 1 ... descending utilizeaza numai indexul - fata de select max() - si duce la cea mai mica incarcare pe server.

Conexiunea la server nu conteaza la incarcarea retelei, ci a serverului. Foloseste SQLIDLEDISCONNECT si se va deconecta automat.
In retea conteaza doar volumul de date transferate si frecventa cu care se face asta.
Trebuie sa gasesti solutii pentru a transfera doar strictul necesar. Sugestiile mele erau in acest sens.
Sa transferi o singura inregistrare de la server - ultimul timestamp - o data pe secunda e una, si cu totul altceva e sa transferi intreaga tabela antet.
Daca afisezi un subset de inregistrari - sa zicem inregistrarile din ziua respectiva sau din ultima saptamana sau doar inregistrarile la care se s-a lucrat deja ai un trafic mai redus. La cerere, poti aduce toate inregistrarile din luna, sau din tot anul, sau de cand e baza de date.
 Daca reduci si numarul de campuri, e si mai bine. Utilizatorul poate vedea ce inregistrare l-ar interesa, se pozitioneaza pe ea, si abia atunci ii aduci toate datele referite de acea inregistrare.
Chiar daca aduci 10.000 de inregistrari in cursorul editabil, nu le va parcurge nimeni. Va fi nevoie de filtre care sa reduca numarul de inregistrari care trebuie parcurse. E mai bine sa faci asta de la inceput, decat atunci cand sistemul incepe sa se miste suparator de greu.

Daca vrei neaparat sa stii ce face fiecare user la un anumit moment, atunci mai pui o tabela pe server, in care adaugi o inregistrare cu userul, documentul, data si ora in momentul in care se intra in editare, si o actualizezi cu  data si ora terminarii modificarii. Dar asta e deja politie.
Daca cineva vrea sa vada cum lucreaza angajatii, se pot sintetiza date cum ar fi numarul de documente, numarul totatl de pozitii, numarul de modificari, numarul de pozitii modificate si altele ... Dar, daca este nevoie de asa ceva, vorba lui Hamlet, cave e putred in Danemarca ...



Daniel Buduru
 8/10/2010 3:09:24 AM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Mai am o problema, Daniel!
Am un buton [ADAUG] in care incerc sa adaug o inregistrare in grid:


SELECT (THISFORM.cTabelaAntet)
INSERT INTO (THISFORM.cTabelaAntet) (NumarDoc, DataDoc, Tip, iUserWorking, CreatedBy);
VALUES (mNrDoc, DATE(), c_test.tip, m.UserId, m.UserId)

*WAIT WINDOW RECNO(THISFORM.cTabelaAntet)
GO RECNO(THISFORM.cTabelaAntet)
*BROW LAST

*IF SET("Near") = [OFF]
* GO BOTT IN (THISFORM.cTabelaAntet)
*ENDIF


Problema e ca nu de duce pe ultima inregistrare pe care am adaugat eu si ramane pe inregistrarea curenta. Merge numai daca las cu BROW.
Ti s-a intamplat vreodata? ca pe mine m-a disperat chestia asta. Ce poate fi?
 8/10/2010 6:15:54 AM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Insert pozitioneaza cursorul pe ultima inregistrare adaugata. Nu ar trebui sa mai fie nevoie de alta pozitionare, e suficient un refresh.

Inregistrarile adaugate in buffer au recno() <0 (negativ).
GO RECCOUNT() nu te pozitioneaza pe ultima inregistrare adaugata, intrucat RECCOUNT e intotdeauna pozitiv.
Daca nu ai un index activ, GO BOTTOM ar trebui sa te duca pe inregistrarea adaugata.
Daca ai un index activ, poti face locate dupa o cheie unica pentru a te pozitiona pe inregistrare. Cum ar fi Locate for Numardoc=m.nrdoc.

Nu e recomandabil sa aduci in cursorul pe care faci editarea si alte inregistrari decat cele care trebuie editate. Respectiv, nu ar trebui sa ai in cTabela Antet decat o singura inregistrare, cea cu acelasi iddoc ca si in cTabelaDetalii.
Daca afisezi in grid mai multe inregistrari din tabela antet, utilizatorul poate modifica neintentionat un camp dintr-o alta inregistrare, inregistrare care nu face obiectul editarii.
E inca un motiv pentru a avea cursoare separate pentru navigare si pentru editare. Cursorul pe care se navigheaza trebuie sa fie read-only. Controalele legate la cursoarele editabile - textbox-uri, combo, griduri, etc - trebuie sa fie readonly sau disabled in afara editarii.

Daniel Buduru
 8/10/2010 11:37:19 AM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Toate cele spuse de tine le stiam si eu dar in formul asta cred ca am ceva care imi scapa, care face vreun refresh ceva. Am dezactivat si timerele dar degeaba.
Formul fiind 0-Modeless am posibilitatea sa vad interactiv si [Data Session]. Aici imi arata ca se pozitioneaza pe alta tabela, dar cand dau focusul pe ea si ma intorc in Gridul din form se pozitioneaza pe tabela ANTET.
Ti-am spus, DACA dau un BROW dupa INSERT INTO (ThisForm.cTabelaAntet), cursorul se pozitioneaza corect, altfel NU (ramane pe inregistrarea corecta).
Am incercat si cu Debuggerul cu SET STEP ON sa execut fiecare instructiune (F8 - STEP INTO), si functioneaza corect (chiar si fara BROW).
Nu stiu ce sa mai cred!

Referitor la editarea in grid, cand adaug o inregistrare sau o editez am o proprietate in form [Editez] care ia valoarea .T.
Pe BeforeColChange am urmatorul cod care blocheaza gridul numai pe inregistrarea curenta:

IF ThisForm.Editez
IF This.RowColChange <> 2 && 0-No Change;1-RowChange;2-ColChange;3-RowAndColChange
NODEFAULT
SELECT (ThisForm.cTabelaAntet)
ENDI
ENDI
 8/10/2010 11:52:49 AM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Nu cumva acel NODEFAULT din BeforeRowColChange e de vina?
Comenteaza linia si vezi daca se comporta la fel.

Daniel Buduru
 8/10/2010 12:03:22 PM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Am sa incerc.
 8/10/2010 12:16:44 PM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Am scos tot de pe BeforeRowColChange dar tot nu merge bine. Cateodata ma duce pe ultima inregistrare, altadata ramane pe inregistrarea curenta.
O sa mai sap si daca o sa rezolv te anunt, sa nu mai pierzi timpul cu mine.
 8/10/2010 12:39:32 PM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
S-a rezolvat!

Pe cmdAd.Click()

INSERT INTO (THISFORM.cTabelaAntet) (NumarDoc, DataDoc, Tip, iUserWorking, CreatedBy);
VALUES (mNrDoc, DATE(), c_test.tip, m.UserId, m.UserId)

IF SET("Near") = [OFF]
GO BOTT IN (THISFORM.cTabelaAntet)
ENDIF
ogrdDoc.BeforeRowColChange()
ogrdDoc.REFRESH

WITH THISFORM
.CLOSABLE = .F.
.Is_Adaug = .T.
.Editez = .T.
ENDWITH

In BeforeRowColChange a ramas neschimbat!
 8/10/2010 12:55:07 PM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Da, pentru ca ai mutat form.editez=.t. dupa inserare

Totusi, pune in BeforeRowColChange asa:

IF Thisform.Editez AND BITAND(This.RowColChange,1)=1
    NODEFAULT
ENDIF

Asa cuprinzi si cazul 3, cand se schimba si linia si coloana.

Daniel Buduru
 8/11/2010 2:24:50 PM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Daniel, cum pot sa fac RECORDREFRESH numai pentru inregistrarile care le vad in grid la un moment dat?
Cum aflu care este Recno() pt prima inregistrare AFISATA in GRID si care este ultima inregistrare AFISATA?

Eu am incerca asta, dar nu merge in toate cazurile:

oGrdDoc.DoScroll(3) && Pg Down
nRecMaxim = RECNO([iesiri])
oGrdDoc.DoScroll(2) && Pg Up
nRecMinim = RECNO([iesiri])

loCursor1.RECORDREFRESH(nRecMaxim,nRecMaxim - nRecMinim)
 8/11/2010 3:02:53 PM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A) Modified By Daniel Buduru  on 8/11/2010 2:27:03 PM)
Edit
1. Nu poti face refresh doar pe anumite inregistrari dintr-un cursor. Poti face, cu cursoradapter.

2. Poti folosi Grid.ActiveRow pentru a gasi linia curenta din grid, numarata de sus in jos. Atentie, Grid.ActiveRow returneaza o valoare doar daca gridul are focusul.
Poti afla numarul de linii din grid din inaltimea gridului, din care scazi headerul si eventuala bara de scroll orizonta apoi imparti la inatimea liniei+grosimea liniei.

grid.rows=grid.Height-grid.HeaderHeight-IIF(BITAND(grid.ScrollBars,1)=1,SYSMETRIC(8),0))/grid.RowHeight

Pentru a gasi prima inregistrare din grid faci skip-grid.activerow in recordsource, ultima inregistrare skip linii_in_grid.
Ca sa nu misti pointerul in grid, poti deschide inca o data cursorul cu USE (grid.recordsource) alias crsGridMirror shared again in 0, si faci toate deplasarile in crsGridMirror.
Solutia cu skip acopera si situatia in care cursorule este indexat iar inregistrarile nu sunt in ordinea fizica.

Pentru recordrefresh, te pozitionezi pe crsGridMirror pe prima inregistrare vizibila, apoi scan next - cate linii are gridul - si dai comanda ca.recordrefresh.

Poti construi o metoda de paginare, care sa-ti aduca de pe server doar liniile afisate la un moment dat in grid, apoi sa completeze cursorul cu alte linii - la scroll down - dar nu e ceea ce cauti acum.

E complicatie inutila ceea ce incerci sa faci, lucru de care de vei convinge in scurt timp.
O aplicatie de retea, multiuser, se construieste altfel decat o aplicatie desktop, indiferent daca baza de date e vfp sau server sql, si daca clientul e scris in vfp, net sau java.
Solutiile ti le-am prezentat deja, nu mai insist asupra lor.


Daniel Buduru
 8/11/2010 5:30:06 PM
User is offlineDaniel Buduru
2141 posts
1st




Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
 vicos wrote
.....
Eu am incerca asta, dar nu merge in toate cazurile:

oGrdDoc.DoScroll(3) && Pg Down
nRecMaxim = RECNO([iesiri])
oGrdDoc.DoScroll(2) && Pg Up
nRecMinim = RECNO([iesiri])

loCursor1.RECORDREFRESH(nRecMaxim,nRecMaxim - nRecMinim)


Am raspuns in viteza si n-am fost atent la codul postat de tine.
Sintaxa pentru recordrefresh este

ca.RecordRefresh(nRecords, nRecordOffset)

Unde nRecords e numarul de inregistrari consecutive ce se proceseaza iar nRecordOffset e decalajul primei inregistrari fata de inregistrarea curenta.
In cazul tau, ar fi ceva de genul asta:

nRecordOffset=grid.ActiveRow-1
nRecords=floor(grid.Height-grid.HeaderHeight-IIF(BITAND(grid.ScrollBars,1)=1,SYSMETRIC(8),0))/grid.RowHeight)
loCursor1.RecordRefresh(nRecords, nRecordOffset)

Dupa cum am mai spus, grid.ActiveRow returneaza linia curenta numai cand gridul are focusul.
Poti salva ActiveRow intr-o proprietate in AfterRowColChange, pentru a avea acces la aceasta valoare si in situatia cand gridul nu are focusul.


Daniel Buduru
 8/11/2010 5:42:28 PM
User is offlinevicos
137 posts
5th


Re: Cum pot sa fac REQUERY() pe un CursorAdapter fara sa se mute pointerul pe prima inregistrare?
 (N/A)
Multam de sfaturi!
  Visual FoxPro  Visual FoxPro in general  Cum pot sa fac ...

Search  Forum Home         

 Google Ads Minimize

    

Copyright 2002-2010 Profox   Terms Of Use  Privacy Statement