«Ошибка пути BindingExpression» с использованием ItemsControl и VirtualizingStackPanel - PullRequest
2 голосов
/ 09 февраля 2011

Я использую Silverlight на Windows Phone 7.

При использовании VirtualizingStackPanel нормально загружать отладочные сообщения "Ошибка пути BindingExpression"?Я думаю, что это происходит потому, что визуальные элементы временно не связаны с коллекцией элементов данных, поскольку они перерабатываются ...

У меня есть ItemsControl, у которого ItemsPanelTemplate ItemsPanel является VirtualizingStackPanel.Он привязывается к ObservableCollection «Notes» в моей ViewModel.

<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 содержит кнопку, которая сама содержитнекоторый текст, привязанный к свойству элементов в 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')..

Требуется свойство «Заголовок» в модели основного вида (свойство Notes - это ItemsSource) - I подумайте, это нормально - это временная ситуация, поскольку визуальные элементы не связаны с элементами в коллекции Notes, чтобы их можно было переработать, назначив их другому элементу в коллекции.

Кто-нибудь еще виделэтот?Прав ли я, что это нормально?

Если я создаю фиктивное свойство Title в моем MainViewModel и устанавливаю точку останова отладки, я вижу эту трассировку стека.Обратите внимание на «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 ]

2 голосов
/ 10 февраля 2011

Я думаю, что ваша оценка ситуации верна, но звучит как ошибка в том, как работает VirtualizingStackPanel. Это не влияет на выполнение или привязки вашего приложения в тех точках, где оно вам необходимо, но создание исключений является относительно дорогой операцией, и поэтому имеет смысл соответственно обновить соответствующий DataContext при отсоединении контейнеров.

Я думаю, что лучшее место, чтобы поднять его как потенциальную ошибку, было бы на форумах App Hub

1 голос
/ 28 мая 2013

Я знаю, что этот пост довольно старый, но решение без создания фиктивных свойств заключается в том, чтобы 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 родительского элемента никогда не будет проверено.

...