[C#/WPF] (User)Control opbouwen buiten UI thread

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Knakker
  • Registratie: April 2000
  • Laatst online: 26-07 21:05
Ik heb een rapportagetool gemaakt die zijn rapportages uit een WCF service trekt en deze middels een eigen gemaakte user control op het scherm toont. De rapportbasis wordt asynchroon via de service binnengetrokken en alle benodigde berekeningen worden daarna in separate threads uitgevoerd, om pas getoond te worden wanneer de rapportinhoud 'echt' klaar is.

Mijn user control bestaat uit een standaard WPF Grid dat ik zelf dynamisch vul met allerlei andere controls en kleuren. Dat werkt uitstekend, alleen bij hele grote rapporten kan dit opbouwen een paar seconden duren. Het gevolg daarvan is dat de UI thread hangt en de gebruiker met een niet-responsieve applicatie te maken heeft.

Is het mogelijk om controls in een aparte thread op te bouwen en pas wanneer deze klaar zijn naar de UI thread over te hevelen? Als ik in een aparte thread mijn user control aanmaak dan krijg ik exceptions voor mijn kiezen.

Hoe zou ik dit aan kunnen pakken? Alvast bedankt :)

Geef mij maar een Warsteiner.


Acties:
  • 0 Henk 'm!

  • Gimmeabrake
  • Registratie: December 2008
  • Laatst online: 23-08 10:45
Het is maar een gok, aangezien je niet veel info geeft over de daadwerkelijke exception die je krijgt, maar ik denk dat die exceptions zijn cross-thread-exceptions zijn, toch? Je moet dan vanuit je thread de method in je ui-thread aanroepen via de invoke method. Hiermee zou je een heel eind moeten komen: http://www.dreamincode.ne...ad-communication-in-c%23/

[ Voor 0% gewijzigd door Gimmeabrake op 15-12-2010 14:59 . Reden: typo ]


Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 19:03

Sebazzz

3dp

Ik heb geen antwoord voor je, maar ik vermoet dat je teveel gegevens probeert te weergeven.
gerrymeistah schreef op woensdag 15 december 2010 @ 14:58:
Je moet dan vanuit je thread de method in je ui-thread aanroepen via de invoke method. Hiermee zou je een heel eind moeten komen: http://www.dreamincode.ne...ad-communication-in-c%23/
En dus bouw je je control op in de UI thread, en dus niet erbuiten ;)

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


Acties:
  • 0 Henk 'm!

  • TheNameless
  • Registratie: September 2001
  • Laatst online: 07-02 21:38

TheNameless

Jazzballet is vet!

Daadwerkelijk een Usercontrol opbouwen op een andere thread dan de UI thread gaat niet lukken.
Dat lukt alleen bij Freezable componenten en die moet je daadwerkelijk Freezen voordat je deze door geeft aan je UI thread.

Ik denk zelf dat je WPF op een verkeerde manier gebruikt. Als je namelijk bezig bent handmatig controls te maken voor elke rij in je grid, dan ben je verkeerd bezig.

Een betere aanpak zal volgens mij zijn niet een Grid te gebruiken maar een ItemsControl.
Als je vervolgens een VirtualizingStackPanel gebruikt als ItemsPanel, dan moet je het wel heel bont maken wil je nog in performance problemen komen.

[ Voor 11% gewijzigd door TheNameless op 15-12-2010 19:03 ]

Ducati: making mechanics out of riders since 1946


Acties:
  • 0 Henk 'm!

  • TheNameless
  • Registratie: September 2001
  • Laatst online: 07-02 21:38

TheNameless

Jazzballet is vet!

gerrymeistah schreef op woensdag 15 december 2010 @ 14:58:
Het is maar een gok, aangezien je niet veel info geeft over de daadwerkelijke exception die je krijgt, maar ik denk dat die exceptions zijn cross-thread-exceptions zijn, toch? Je moet dan vanuit je thread de method in je ui-thread aanroepen via de invoke method. Hiermee zou je een heel eind moeten komen: http://www.dreamincode.ne...ad-communication-in-c%23/
WPF heeft hier al een standaard oplossing voor, namelijk de Dispatcher.
Bijna elk object in WPF stamt af van een DispatcherObject. Dit object heeft een public property genaamd Dispatcher die werk kan uitvoeren op de thread waar het desbetreffende object leeft.
Je krijgt dan bijvoorbeeld zoiets:
C#:
1
2
3
4
5
  Grid mygrid;

  mygrid.Dispatcher.Invoke(new Action(delegate(){
    mygrid.Children.Add(..);
  }));

Ducati: making mechanics out of riders since 1946


  • Knakker
  • Registratie: April 2000
  • Laatst online: 26-07 21:05
Sorry, het gaat inderdaad om cross-thread exceptions. Ik weet dat ik via Dispatcher.Invoke controls die op de UI thread zijn gemaakt, kan wijzigen. Ik moet alleen zoveel controls toevoegen dat het die actie op zich is dat het geheel vertraagd. Ik hoopte in een aparte thread het control te aan te maken en dan aan de ItemsCollection van mijn hoofd control toe te voegen, maar dat pikt ie niet.

Ik kan me uitstekend voorstellen dat ik WPF verkeerd gebruik ;) Dit is de constructie (zwaar gestyleerd) die ik nu toepas:

C#:
1
2
3
4
5
6
7
foreach (ReportItem Item in Report)
{
    TextBlock Block = new TextBlock() { Text = Item.Description }; 
    Grid.SetValue(Grid.RowProperty, Item.Row);
    Grid.SetValue(Grid.ColumnProperty, Item.Column);
    Grid.Children.Add(Block);
}


Op zich ben ik nog niet eens zo heel ontevreden over de snelheid - een seconde of 3 wachten voordat een groot rapport verschijnt (>300 kolommen, >100 rijen dat in de praktijk niet heel vaak gebruikt gaat worden) is niet zo'n ramp, alleen zou ik wel willen dat de UI thread niet blockt.

De reden dat ik een Grid gebruik is omdat ik de gegevens ook daadwerkelijk in een Grid-achtige vorm wil laten zien, deze makkelijk kan opmaken en daar zonder problemen een ScrollViewer omheen kan plaatsen (wat bij een StackPanel alweer niet kan). Maar suggesties zijn natuurlijk welkom :)

[ Voor 6% gewijzigd door Knakker op 16-12-2010 12:07 ]

Geef mij maar een Warsteiner.


  • TheNameless
  • Registratie: September 2001
  • Laatst online: 07-02 21:38

TheNameless

Jazzballet is vet!

Knakker schreef op donderdag 16 december 2010 @ 12:04:
Sorry, het gaat inderdaad om cross-thread exceptions. Ik weet dat ik via Dispatcher.Invoke controls die op de UI thread zijn gemaakt, kan wijzigen. Ik moet alleen zoveel controls toevoegen dat het die actie op zich is dat het geheel vertraagd. Ik hoopte in een aparte thread het control te aan te maken en dan aan de ItemsCollection van mijn hoofd control toe te voegen, maar dat pikt ie niet.

Ik kan me uitstekend voorstellen dat ik WPF verkeerd gebruik ;) Dit is de constructie (zwaar gestyleerd) die ik nu toepas:

C#:
1
2
3
4
5
6
7
foreach (ReportItem Item in Report)
{
    TextBlock Block = new TextBlock() { Text = Item.Description }; 
    Grid.SetValue(Grid.RowProperty, Item.Row);
    Grid.SetValue(Grid.ColumnProperty, Item.Column);
    Grid.Children.Add(Block);
}


Op zich ben ik nog niet eens zo heel ontevreden over de snelheid - een seconde of 3 wachten voordat een groot rapport verschijnt (>300 kolommen, >100 rijen dat in de praktijk niet heel vaak gebruikt gaat worden) is niet zo'n ramp, alleen zou ik wel willen dat de UI thread niet blockt.

De reden dat ik een Grid gebruik is omdat ik de gegevens ook daadwerkelijk in een Grid-achtige vorm wil laten zien, deze makkelijk kan opmaken en daar zonder problemen een ScrollViewer omheen kan plaatsen (wat bij een StackPanel alweer niet kan). Maar suggesties zijn natuurlijk welkom :)
Het probleem met een Grid is dat elke keer als je er een Child aan toevoegd, hij voor elk kind de Measure() en Arrange() moet aanroepen om de breedte en hoogte te bepalen.
Als dit vaak voorkomt dan wordt het gewoon traag.

Als je verder geen moeite hebt met dat het traag werkt, zou je zoiets kunnen proberen (quick-and-dirty):
C#:
1
2
3
4
5
6
7
8
9
foreach (ReportItem Item in Report)
{
    Dispatcher.BeginInvoke(new Action(delegate() {
        TextBlock Block = new TextBlock() { Text = Item.Description }; 
        Block.SetValue(Grid.RowProperty, Item.Row);
        Block.SetValue(Grid.ColumnProperty, Item.Column);
        Grid.Children.Add(Block);
    }, DispatcherPrioirty.Background);
}


De dispatcher is namelijk niet veel meer als een queue van opdrachten die gedaan moeten worden op een thread (in dit geval de UI thread).
BeginInvoke() is asynchroon, dus returned meteen. Met de DispatcherPriority kun je enigszins aangeven in welke volgorde opdrachten moeten worden uitgevoerd.

Beter oplossing blijft om voor een GridView of iets dergelijks te kiezen (zie bijvoorbeeld http://www.c-sharpcorner....182813PM/GridViewWpf.aspx).

Ducati: making mechanics out of riders since 1946


  • Knakker
  • Registratie: April 2000
  • Laatst online: 26-07 21:05
Het probleem met een Grid is dat elke keer als je er een Child aan toevoegd, hij voor elk kind de Measure() en Arrange() moet aanroepen om de breedte en hoogte te bepalen.
Als dit vaak voorkomt dan wordt het gewoon traag.
Kan dit omzeild worden? Dat de Measure() en Arrange() pas aan het einde aangeroepen worden? In het good old Windows Forms had je nog zoiets als SuspendLayout en ResumeLayout (alhoewel dat ook nooit precies werkte zoals ik dat wou).
Beter oplossing blijft om voor een GridView of iets dergelijks te kiezen (zie bijvoorbeeld http://www.c-sharpcorner....182813PM/GridViewWpf.aspx).
De reden dat ik hoofdzakelijk voor een eigen control gekozen heb is omdat ik met de 'standaard' gridviews niet precies krijg wat ik wil - bijvoorbeeld genest groeperen (zoals een treeview) van zowel rijen als kolommen.

In hoeverre verschilt een WPF gridview eigenlijk van een (layout) grid gevuld met editing controls eigenlijk?

Ontzettend bedankt tot zover in ieder geval :)

Geef mij maar een Warsteiner.


  • Knakker
  • Registratie: April 2000
  • Laatst online: 26-07 21:05
Wat zoekwerk levert op dat

C#:
1
2
3
var d = Dispatcher.DisableProcessing();
...
d.Dispose();


voldoende is om de UI thread te stoppen met verwerken.

Alle overige suggesties over het bouwen van een custom control om veel data te laten zien is natuurlijk nog steeds welkom :)

Geef mij maar een Warsteiner.

Pagina: 1