Vra

Ek het 'n eenvoudige UserControl vir databasisblaai, wat 'n kontroleerder gebruik om die werklike DAL-oproepe uit te voer.Ek gebruik a BackgroundWorker om die swaar opheffing uit te voer, en op die OnWorkCompleted gebeurtenis Ek heraktiveer sommige knoppies, verander a TextBox.Text eiendom en verhoog 'n geleentheid vir die ouervorm.

Vorm A hou my UserControl.Wanneer ek op een of ander knoppie klik wat vorm B oopmaak, selfs al doen ek niks "daar" en maak dit net toe, en probeer om die volgende bladsy van my databasis in te bring, die OnWorkCompleted word op die werkersdraad geroep (en nie my Hoofdraad nie), en gooi 'n kruisdraad-uitsondering.

Op die oomblik het ek 'n tjek bygevoeg vir InvokeRequired by die hanteerder daar, maar is nie die hele punt van OnWorkCompleted moet op die Hoofdraad genoem word?Hoekom sal dit nie werk soos verwag nie?

EDIT:

Ek het daarin geslaag om die probleem te beperk tot arcgis en BackgroundWorker.Ek het die volgende oplossing wat 'n opdrag by arcmap voeg, wat 'n eenvoudige oopmaak Form1 met twee knoppies.

Die eerste knoppie loop a BackgroundWorker wat vir 500 ms slaap en 'n teller opdateer.In die RunWorkerCompleted metode waarna dit nagaan InvokeRequired, en werk die titel op om te wys of die metode oorspronklik binne die hoofdraad of die werkdraad geloop het.Die tweede knoppie maak net oop Form2, wat niks bevat nie.

Aanvanklik het al die oproepe na RunWorkerCompletedare word binne die hoofdraad gemaak (soos verwag - dit is die hele punt van die RunWorkerComplete-metode, ten minste volgens wat ek verstaan ​​uit die MSDN aan BackgroundWorker)

Na oop- en toemaak Form2, die RunWorkerCompleted word altyd op die werkersdraad geroep.Ek wil byvoeg dat ek hierdie oplossing vir die probleem net kan laat (kyk vir InvokeRequired in die RunWorkerCompleted metode), maar ek wil verstaan ​​hoekom dit teen my verwagtinge gebeur.In my "regte" kode wil ek altyd weet dat die RunWorkerCompleted metode word op die hoofdraad genoem.

Ek het daarin geslaag om die probleem vas te stel by die form.Show(); bevel in my BackgroundTesterBtn - as ek gebruik ShowDialog() in plaas daarvan kry ek geen probleem nie (RunWorkerCompleted loop altyd op die hoofdraad).Ek moet gebruik Show() in my ArcMap-projek, sodat die gebruiker nie aan die vorm gebind sal wees nie.

Ek het ook probeer om die fout op 'n normale WinForms-projek te reproduseer.Ek het 'n eenvoudige projek bygevoeg wat net die eerste vorm sonder ArcMap oopmaak, maar in daardie geval kon ek nie die fout reproduseer nie - die RunWorkerCompleted het op die hoofdraad gehardloop, of ek gebruik het Show() of ShowDialog(), voor en na opening Form2.Ek het probeer om 'n derde vorm by te voeg om as 'n hoofvorm voor my op te tree Form1, maar dit het nie die uitslag verander nie.

Hier is my eenvoudige sln (VS2005sp1) - dit vereis

ESRI.ArcGIS.ADF(9.2.4.1420)

ESRI.ArcGIS.ArcMapUI(9.2.3.1380)

ESRI.ArcGIS.SystemUI (9.2.3.1380)

Was dit nuttig?

Oplossing

Dit lyk soos 'n fout:

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=116930

http://thedatafarm.com/devlifeblog/archive/2005/12/21/39532.aspx

So ek stel voor om die koeëlvaste (pseudokode) te gebruik:

if(control.InvokeRequired)
  control.Invoke(Action);
else
  Action()

Ander wenke

Is nie die hele punt van OnWorkCompleted moet op die Hoofdraad genoem word?Hoekom sal dit nie werk soos verwag nie?

Nee dit is nie.
Jy kan nie sommer enige ou ding op enige ou draad gaan hardloop nie.Drade is nie beleefde voorwerpe wat jy bloot kan sê "hardloop dit asseblief".

'n Beter geestelike model van 'n draad is 'n goederetrein.Sodra dit aan die gang is, is dit op sy eie spoor.Jy kan nie sy koers verander of dit stop nie.As jy dit wil beïnvloed, moet jy óf wag totdat dit by die volgende treinstasie kom (bv.laat dit handmatig kyk vir sommige gebeurtenisse), of laat dit ontspoor (Thread.Abort en CrossThread-uitsonderings het baie dieselfde gevolge as om 'n trein te ontspoor ...pasop!).

Winforms kontroles soortvan ondersteun hierdie gedrag (Hulle het Control.BeginInvoke wat jou toelaat om enige funksie op die UI-draad te laat loop), maar dit werk net omdat hulle 'n spesiale haak in die Windows UI-boodskappomp het en 'n paar spesiale hanteerders skryf.Om met bogenoemde analogie te gaan, hul trein meld by die stasie in en soek periodiek na nuwe aanwysings, en jy kan daardie fasiliteit gebruik om dit jou eie aanwysings te plaas.

Die BackgroundWorker is ontwerp om algemene doel te wees (dit kan nie aan die Windows GUI gekoppel word nie) sodat dit nie die vensters kan gebruik nie Control.BeginInvoke kenmerke.Dit moet aanvaar dat jou hoofdraad 'n onstuitbare 'trein' is wat sy eie ding doen, so die voltooide gebeurtenis moet in die werkersdraad loop of glad nie.

Soos jy egter winforms gebruik, in jou OnWorkCompleted hanteerder, kan jy die venster kry om uit te voer 'n ander terugbel met behulp van die BeginInvoke funksionaliteit wat ek hierbo genoem het.Soos hierdie:

// Assume we're running in a windows forms button click so we have access to the 
// form object in the "this" variable.
void OnButton_Click(object sender, EventArgs e )
    var b = new BackgroundWorker();
    b.DoWork += ... blah blah

    // attach an anonymous function to the completed event.
    // when this function fires in the worker thread, it will ask the form (this)
    // to execute the WorkCompleteCallback on the UI thread.
    // when the form has some spare time, it will run your function, and 
    // you can do all the stuff that you want
    b.RunWorkerCompleted += (s, e) { this.BeginInvoke(WorkCompleteCallback); }
    b.RunWorkerAsync(); // GO!
}

void WorkCompleteCallback()
{
    Button.Enabled = false;
    //other stuff that only works in the UI thread
}

Moet ook nie hierdie vergeet nie:

Jou RunWorkerCompleted-gebeurtenishanteerder moet altyd die Fout- en Gekanselleer-eienskappe nagaan voordat jy toegang tot die Resultaat-eienskap kry.As 'n uitsondering geopper is of as die operasie gekanselleer is, veroorsaak toegang tot die Resultaat-eiendom 'n uitsondering.

Die BackgroundWorker kontroleer of die afgevaardigde instansie na 'n klas wys wat die koppelvlak ondersteun ISynchronizeInvoke.Jou DAL-laag implementeer waarskynlik nie daardie koppelvlak nie.Normaalweg sal jy die BackgroundWorker op 'n Form, wat wel daardie koppelvlak ondersteun.

In die geval dat jy die BackgroundWorker vanaf die DAL-laag en van daar af die UI wil opdateer, het jy drie opsies:

  • jy sal die bly bel Invoke metode
  • die koppelvlak te implementeer ISynchronizeInvoke op die DAL-klas, en herlei die oproepe met die hand (dit is net drie metodes en 'n eienskap)
  • voordat u die aanroep BackgroundWorker (dus, op die UI-draad), om te bel SynchronizationContext.Current en om die inhoudsinstansie in 'n instansieveranderlike te stoor.Die SynchronizationContext sal dan vir jou die Send metode, wat presies sal doen wat Invoke doen.

Die beste benadering om probleme met kruisdraad in GUI te vermy, is om gebruik SynchronizationContext.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top