2009-03-19 5 views
9

Sto provando a misurare un oggetto immediatamente dopo aver modificato DataContext, ma il binding per l'oggetto non viene aggiornato abbastanza presto. Qui è il mio codice:Aggiornamento del binding immediatamente quando DataContext cambia

// 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); 
} 

voglio le associazioni per l'oggetto View per aggiornare in modo che posso misurare quanto grande la vista deve essere per visualizzare il ViewModel. Sto riutilizzando la stessa vista per misurare perché sto virtualizzando i dati.

Qualcuno sa come forzare il binding ad aggiornare quando cambia DataContext?

Si noti che l'associazione si aggiorna infine.

La vista contiene un TextBlock che è l'elemento principale che modifica le dimensioni in base a ViewModel. Ho esaminato BindingExpression per TextProperty su questo elemento immediatamente dopo aver modificato DataContext, ma chiamare UpdateTarget() non risolve il problema e BindingExpression.DataItem sembra essere nullo.

MODIFICA: Lo stato di BindingExpression non è associato. Il trucco è capire come collegarlo.

risposta

5

Bene, se dopo aver impostato DataContext, hai fatto un Invoke sul Dispatcher alla priorità DataBind, dovrebbe far sì che tutti fossero aggiornati.

Poiché questo codice viene eseguito all'interno del metodo MeasureOverride, non è possibile eseguire un Invoke sul Dispatcher. Invece, vorrei fare un flag che indicava se la larghezza del righello era stata misurata, e in caso contrario, fare un BeginInvoke sul metodo che calcola quelle larghezze. Quindi, quando vengono calcolate le larghezze, chiamare InvalidateMeasure per forzare un secondo passaggio di layout.

Ciò richiederà un passaggio di layout aggiuntivo ogni volta che cambia una di queste larghezze. Sarà necessario reimpostare il flag su false ogni volta che le caselle di testo devono essere rimisurate.

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

Buona idea. Ci proverò. –

+0

Ho ricevuto la seguente eccezione: Impossibile eseguire questa operazione mentre l'elaborazione del dispatcher è sospesa. Sembra che WPF blocchi il Dispatcher durante il layout. Sto eseguendo questo codice in MeasureOverride() –