使用ItemsControl和VirtualizingStackPanel“BindingExpression路径错误”

时间:2011-02-09 20:41:10

标签: silverlight windows-phone-7 itemscontrol virtualizingstackpanel

我在Windows Phone 7上使用Silverlight。

使用VirtualizingStackPanel时,是否正常获取“BindingExpression路径错误”调试消息?我认为这种情况正在发生,因为视觉项目暂时不受数据项集合的限制,因为它们被回收......

我有一个ItemsControl,其ItemsPanel的ItemsPanelTemplate是VirtualizingStackPanel。它绑定到我的ViewModel上的“Notes”ObservableCollection。

<ItemsControl x:Name="ListView" ItemsSource="{Binding Notes}" 
              ItemTemplate="{StaticResource ListDataTemplate}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.Template>
        <ControlTemplate>
            <ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}">
                <ItemsPresenter/>
            </ScrollViewer>
        </ControlTemplate>
    </ItemsControl.Template>
</ItemsControl>

这是在UserControl中,其DataContext设置为MainViewModel ViewModel。

ListDataTemplate包含一个Button,它本身包含一些文本,数据绑定到Notes ObservableCollection中项目的属性:

<DataTemplate x:Key="ListDataTemplate">
        <Button>
        <Grid>
            <TextBlock TextWrapping="Wrap" Text="{Binding Title}">
        </Grid>
    </Button>
</DataTemplate>

当我运行我的程序时,一切正常 - 只有我的Notes集合中的少量项目在任何时候都被绑定。但是,当我向上和向下滚动时,我收到很多调试错误:

System.Windows.Data Error: BindingExpression path error: 'Title' property not found on 'EnWp7.ViewModels.MainViewModel' 'EnWp7.ViewModels.MainViewModel' (HashCode=119211466). BindingExpression: Path='Title' DataItem='EnWp7.ViewModels.MainViewModel' (HashCode=119211466); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
System.Windows.Data Error: BindingExpression path error: 'Title' property not found on 'EnWp7.ViewModels.MainViewModel' 'EnWp7.ViewModels.MainViewModel' (HashCode=119211466). BindingExpression: Path='Title' DataItem='EnWp7.ViewModels.MainViewModel' (HashCode=119211466); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
System.Windows.Data Error: BindingExpression path error: 'Title' property not found on 'EnWp7.ViewModels.MainViewModel' 'EnWp7.ViewModels.MainViewModel' (HashCode=119211466). BindingExpression: Path='Title' DataItem='EnWp7.ViewModels.MainViewModel' (HashCode=119211466); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..
System.Windows.Data Error: BindingExpression path error: 'Title' property not found on 'EnWp7.ViewModels.MainViewModel' 'EnWp7.ViewModels.MainViewModel' (HashCode=119211466). BindingExpression: Path='Title' DataItem='EnWp7.ViewModels.MainViewModel' (HashCode=119211466); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..

它正在寻找主视图模型上的“Title”属性(其Notes属性是ItemsSource) - 我认为这是正常的 - 这是一个瞬态情况,因为视觉项目是未绑定的来自Notes集合中的项目,以便通过将它们分配给集合中的另一个项目来进行回收。

有没有人见过这个?我是对的,这是正常的吗?

如果我在MainViewModel上创建一个虚拟Title属性并设置一个调试断点,那么我会看到这个堆栈跟踪。请注意“UnlinkContainerFromItem”:

...!EnWp7.ViewModels.MainViewModel.Title.get() Line 54  C#
mscorlib.dll!System.Reflection.RuntimeMethodInfo.InternalInvoke(System.Reflection.RuntimeMethodInfo rtmi = {System.Reflection.RuntimeMethodInfo}, object obj = {EnWp7.ViewModels.MainViewModel}, System.Reflection.BindingFlags invokeAttr = Default, System.Reflection.Binder binder = null, object parameters = null, System.Globalization.CultureInfo culture = null, bool isBinderDefault = false, System.Reflection.Assembly caller = null, bool verifyAccess = true, ref System.Threading.StackCrawlMark stackMark = LookForMyCaller) 
mscorlib.dll!System.Reflection.RuntimeMethodInfo.InternalInvoke(object obj = {EnWp7.ViewModels.MainViewModel}, System.Reflection.BindingFlags invokeAttr = Default, System.Reflection.Binder binder = null, object[] parameters = null, System.Globalization.CultureInfo culture = null, ref System.Threading.StackCrawlMark stackMark = LookForMyCaller) + 0x14e bytes 
mscorlib.dll!System.Reflection.RuntimePropertyInfo.InternalGetValue(System.Reflection.PropertyInfo thisProperty = {System.Reflection.RuntimePropertyInfo}, object obj = {EnWp7.ViewModels.MainViewModel}, object[] index = null, ref System.Threading.StackCrawlMark stackMark = LookForMyCaller) + 0x4e bytes  
mscorlib.dll!System.Reflection.RuntimePropertyInfo.GetValue(object obj = {EnWp7.ViewModels.MainViewModel}, object[] index = null) + 0x2 bytes   
System.Windows.dll!System.Windows.CLRPropertyListener.Value.get() + 0x1b bytes  
System.Windows.dll!System.Windows.PropertyAccessPathStep.ConnectToPropertyInSource() + 0x148 bytes  
System.Windows.dll!System.Windows.PropertyAccessPathStep.ConnectToProperty() + 0x16 bytes   
System.Windows.dll!System.Windows.PropertyAccessPathStep.ReConnect(object newSource = {EnWp7.ViewModels.MainViewModel}) + 0x13 bytes    
System.Windows.dll!System.Windows.PropertyPathListener.ReConnect(object source = {EnWp7.ViewModels.MainViewModel}) + 0x10 bytes 
System.Windows.dll!System.Windows.Data.BindingExpression.SourceAquired() + 0x11 bytes   
System.Windows.dll!System.Windows.Data.BindingExpression.DataContextChanged(object o = {System.Windows.Controls.TextBlock}, System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x26 bytes   
System.Windows.dll!System.Windows.FrameworkElement.OnDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x15 bytes   
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x11 bytes   
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes   
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes   
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes   
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes   
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes   
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes   
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes   
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes   
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes   
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes   
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes   
System.Windows.dll!System.Windows.FrameworkElement.OnAncestorDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x18 bytes   
System.Windows.dll!System.Windows.FrameworkElement.NotifyDataContextChanged(System.Windows.DataContextChangedEventArgs e = {System.Windows.DataContextChangedEventArgs}) + 0x59 bytes   
System.Windows.dll!System.Windows.FrameworkElement.OnPropertyChanged(System.Windows.DependencyProperty dp = {System.Windows.CoreDependencyProperty}) + 0x1d bytes   
System.Windows.dll!System.Windows.DependencyObject.RaisePropertyChangeNotifications(System.Windows.DependencyProperty dp = {System.Windows.CoreDependencyProperty}, object newValue = {EnWp7.ViewModels.MainViewModel}, object oldValue = {EnWp7.Store.NoteLocal}) + 0x38 bytes 
System.Windows.dll!System.Windows.DependencyObject.ClearValueInternal(System.Windows.DependencyProperty dp = {System.Windows.CoreDependencyProperty}) + 0x138 bytes 
System.Windows.dll!System.Windows.DependencyObject.ClearValue(System.Windows.DependencyProperty dp = {System.Windows.CoreDependencyProperty}) + 0x7 bytes   
System.Windows.dll!System.Windows.Controls.ItemContainerGenerator.UnlinkContainerFromItem(System.Windows.DependencyObject container = {System.Windows.Controls.ContentPresenter}, object item = {EnWp7.Store.NoteLocal}, bool isRecycling = true) + 0x1f bytes  
System.Windows.dll!System.Windows.Controls.ItemContainerGenerator.Remove(System.Windows.Controls.Primitives.GeneratorPosition position = {System.Windows.Controls.Primitives.GeneratorPosition}, int count = 2, bool isRecycling = true) + 0x14a bytes  
System.Windows.dll!System.Windows.Controls.ItemContainerGenerator.System.Windows.Controls.Primitives.IRecyclingItemContainerGenerator.Recycle(System.Windows.Controls.Primitives.GeneratorPosition position = {System.Windows.Controls.Primitives.GeneratorPosition}, int count = 2) + 0x9 bytes    
System.Windows.dll!System.Windows.Controls.VirtualizingStackPanel.CleanupRange(int startIndex = 0, int count = 2) + 0x20 bytes  
System.Windows.dll!System.Windows.Controls.VirtualizingStackPanel.CleanupContainers(System.Windows.Controls.ItemsControl itemsControl = {System.Windows.Controls.ItemsControl}) + 0x73 bytes    
System.Windows.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(System.Windows.Size constraint = {System.Windows.Size}) + 0xe4 bytes  
System.Windows.dll!System.Windows.FrameworkElement.MeasureOverride(System.IntPtr nativeTarget = 102509504, float inWidth = 480.0, float inHeight = 499.0, out float outWidth = 0.0, out float outHeight = 0.0) + 0x45 bytes 

谢谢,     达米安

2 个答案:

答案 0 :(得分:2)

我认为您对情况的评估是正确的,但这听起来像是VirtualizingStackPanel工作方式的错误。它不会影响应用程序在您需要的位置执行或绑定,但引发异常是一项相对昂贵的操作,因此在取消链接容器时相应地更新相应的DataContext是有意义的。

我认为将其作为潜在错误提出的最佳位置是App Hub forums

答案 1 :(得分:1)

我知道这篇文章很老,但是没有创建虚拟属性的解决方案是确保项目的DataContext只能是你期望的类型。

我在数据模板的根目录下使用以下转换器执行此操作:

public class SpecificTypeConverter : IValueConverter
{
    public string Type { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null || value.GetType().FullName == this.Type)
        {
            return value;
        }

        return DependencyProperty.UnsetValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

添加如下静态资源:

<p:PhoneApplicationPage.Resources>
    <converters:SpecificTypeConverter x:Key="MustBeItem" Type="My.Namespace.MyListItemViewModel" /> 
</p:PhoneApplicationPage.Resources>

在数据模板中,使用转换器:

<DataTemplate>
    <StackPanel DataContext="{Binding Converter={StaticResource MustBeItem}}" >
        <TextBlock Tekst="{Binding Path=Name}" />
    </StackPanel>
</DataTemplate>

现在永远不会检查父级的Name属性。