电影.cs
public class Movie : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
private string name;
public string Name { get; set; }
private int id;
public int Id { get; set; }
private string ratingNumber;
public string RatingNumber { get; set; }
private BitmapSource _bitmap;
public BitmapSource Bitmap
{
get; set;
}
private double rating;
public double Rating
{
get
{
return rating;
}
set
{
rating = value;
rating = rating / 2;
}
}
private ShowType showType;
public ShowType ShowType { get; set; }
}
服务.cs
public static FastObservableCollection<Movie> PopularMovies = new FastObservableCollection<Movie>();
public static async Task GetPopularMoviesAsync(string language, int page)
{
PopularMovies.Clear();
var popularMovies = await client.GetMoviePopularListAsync(language, page);
foreach (var movie in popularMovies.Results)
{
Movie mov = new Movie()
{
Bitmap = GetImage(client.GetImageUrl("w500", movie.PosterPath).AbsoluteUri,ImageType.Poster),
Id = movie.Id,
Name = movie.Title,
Rating = movie.VoteAverage,
RatingNumber = movie.VoteAverage.ToString(),
ShowType = ShowType.Movie,
};
if (PopularMovies.Any(x => x.Id == movie.Id))
{
}
else
{
PopularMovies.Add(mov);
}
}
}
Home.xaml.cs
private async void OnLoad()
{
var tasks = new List<Task>();
if (Service.PopularMovies.Count == 0)
{
tasks.Add(Service.GetPopularMoviesAsync("en",1));
}
await Task.WhenAll
}
public static BitmapImage GetImage(string url,ImageType imageType)
{
var bitmapImage = new BitmapImage(new Uri(url,UriKind.Absolute));
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
return bitmapImage;
}
<ListBox ScrollViewer.CanContentScroll="True" MaxHeight="500"
VirtualizingPanel.IsVirtualizing="true"
VirtualizingPanel.VirtualizationMode="Recycling" ItemsSource="{Binding PopularMovies, Mode=OneWay, IsAsync=True}" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" SelectionChanged="MoviesDisplay_OnSelectionChanged" ScrollViewer.ScrollChanged="MoviesDisplay_OnScrollChanged" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Disabled" Background="Transparent" x:Name="MoviesDisplay">
<b:Interaction.Behaviors>
<local:IgnoreMouseWheelBehavior />
</b:Interaction.Behaviors>
<ItemsControl.Resources>
<Style x:Key="ScaleStyle" TargetType="Image">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Grid.ZIndex" Value="1"/>
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="1.1" ScaleY="1.1"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Content.IsSelected, Mode=TwoWay, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Cursor="Hand" Margin="0 0 20 0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="0.052*"></RowDefinition>
<RowDefinition Height="0.05*"></RowDefinition>
</Grid.RowDefinitions>
<Viewbox HorizontalAlignment="Left" Grid.Row="0" Grid.Column="0">
<Image RenderTransformOrigin="0.5,0.5" Style="{StaticResource ScaleStyle}" Source="{Binding Bitmap}"></Image>
</Viewbox>
<Viewbox MaxWidth="200" HorizontalAlignment="Left" Grid.Row="1">
<TextBlock TextWrapping="Wrap" Text="{Binding Name}" Foreground="White"></TextBlock>
</Viewbox>
<Grid Grid.Row="2" Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Viewbox HorizontalAlignment="Left">
<materialDesign:RatingBar
x:Name="ReadOnlyRatingBar"
IsReadOnly="True"
Value="{Binding Rating}"></materialDesign:RatingBar>
</Viewbox>
<Viewbox Margin="10 0 0 0" Grid.Column="1" HorizontalAlignment="Left">
<TextBlock Text="{Binding RatingNumber}" Foreground="#767676"></TextBlock>
</Viewbox>
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
public class FastObservableCollection<T> : ObservableCollection<T>
{
private readonly object locker = new object();
/// <summary>
/// This private variable holds the flag to
/// turn on and off the collection changed notification.
/// </summary>
private bool suspendCollectionChangeNotification;
/// <summary>
/// Initializes a new instance of the FastObservableCollection class.
/// </summary>
public FastObservableCollection()
: base()
{
this.suspendCollectionChangeNotification = false;
}
/// <summary>
/// This event is overriden CollectionChanged event of the observable collection.
/// </summary>
public override event NotifyCollectionChangedEventHandler CollectionChanged;
/// <summary>
/// This method adds the given generic list of items
/// as a range into current collection by casting them as type T.
/// It then notifies once after all items are added.
/// </summary>
/// <param name="items">The source collection.</param>
public void AddItems(IList<T> items)
{
lock (locker)
{
this.SuspendCollectionChangeNotification();
foreach (var i in items)
{
InsertItem(Count, i);
}
this.NotifyChanges();
}
}
/// <summary>
/// Raises collection change event.
/// </summary>
public void NotifyChanges()
{
this.ResumeCollectionChangeNotification();
var arg
= new NotifyCollectionChangedEventArgs
(NotifyCollectionChangedAction.Reset);
this.OnCollectionChanged(arg);
}
/// <summary>
/// This method removes the given generic list of items as a range
/// into current collection by casting them as type T.
/// It then notifies once after all items are removed.
/// </summary>
/// <param name="items">The source collection.</param>
public void RemoveItems(IList<T> items)
{
lock (locker)
{
this.SuspendCollectionChangeNotification();
foreach (var i in items)
{
Remove(i);
}
this.NotifyChanges();
}
}
/// <summary>
/// Resumes collection changed notification.
/// </summary>
public void ResumeCollectionChangeNotification()
{
this.suspendCollectionChangeNotification = false;
}
/// <summary>
/// Suspends collection changed notification.
/// </summary>
public void SuspendCollectionChangeNotification()
{
this.suspendCollectionChangeNotification = true;
}
/// <summary>
/// This collection changed event performs thread safe event raising.
/// </summary>
/// <param name="e">The event argument.</param>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
// Recommended is to avoid reentry
// in collection changed event while collection
// is getting changed on other thread.
using (BlockReentrancy())
{
if (!this.suspendCollectionChangeNotification)
{
NotifyCollectionChangedEventHandler eventHandler =
this.CollectionChanged;
if (eventHandler == null)
{
return;
}
// Walk thru invocation list.
Delegate[] delegates = eventHandler.GetInvocationList();
foreach
(NotifyCollectionChangedEventHandler handler in delegates)
{
// If the subscriber is a DispatcherObject and different thread.
DispatcherObject dispatcherObject
= handler.Target as DispatcherObject;
if (dispatcherObject != null
&& !dispatcherObject.CheckAccess())
{
// Invoke handler in the target dispatcher's thread...
// asynchronously for better responsiveness.
dispatcherObject.Dispatcher.BeginInvoke
(DispatcherPriority.DataBind, handler, this, e);
}
else
{
// Execute handler as is.
handler(this, e);
}
}
}
}
}
}
它很慢并且会冻结 UI,并且会延迟图像显示,直到整个列表加载完成。我该如何解决这个问题?我 2018 年的旧项目与上面的代码相同,工作得很好,速度很快,而且没有阻塞 ui。但对于 .net 8.0 来说则不然。我不明白微软改变了什么。
我建议您在另一个线程中执行 OnLoad 方法的整个主体。
我也不明白为什么你对一项任务使用
var tasks = new List<Task>();
?这是代码复制错误吗?该示例是否未成功缩短?
示例:
private async void OnLoad() => await Task.Run(async () =>
{
var tasks = new List<Task>();
if (Service.PopularMovies.Count == 0)
{
tasks.Add(Service.GetPopularMoviesAsync("en", 1));
}
await Task.WhenAll();
});
此外,您很可能需要冻结 ImageSource (BitmapImage):
public static BitmapImage GetImage(string url, ImageType imageType)
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.UriSource = new Uri(url);
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}