2009-03-19 11 views
9

Я пытаюсь измерить объект сразу после изменения DataContext, но привязка для объекта не будет обновляться достаточно быстро. Вот мой код:Обновление привязки сразу после изменения DataContext

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

Я хочу привязки для представления объекта для обновления, так что я могу определить, насколько велика View должна быть для отображения ViewModel. Я повторно использую один и тот же вид для измерения, потому что я виртуализую данные.

Кто-нибудь знает, как заставить привязку обновляться при изменении DataContext?

Обратите внимание, что привязка действительно обновляется.

View содержит текстовый блок, который является основным элементом, который изменяет размер на основе ViewModel. Я посмотрел на BindingExpression для TextProperty на этом элементе сразу после изменения DataContext, но вызов UpdateTarget() не устраняет проблему, а BindingExpression.DataItem выглядит как null.

РЕДАКТИРОВАТЬ: Состояние привязки: Непривязанный. Трюк состоит в том, чтобы выяснить, как его прикрепить.

ответ

5

Ну, если после установки DataContext вы вызывали в диспетчере приоритет DataBind, это должно привести к тому, что все они будут обновлены.

Поскольку этот код выполняется внутри метода MeasureOverride, вы не можете делать Invoke на Диспетчере. Вместо этого я бы установил флаг, указывающий, была ли измерена ширина линейки, а если нет, выполните метод BeginInvoke в методе, который вычисляет эти ширины. Затем, когда вычисляются значения ширины, вызовите InvalidateMeasure, чтобы принудительно выполнить второй переход макета.

Это потребует дополнительного макета при каждом изменении одной из этих ширины. Вам нужно будет сбросить флаг до false, когда текстовые поля необходимо переоценить.

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

Хорошая идея. Я попробую. –

+0

Я получил следующее исключение: Не удается выполнить эту операцию, пока обработка диспетчера приостановлена. Похоже, WPF блокирует Диспетчер во время компоновки. Я запускаю этот код в MeasureOverride() –