WPF UI多任务处理

我正在以编程方式创建一些UI,这涉及一些繁重的处理。 基本上我想要的是在我的UI被构建/添加到窗口时运行加载器动画。 添加的UI是一些网格,一些图像被加载到它们中。

到目前为止,我已经尝试过BackgroundWorker,但由于我需要使用UI线程来添加我正在构建的UI,因此在添加的UI完成之前,加载器将无法启动/动画。

我也尝试过一些没有任何结果的异步方法。 我的最后一次尝试是这样的,但它仍然需要访问UI线程,这将最终冻结UI动画,直到工作完成

private async void begin() { await this.LongRunOpAsync(); } public Task LongRunOpAsync() { return Task.Run(() => { InitGrid(); stopLoadingScreen(); }); } 

我可能会遗漏一些东西,但我不知道是什么,我也没有想到这样做。 任何帮助,将不胜感激。

编辑:有繁重工作的方法

 private void makeIgrid() { Grid hostgrid = new Grid(); hostgrid.Name = "imagesHostGrid"; hostgrid.Width = 700; hostgrid.VerticalAlignment = VerticalAlignment.Top; hostgrid.HorizontalAlignment = HorizontalAlignment.Center; hostgrid.SetValue(Canvas.ZIndexProperty, 0); this.RegisterName(hostgrid.Name, hostgrid); Grid imagegrid = new Grid(); imagegrid.Name = "imagegrid"; imagegrid.Height = height2; //imagegrid.Width = 700; imagegrid.SetValue(Canvas.ZIndexProperty, 0); imagegrid.VerticalAlignment = VerticalAlignment.Top; imagegrid.HorizontalAlignment = HorizontalAlignment.Center; imagegrid.Margin = new Thickness(0, height1, 0, 0);//(left,top,right,bottom) RowDefinition iRow1 = new RowDefinition(); iRow1.Height = new GridLength(2, GridUnitType.Star); imagegrid.RowDefinitions.Add(iRow1); RowDefinition iRow2 = new RowDefinition(); iRow2.Height = new GridLength(70, GridUnitType.Star); imagegrid.RowDefinitions.Add(iRow2); ScrollViewer sv = new ScrollViewer { CanContentScroll = true, HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden, VerticalScrollBarVisibility = ScrollBarVisibility.Disabled }; for (int i = 0; i < images.Length; i++) { ColumnDefinition columns = new ColumnDefinition(); columns.MinWidth = 100; columns.Width = new GridLength(100, GridUnitType.Star); imagegrid.ColumnDefinitions.Add(columns); BitmapImage bmp = new BitmapImage(); bmp.BeginInit(); bmp.UriSource = new Uri(currentDirectory + "//Media//Images//" + selectedFolder + "//" + System.IO.Path.GetFileName(images[i].ToString()), UriKind.Relative); bmp.CacheOption = BitmapCacheOption.OnLoad; Debug.WriteLine("Loading: " + currentDirectory + "//Media//Images//" + selectedFolder + "//" + System.IO.Path.GetFileName(images[i].ToString())); bmp.EndInit(); Image img = new Image(); img.Name = System.IO.Path.GetFileNameWithoutExtension(images[i].ToString()); img.Source = bmp; img.VerticalAlignment = VerticalAlignment.Center; img.HorizontalAlignment = HorizontalAlignment.Center; img.TouchDown += addImagetoScreen; img.Width = 94; img.Stretch = Stretch.Uniform; img.SetValue(Canvas.ZIndexProperty, 0); this.RegisterName(img.Name, img); Border border = new Border(); border.SetResourceReference(Control.BackgroundProperty, "MenuSelected"); border.SetValue(Canvas.ZIndexProperty, 0); Grid.SetRow(border, 0); Grid.SetColumn(border, i); Grid.SetRow(img, 1); Grid.SetColumn(img, i); imagegrid.Children.Add(border); imagegrid.Children.Add(img); } sv.Content = imagegrid; sv.SetValue(Canvas.ZIndexProperty, 0); hostgrid.Children.Add(sv); mainGrid.Children.Add(hostgrid); } 

好。 删除所有代码并从头开始。

这就是你在WPF中的表现:

                            

代码背后:

 public partial class ItemsControlSample2 : Window { public ItemsControlSample2() { InitializeComponent(); //Make sure you change this path to a valid path in your PC where you have JPG files var path = @"F:\Media\Images\My Drums"; var images = Directory.GetFiles(path,"*.jpg") .Select(x => new ImageViewModel() { Path = x, }); DataContext = images.ToList(); } } 

数据项:

 public class ImageViewModel : INotifyPropertyChanged { private bool _isLoading; public bool IsLoading { get { return _isLoading; } set { _isLoading = value; OnPropertyChanged("IsLoading"); } } private ImageSource _imageSource; public ImageSource ImageSource { get { return _imageSource; } set { _imageSource = value; OnPropertyChanged("ImageSource"); } } private string _path; public string Path { get { return _path; } set { _path = value; OnPropertyChanged("Path"); LoadImageAsync(); } } private void LoadImageAsync() { IsLoading = true; var UIScheduler = TaskScheduler.FromCurrentSynchronizationContext(); Task.Factory.StartNew(() => { var bmp = new BitmapImage(); bmp.BeginInit(); bmp.UriSource = new Uri(Path, UriKind.Relative); bmp.CacheOption = BitmapCacheOption.OnLoad; bmp.EndInit(); bmp.Freeze(); return bmp; }).ContinueWith(x => { ImageSource = x.Result; IsLoading = false; },UIScheduler); } #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } #endregion } 

结果:

在此处输入图像描述

  • 请注意我是如何在XAML中声明性地定义 UI而不是在C#代码中以程序方式创建它。 这是一个更清洁的方法,因为它让WPF做它的工作

  • 我正在使用ItemsControl ,它是适用于WPF中所有“基于项目”的UI的方法。 无论视觉外观如何。

  • 还要注意我是如何利用DataBinding来为实际数据和DataTriggers填充UI以创建基本的有状态行为。

    • 在加载图像时( IsLoading == true ), Image.Source为null并显示TextBlock
    • 当图像完成加载( IsLoading == false )时, Image.Source绑定到ViewModel数据并隐藏TextBlock
  • 看看我如何使用WrapPanel进行布局而不是手动放置项目。 这为您提供了“类似资源管理器”的行为。 尝试调整窗口大小以查看结果。

  • 我强烈建议你阅读上面链接的文档,主要是ItemsControl的东西,还有Rachel的WPF Mentalitypost。

  • WPF Rocks。 只需将我的代码复制并粘贴到File -> New Project -> WPF Application然后自己查看结果。

  • 我正在使用C#4.0和.Net 4.0,所以我没有async/await 。 您应该能够删除所有基于Task的代码,并通过这种更新,更清晰的异步方法替换它。

  • 如果您需要进一步的帮助,请告诉我。

除了可能加载图像之外,我在那里看不到任何繁重的工作。

理想情况下,您应该在ui线程上创建所有UI元素(border / grid / etc),并且只在另一个线程中加载图像。

或者更好的是,不是以编程方式创建所有UI,而是应该使用某种forms的ItemsControlDataTemplate来生成所有ui,并在某种视图模型中加载所有图像异步。