滚动后,列表框会错误地显示元素

时间:2014-07-15 11:23:22

标签: c# listbox observablecollection windows-phone-8.1 inotifypropertychanged

在我的Windows Phone 8.1应用程序中,我有一个列表框,当我最初加载我要在列表框中显示的所有内容时,一切都很好。但滚动后,某些元素将显示不正确。这似乎是完全随机的。

前面提到的列表框在xaml中是这样的:

            <ListBox Name="MainPage_List" Grid.Column="0" Background="#EDEDED" >
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Components:MyUserControl />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

如您所见,其datatemplate链接到usercontrol。在这个UserControl中,我有一个DataContextChanged事件。看起来像这样:

private void DataContextChanged(object sender, object e)
    {
        if (mySource == null)
        {
            mySource = DataContext as Message;
        }

        if (mySource != null)
        {
            if (mySource.Source_Type == SourceTypes.Type1)
            {
                MyGrid.Visibility = Visibility.Collapsed;
                MyOtherGrid.Visibility = Visibility.Visible;
            }
            else if (mySource.Source_Type == SourceTypes.Type2)
            {
                MyOtherGrid.Visibility = Visibility.Collapsed;
                MyGrid.Visibility = Visibility.Visible;
            }
        }
    }

我在这里检查多个类型和变量,并根据我设置其他可见的东西或加载其他图像。这很好用。但是,当滚动列表时,有时会显示某些元素的不同之处。即使我确保决定显示哪些元素的代码不再使用。

列表的来源是一个自定义类,它使用一个来自INotifyPropertyChanged的自定义类从ObservableCollection中继承。

有谁知道我在做错了什么?或者为什么会发生这种情况以及如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

这可能是虚拟化的结果。 如果您的列表足够长,则可以重复使用其中的项目。 想象一下,你有一个包含1000个项目的列表,但是列表框只有30个控件实例,当你向上和向下滚动时会重复使用它们。 如果是这种情况,您将看到错误的行为重复X项。

为了解决这个问题,我建议您不要使用DataContextChanged,而应该将属性公开为DependencyProperty。

在您的数据模板中,绑定到此属性,这将为您提供帮助。

public bool ShowMyGrid
{
    get { return (bool)GetValue(ShowMyGridProperty); }
    set { SetValue(ShowMyGridProperty, value); }
}

public static readonly DependencyProperty ShowMyGridProperty =
    DependencyProperty.Register("ShowMyGrid", typeof(bool), typeof(MyUserControl1), new UIPropertyMetadata(false, ShowMyGridCallback));


static void ShowMyGridCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var myControl = d as MyUserControl1;
    bool newVal = (bool)e.NewValue;
    if (newVal)
    {
        myControl.MyGrid.Visibility = Visibility.Collapsed;
        myControl.MyOtherGrid.Visibility = Visibility.Visible;
    }
    else
    {
        myControl.MyGrid.Visibility = Visibility.Visible;
        myControl.MyOtherGrid.Visibility = Visibility.Collapsed;
    }
}

答案 1 :(得分:1)

经过与WereWolfBoy的一些讨论后,我们发现了问题。

它与ListBox的默认ItemsPanel有关,它是VirtualizingStackPanel。它重用ListBox的ItemTemplate中的控件,并在必要时更改其DataContext以显示不同的项。与普通的StackPanel不同,它不会为不同的项目创建单独的控件。

要使其工作,DataContext更改时必须刷新项目。这是实际问题。因为这段代码:

    if (mySource == null)
    {
        mySource = DataContext as Message;
    }

DataContext实际上只加载了一次,后续更改不会影响UI。删除if并在每次更改时获取DataContext都会解决问题。