我刚刚成功实现了该示例 (Create Flexible UIs With Flow Documents And Data Binding),并且遇到了与您相同的问题。
问题是BindableRun 的DataContext 设置不正确。如文章中所述,您需要修复上下文(使用FixupDataContext 辅助方法),为FrameworkContentElement 设置DataContext,并清除您之前“修复”的上下文(使用@987654327 @辅助方法)。 这些语句的执行顺序很关键。重新阅读文章并确保您理解它;我不得不重新阅读它几次并研究代码以真正理解他在说什么。
我已将实现更进一步,并在TableCell 中添加了对数据绑定其他元素的支持。更改包括使用附加属性来识别具有DataContext“固定”的元素,然后扩展辅助方法以除FrameworkContentElements 之外还针对FrameworkElements。
HTH,
编辑:
public static class DataContextHelper
{
#region UseAncestorDataContext
public static readonly DependencyProperty UseAncestorDataContextProperty = DependencyProperty.RegisterAttached("UseAncestorDataContext", typeof(bool), typeof(DataContextHelper),
new FrameworkPropertyMetadata(false, DataContextHelper.OnUseAncestorDataContextChanged));
public static bool GetUseAncestorDataContext(DependencyObject d)
{
return (bool)d.GetValue(UseAncestorDataContextProperty);
}
public static void SetUseAncestorDataContext(DependencyObject d, bool value)
{
d.SetValue(UseAncestorDataContextProperty, value);
}
private static void OnUseAncestorDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
UseAncestorDataContext(d);
}
#endregion
/// <summary>
/// If you use a Bindable flow document element more than once, you may encounter a "Collection was modified"
/// exception. The error occurs when the binding is updated because of a change to an inherited dependency
/// property. The most common scenario is when the inherited DataContext changes. It appears that an inherited
/// properly like DataContext is propagated to its descendants. When the enumeration of descendants gets to
/// a Bindable, the dependency properties of that element change according to the new DataContext, which change
/// the (non-dependency) properties. However, for some reason, changing the flow content invalidates the enumeration
/// and raises an exception.
/// </summary>
public static void UseAncestorDataContext(DependencyObject element)
{
if (element is FrameworkContentElement)
{
FrameworkContentElement contentElement = (FrameworkContentElement)element;
Binding binding = new Binding(FrameworkContentElement.DataContextProperty.Name);
binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(FrameworkElement), 1);
contentElement.SetBinding(FrameworkContentElement.DataContextProperty, binding);
}
else if (element is FrameworkElement)
{
FrameworkElement frameworkElement = (FrameworkElement)element;
Binding binding = new Binding(FrameworkElement.DataContextProperty.Name);
binding.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(FrameworkElement), 1);
frameworkElement.SetBinding(FrameworkContentElement.DataContextProperty, binding);
}
}
public static void ClearDataContextBinding(DependencyObject d)
{
while (RestoreDataContextRecursive(d));
}
private static bool RestoreDataContextRecursive(DependencyObject d)
{
if (d is FrameworkContentElement && GetUseAncestorDataContext(d))
{
Binding binding = BindingOperations.GetBinding(d, FrameworkContentElement.DataContextProperty);
if (binding != null && binding.Path != null && binding.Path.Path == FrameworkContentElement.DataContextProperty.Name
&& binding.RelativeSource != null && binding.RelativeSource.Mode == RelativeSourceMode.FindAncestor && binding.RelativeSource.AncestorType == typeof(FrameworkElement) && binding.RelativeSource.AncestorLevel == 1)
{
BindingOperations.ClearBinding(d, FrameworkContentElement.DataContextProperty);
return true;
}
}
else if (d is FrameworkElement && GetUseAncestorDataContext(d))
{
Binding binding = BindingOperations.GetBinding(d, FrameworkElement.DataContextProperty);
if (binding != null && binding.Path != null && binding.Path.Path == FrameworkElement.DataContextProperty.Name
&& binding.RelativeSource != null && binding.RelativeSource.Mode == RelativeSourceMode.FindAncestor && binding.RelativeSource.AncestorType == typeof(FrameworkElement) && binding.RelativeSource.AncestorLevel == 1)
{
BindingOperations.ClearBinding(d, FrameworkElement.DataContextProperty);
return true;
}
}
// As soon as we have disconnected a binding, return. Don't continue the enumeration, since the collection may have changed
foreach (object child in LogicalTreeHelper.GetChildren(d))
{
if (child is DependencyObject && RestoreDataContextRecursive((DependencyObject)child))
return true;
}
return false;
}
}
用法
<DataTemplate x:Key="PercentCellTemplate">
<documents:ContentFragment>
<TableCell Style="{StaticResource TableCellStyle}" BorderThickness="0,0,1,0">
<Paragraph>
<Rectangle Width="14" Height="14" VerticalAlignment="Center" Margin="0,6,0,0" Fill="{Binding Path=Result, Mode=OneWay, Converter={StaticResource MappingConverterResultEnumToIconResource}}"
documents:DataContextHelper.UseAncestorDataContext="True"/>
<documents:DocumentRun Style="{StaticResource ReportDocument_NormalRunStyle}" Text="{Binding Path=Percent, Mode=OneWay, StringFormat={}{0:0.000}%}"
BaselineAlignment="Center" documents:DataContextHelper.UseAncestorDataContext="True" />
</Paragraph>
</TableCell>
</documents:ContentFragment>
</DataTemplate>