C# UWP:使用DependencyProperty虚拟化StackPanel。处理前一个绑定之前的新绑定会导致问题

C# UWP:使用DependencyProperty虚拟化StackPanel。处理前一个绑定之前的新绑定会导致问题,c#,uwp,C#,Uwp,我有一个绑定到唱片集实例的ItemsSource的ListView。因为可以有许多(>2000)相册实例,所以我使用(水平滚动)VirtualzingStackPanel作为ItemPanel: <ListView.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation="Horizontal" /> </ItemsPanelTemplate

我有一个绑定到唱片集实例的ItemsSource的ListView。因为可以有许多(>2000)相册实例,所以我使用(水平滚动)VirtualzingStackPanel作为ItemPanel:

<ListView.ItemsPanel>
    <ItemsPanelTemplate>
        <VirtualizingStackPanel Orientation="Horizontal" />
    </ItemsPanelTemplate>
</ListView.ItemsPanel>

作为ItemTemplate,我使用一个自定义UserControl,它绑定到ItemsSource中的相册:

<DataTemplate x:Name="albumItem" x:DataType="local2:Album">
    <local3:AlbumControl x:Name="albumControl" Album="{x:Bind}" />
</DataTemplate>

AlbumControl上的DependencyProperty相册在发生新绑定时执行一些操作:

/// <summary>
/// Gets or sets the Album assigned to the control.
/// </summary>
public Album Album
{
    get { return (Album)GetValue(AlbumProperty); }
    set { SetValue(AlbumProperty, value); }
}

/// <summary>
/// Identifies the Album dependency property.
/// </summary>
public static readonly DependencyProperty AlbumProperty = DependencyProperty.Register(nameof(Album), typeof(Album), typeof(AlbumControl), new PropertyMetadata(null, HandleAlbumChange));

private static async void HandleAlbumChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d is AlbumControl control)
    {
        if (e.NewValue is Album album)
        {
            control.DoStuff(album);
            await control.DoStuffAsync(album);
            control.DoMoreStuff(album);
        }
    }
}
//
///获取或设置分配给控件的相册。
/// 
公共相册
{
获取{return(Album)GetValue(AlbumProperty);}
set{SetValue(AlbumProperty,value);}
}
/// 
///标识相册依赖项属性。
/// 
public static readonly dependencProperty AlbumProperty=dependencProperty.Register(相册名称、相册类型、相册控制类型、新属性元数据(null、HandleAlbumChange));
私有静态异步void HandleAlbumChange(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
如果(d是控制)
{
如果(例如,NewValue是相册)
{
对照组.DoStuff(相册);
等待控制。DoStuffAsync(相册);
控件.DoMoreStuff(相册);
}
}
}
因为ItemsPanel是虚拟化的,所以在滚动期间,有可能在DoStuff()之后和DoStuffAsync()完成之前将新相册绑定到AlbumControl。这会引起问题

我想我需要:

  • 某种类型的锁定(DoStuff()包含一个锁,但不能锁定异步调用),或者
  • 以某种方式取消处理旧相册而只处理新相册,或者
  • 也许在等待的电话中出错了,或者
  • 也许我的设置完全错了
主要问题是:如何处理比处理绑定更快发生的绑定事件

========================================

作为对评论的回应,我将补充一些我观察到的细节

DoStuff(album)清除了AlbumControl的ObservableCollection属性。AlbumControl在ListView中显示此集合

wait control.DoStuffAsync(相册)用于设置相册控件中图像控件的源。相册中此源的信息为字节[]。我需要将其转换为RandomAccessStream,以便能够调用
wait bmp.SetSourceAsync(stream)
,然后我可以使用bmp(位图图像)作为源。字节[]没有磁盘上的文件支持,因此我无法使用Uri设置映像控件的源

在这个等待的调用之后,我重新填充了ObservableCollection(control.domorteuff())。如果我这样做,那么最后一个AlbumControl的集合包含来自多个相册的listitems。表示在等待的通话中设置了新唱片集。当线程在第一个相册的异步结束后返回时,它将重新填充集合,然后第二个相册的线程将返回,并重新填充集合。导致两个相册的列表项都在集合中。我通过在异步方法调用之后,即重新填充步骤之前移动Clear()来“解决”这个问题。但我仍然怀疑我是否做了一些可疑的事情

我找不到预计算BitmapImage的方法,因此不需要异步调用。我一直得到“为不同的线程编组”的错误,这意味着我需要在UI线程上创建RandomAccessStream

=========================================================


演示代码可以在

上找到,我想我们可以从两个方面开始改变这一点:

  • Album
    Song
    类继承
    INotifyPropertyChanged
    接口,以便及时响应数据更改
  • 公共类相册:列表,INotifyPropertyChanged
    {
    // ...
    私有字符串\u标题;
    /// 
    ///专辑的标题
    /// 
    公共字符串标题
    {
    获取=>\u标题;
    设置
    {
    _标题=价值;
    OnPropertyChanged();
    }
    }
    公共事件属性更改事件处理程序属性更改;
    公共void OnPropertyChanged([CallerMemberName]字符串propertyName=”“)
    {
    PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
    }
    }
    
    公共类歌曲:INotifyPropertyChanged
    {
    //...
    私有字符串\u标题;
    /// 
    ///歌名
    /// 
    公共字符串标题
    {
    获取=>\u标题;
    设置
    {
    _标题=价值;
    OnPropertyChanged();
    }
    }
    公共事件属性更改事件处理程序属性更改;
    公共void OnPropertyChanged([CallerMemberName]字符串propertyName=”“)
    {
    PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
    }
    }
    
  • 按标识符阻止相册控件内的多个分配
  • private bool\u isLoading=false;
    私有静态异步void HandleAlbumChange(DependencyObject d、DependencyPropertyChangedEventArgs e)
    {
    如果(d是控制)
    {
    if(控制装置.\u卸载)
    回来
    如果(例如,NewValue是相册)
    {
    控件。_isLoading=true;
    //句柄代码
    控件。_isLoading=false;
    }
    其他的
    {
    control.Songs.Clear();
    control.albumSleeve.Source=null;
    }
    }
    }
    
    数据模板

    
    

    谢谢。

    问题似乎是我使用了DependencyPropertyChangedEventArgs
    e
    e.NewValue
    中的相册:

    if (e.NewValue is Album album)
       {
           control.DoStuff(album);
           await control.DoStuffAsync(album);
           control.DoMoreStuff(album);
       }
    
    如果我使用
    control.Album
    ,问题将不再显示。显然,当使用DependencyPropertyChangedEventArgs时
    control.DoStuff(control.Album);
    await control.DoStuffAsync(control.Album);
    control.DoMoreStuff(control.Album);