2009-03-19 3 views
9

Ich versuche, ein Objekt sofort nach dem Ändern des DataContext zu messen, aber die Bindung für das Objekt wird nicht bald genug aktualisiert. Hier ist mein Code:Aktualisieren Bindung sofort, wenn DataContext ändert

// In MeasureOverride(Size) 
m_inputWidth = 0.0; 

Size elemSize = new Size(double.PositiveInfinity, RowHeight); 
MapElementView ruler = new MapElementView(); 

// Measure inputs 
foreach (MapElementViewModel elem in m_vm.InputElements) 
{ 
    ruler.DataContext = elem; 
    ruler.Measure(elemSize); 
    m_inputWidth = Math.Max(m_inputWidth, ruler.DesiredSize.Width); 
} 

Ich mag die Bindungen für das View-Objekt zu aktualisieren, damit ich messen kann, wie groß die Ansicht muß, um das Ansichtsmodell anzuzeigen. Ich verwende dieselbe Ansicht erneut, um zu messen, weil ich die Daten virtualisiere.

Kann jemand die Bindung erzwingen, um zu aktualisieren, wenn sich der DataContext ändert?

Beachten Sie, dass die Bindung schließlich aktualisiert wird.

Die Ansicht enthält einen TextBlock, bei dem es sich um das Hauptelement handelt, das die Größe basierend auf dem ViewModel ändert. Ich habe die BindingExpression für die TextProperty für dieses Element unmittelbar nach dem Ändern des DataContext untersucht, aber das Aufrufen von UpdateTarget() behebt das Problem nicht, und BindingExpression.DataItem scheint null zu sein.

EDIT: Der Status der BindingExpression ist nicht verknüpft. Der Trick besteht darin, herauszufinden, wie man es anhängt.

Antwort

5

Nun, wenn Sie nach dem Setzen des DataContext eine Invoke auf dem Dispatcher mit der DataBind-Priorität durchgeführt haben, sollte dies dazu führen, dass alle aktualisiert werden.

Da dieser Code innerhalb der MeasureOverride-Methode ausgeführt wird, können Sie keine Invoke für den Dispatcher ausführen. Stattdessen würde ich ein Flag erstellen, das angibt, ob die Linealbreite gemessen wurde, und falls nicht, ein BeginInvoke für die Methode, mit der diese Breiten berechnet werden. Wenn die Breiten berechnet sind, rufen Sie InvalidateMeasure auf, um einen zweiten Layoutdurchlauf zu erzwingen.

Dies erfordert jedes Mal, wenn sich eine dieser Breiten ändert, einen zusätzlichen Layout-Durchlauf. Sie müssen das Flag auf "false" zurücksetzen, wenn die Textfelder erneut gemessen werden müssen.

private bool isRulerWidthValid = false; 

protected override Size MeasureOverride(Size available) 
{ 
    ... // other code for measuring 
    if (!isRulerWidthValid) 
    { 
     Dispatcher.BeginInvoke(new Action(CalculateRulerSize)); 
     ... // return some temporary value here 
    } 

    ... // do your normal measure logic 
} 

private void CalculateRulerSize(Size available) 
{ 
    Size elemSize = new Size(double.PositiveInfinity, RowHeight); 
    m_inputWidth = 0.0; 

    foreach (MapElementViewModel elem in m_vm.InputElements) 
    { 
     ruler.DataContext = elem; 
     ruler.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.DataBind); 
     ruler.Measure(elemSize); 
     m_inputWidth = Math.Max(m_inputWidth, ruler.DesiredSize.Width); 
    } 

    // invalidate measure again, as we now have a value for m_inputwidth 
    isRulerWidthValid = true; 
    InvalidateMeasure(); 
} 
+0

Gute Idee. Ich werde es versuchen. –

+0

Ich habe die folgende Ausnahme: Kann diesen Vorgang nicht ausführen, während Dispatcher-Verarbeitung unterbrochen ist. Es scheint, dass WPF den Dispatcher während des Layouts sperrt. Ich führe diesen Code in MeasureOverride() –