2010-04-05 7 views
7

Ho un controllo WPF complesso che disegna un sacco di primitive nel suo OnRender (è un po 'come una mappa). Quando una piccola parte di essa cambia, mi piacerebbe solo ri-emettere i comandi di rendering per gli elementi interessati, invece di eseguire l'intero OnRender. Mentre sto bene con le prestazioni della mia funzione OnRender su un ridimensionamento o qualsiasi altra cosa, non è abbastanza veloce per l'evidenziazione dei primitivi basata sul mouse hover.È possibile InvalidateVisual() su una determinata regione anziché su un controllo WPF intero?

Attualmente l'unico modo per sapere come forzare l'aggiornamento di uno schermo è chiamare InvalidateVisual(). Nessun modo per inviare una regione rect sporca per invalidare.

La più bassa granularità della composizione dello schermo WPF è l'elemento dell'interfaccia utente? Avrò bisogno di fare i miei rendering di primitivi in ​​un target intermedio e poi usare InvalidateVisual() per aggiornare allo schermo?

+0

Ti ignorare UIElement.OnRender disegnare tutto all'interno della UIElement, o se l'elemento ancora bambini normali visivi? –

risposta

0

WPF non funziona così, quindi non è possibile invalidare le regioni. Tuttavia, ci sono alcune ottimizzazioni che possono essere fatte. C'è un passaggio Misura, Disponi, e quindi Render. Se un controllo si sposta ma ciò che effettivamente rende non cambia, puoi dire a WPF di eseguire solo il passaggio di organizzazione. È possibile attivare queste invalidazioni al di fuori delle modifiche del valore della proprietà di dipendenza con FrameworkPropertyMetadata e FrameworkPropertyMetadataOptions (http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx).

3

Quando si desidera scrivere controlli WPF personalizzati/compositi, si dovrebbe cercare di evitare di sovrascrivere OnRender il più possibile, specialmente se si pianifica di invalidarne alcune parti. E 'molto più facile da usare AddVisualChild + ignorare VisualChildrenCount + ignorare GetVisualChild + Override Misura & Disporre come questa (pseudo codice con 2 bambini):

private void BuildMyControls() 
{ 
    AddVisualChild(subControl1); 
    AddVisualChild(subControl2); 
} 

protected override int VisualChildrenCount 
{ 
    get 
    { 
    return 2; 
    } 
} 

protected override Visual GetVisualChild(int index) 
{ 
    if (index == 0) return subControl1; 
    if (index == 1) return subControl2; 
    return null; // should never be called in fact... 
} 

protected override Size MeasureCore(Size availableSize) 
{ 
    base.Measure... 
    BuildMyControls(); 
    .. measure them, probably call subControlX.Measure(...); 
} 

protected override void ArrangeCore(Rect finalRect) 
{ 
    base.ArrangeCore(finalRect); 
    ... arrange them, probably call subControlX.Arrange 
} 

Con questo tipo di codice, è possibile invalidare solo una porzione, con qualcosa di simile subControlX.InvalidateXXX();

+0

'MeasureCore' e' ArrangeCore' non sono sovrascrivibili. Forse volevi dire "MeasureOverride" e "ArrangeOverride" invece? – dotNET

+0

@dotnet - Dipende se si deriva da UIElement o FrameworkElement –

0

Non utilizzare InvalidateVisual() a meno che la dimensione del controllo non cambi, in quanto provoca una rielaborazione abbastanza costosa dell'interfaccia utente.

WPF è un mantenuto sistema di disegno. Ciò significa che OnRender() potrebbe essere meglio chiamato AccumulateDrawingObjects(). In realtà sta accumulando un albero di oggetti di disegno dal vivo, che deve solo succedere una volta per ogni layout. Quindi utilizza questi oggetti per disegnare l'interfaccia utente ogni volta che è necessario. Per cambiare il modo in cui una parte dell'interfaccia utente appare senza ri-layout, alcuni oggetti (come DrawingGroup, RenderTargetBitmap e WriteableBitmap) possono essere aggiornati dopo lo OnRender(), in qualsiasi momento.

per aggiornare una parte della vostra interfaccia utente più tardi, avvolgere i comandi in un DrawingGroup e mettere che oggetto nel DrawingContext. Quindi puoi Open() e aggiornarlo quando vuoi, e WPF ridisegnerà automaticamente quella parte dell'interfaccia utente.

Questo è ciò che sembra:

DrawingGroup backingStore = new DrawingGroup(); 

protected override void OnRender(DrawingContext drawingContext) {  
    base.OnRender(drawingContext);    

    Render(); // put content into our backingStore 
    drawingContext.DrawDrawing(backingStore); 
} 

// I can call this anytime, and it'll update my visual drawing 
// without ever triggering layout or OnRender() 
private void Render() {    
    var drawingContext = backingStore.Open(); 
    Render(drawingContext); 
    drawingContext.Close();    
} 
Problemi correlati