2010-04-05 14 views
7

Mam złożoną kontrolę WPF, która pobiera wiele prymitywów w OnRender (jest to trochę jak mapa). Kiedy zmieni się niewielka część, chciałbym ponownie wydać polecenia renderowania dla elementów, których dotyczy problem, zamiast uruchamiać cały OnRender. Chociaż nie mam żadnych problemów z działaniem funkcji OnRender przy zmianie rozmiaru lub innym podobnym, nie jest ona wystarczająco szybka dla podświetlania prymitywów za pomocą myszy.Możliwe do InvalidateVisual() na danym regionie zamiast całego formantu WPF?

Obecnie jedynym sposobem, w jaki wiem, jak wymusić aktualizację ekranu, jest wywołanie metody InvalidateVisual(). Nie można wysłać nieprawidłowego regionu, aby go unieważnić.

Czy najmniejsza ziarnistość kompozycji ekranu WPF to element interfejsu użytkownika? Czy muszę wykonać renderowanie elementów pierwotnych w celu pośrednim, a następnie użyć tego polecenia InvalidateVisual() w celu aktualizacji na ekranie?

+0

Czy nadpisać UIElement.OnRender wyciągnąć wszystko wewnątrz UIElement, czy też element nadal mają normalne dzieci wizualnych? –

Odpowiedz

0

WPF nie działa tak, więc nie można unieważnić regionów. Istnieją jednak pewne optymalizacje, które można wprowadzić. Istnieje przepustka Mierz, organizuj, a następnie Renderuj. Jeśli kontrolka się poruszy, ale to, co faktycznie renderuje, nie zmienia się, możesz powiedzieć WPF, aby wykonała tylko przepustkę aranżacyjną. Te unieważnienia można wyzwalać poza zmianami wartości właściwości zależności za pomocą obiektów FrameworkPropertyMetadata i FrameworkPropertyMetadataOptions (http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx).

3

Jeśli chcesz pisać niestandardowe/złożone elementy sterujące WPF, powinieneś starać się unikać nadpisywania OnRendera, jeśli to możliwe, szczególnie jeśli planujesz unieważnić jego fragmenty. Jest to znacznie łatwiejsze w użyciu AddVisualChild + zastąpić VisualChildrenCount + zastąpić GetVisualChild + override Measure & Umów tak (pseudo kod z 2 dzieci):

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 
} 

Z tego rodzaju kodu, można unieważnić tylko jedną część, z czymś subControlX.InvalidateXXX();

+0

'MeasureCore' i' ArrangeCore' nie są zastępowalne. Może zamiast tego chciałeś powiedzieć "MeasureOverride" i "ArrangeOverride"? – dotNET

+0

@dotnet - To zależy, czy czerpiesz z UIElement lub FrameworkElement –

0

Nie powinieneś używać InvalidateVisual(), chyba że zmieni się rozmiar kontrolki, ponieważ powoduje to dość kosztowne zmiany układu interfejsu użytkownika.

WPF to zachowany system rysowania. Oznacza to, że OnRender() może być lepiej nazwany AccumulateDrawingObjects(). W rzeczywistości gromadzi drzewo żywych obiektów rysunkowych, które musi się zdarzyć tylko raz na układ. Następnie używa tych obiektów do rysowania interfejsu użytkownika, kiedy tylko zajdzie taka potrzeba. Aby zmienić wygląd części interfejsu użytkownika bez zmiany układu, niektóre obiekty (takie jak DrawingGroup, RenderTargetBitmap i WriteableBitmap) mogą być aktualizowane po OnRender(), w dowolnym momencie.

Aby zaktualizować część swojego UI później owinąć te polecenia w DrawingGroup i umieścić że obiekt w DrawingContext. Następnie możesz Open() zaktualizować go w dowolnym momencie, a WPF automatycznie odświeży tę część interfejsu.

To jak to wygląda:

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();    
} 
Powiązane problemy