C# Xamarin ProgressBar未实时更新

C# Xamarin ProgressBar未实时更新,c#,xamarin.forms,async-await,xamarin.ios,progress-bar,C#,Xamarin.forms,Async Await,Xamarin.ios,Progress Bar,全部, 我对Xamarin还比较陌生,更值得注意的是,C#所以请善待我 我有一个Xamarin.Forms应用程序,它使用DependencyService下拉到.iOS,然后遍历我设备上的所有歌曲,并使用自定义“歌曲”模型返回 毫无疑问,有更好的方法可以做到这一点,但为了将专辑插图返回到PCL,我已将iOS UIImage转换为System.IO.Stream对象,并通过歌曲模型返回 在处理每首歌曲时,添加此artwork功能会导致更大的开销。为了尝试让用户欣赏正在发生的事情,我在页面上放了一

全部,

我对Xamarin还比较陌生,更值得注意的是,C#所以请善待我

我有一个Xamarin.Forms应用程序,它使用DependencyService下拉到.iOS,然后遍历我设备上的所有歌曲,并使用自定义“歌曲”模型返回

毫无疑问,有更好的方法可以做到这一点,但为了将专辑插图返回到PCL,我已将iOS UIImage转换为System.IO.Stream对象,并通过歌曲模型返回

在处理每首歌曲时,添加此artwork功能会导致更大的开销。为了尝试让用户欣赏正在发生的事情,我在页面上放了一个进度条,希望它在每次处理一首歌曲时都能更新

我的问题是,我无法让进度条实时更新。它仅在流程完成后更新

我在这个阶段没有使用MVVM,所以这是代码隐藏

using Xamarin.Forms;
using TestApplication.Interfaces;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System;

namespace TestApplication
{
    public partial class TestApplicationPage : ContentPage, INotifyPropertyChanged
    {
        private double _progress;
        public double Progress
        {
            get { return _progress; }
            set
            {
                _progress = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public TestApplication()
        {
            InitializeComponent();
            BindingContext = this;
        }

        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        async void GetNoOfSongs(object sender, System.EventArgs e)
        {
            Action<double> progressUpdate = UpdateProgress;

            var songs = await DependencyService.Get<IMedia>().GetSongs(progressUpdate);

            await DisplayAlert("No. of Songs", string.Format("You have { 0} songs on your device!", songs.Count), "Ok");
        }

        void UpdateProgress(double obj)
        {
            Progress = (double)obj;
        }
    }
}
。。。这是IMedia接口

using System.Threading.Tasks;
using System.Collections.Generic;
using TestApplication.Models;
using System;

namespace TestApplication.Interfaces
{
    public interface IMedia
    {
        Task<bool> IsAuthorised();
        Task<List<Song>> GetSongs(Action<double> callback);
    }
}
使用System.Threading.Tasks;
使用System.Collections.Generic;
使用TestApplication.Models;
使用制度;
命名空间TestApplication.Interfaces
{
公共接口IMedia
{
任务已授权();
任务GetSongs(动作回调);
}
}
。。。这是.iOS项目中的DependencyService实现

using TestApplication.Interfaces;
using TestApplication.Models;
using MediaPlayer;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.IO;

[assembly: Xamarin.Forms.Dependency(typeof(TestApplication.iOS.Media))]
namespace TestApplication.iOS
{
    public class Media : IMedia
    {
        public List<Song> Songs { get; private set; }

        public async Task<bool> IsAuthorised()
        {
            await MPMediaLibrary.RequestAuthorizationAsync();

            if (MPMediaLibrary.AuthorizationStatus == MPMediaLibraryAuthorizationStatus.Authorized)
                return true;
            else
                return false;
        }

        public async Task<List<Song>> GetSongs(Action<double> callback)
        {
            Songs = new List<Song> { };

            if (await IsAuthorised())
            {
                var songs = MPMediaQuery.SongsQuery.Items;
                double index = 0;

                foreach (var song in songs)
                {
                    index++;

                    callback.Invoke(index / songs.Length);

                    Stream artwork = null;

                    if (song.Artwork != null)
                        artwork = song.Artwork.ImageWithSize(song.Artwork.Bounds.Size).AsPNG().AsStream();

                    Songs.Add(new Song
                    {
                        Artist = song.AlbumArtist,
                        Album = song.AlbumTitle,
                        Title = song.Title,
                        Artwork = artwork,
                        Duration = TimeSpan.FromSeconds(song.PlaybackDuration),
                    });
                }
            }

            return Songs;
        }
    }
}
使用TestApplication.Interface;
使用TestApplication.Models;
使用MediaPlayer;
使用制度;
使用System.Threading.Tasks;
使用System.Collections.Generic;
使用System.IO;
[程序集:Xamarin.Forms.Dependency(typeof(TestApplication.iOS.Media))]
命名空间TestApplication.iOS
{
公共类媒体:IMedia
{
公共列表歌曲{get;private set;}
公共异步任务已授权()
{
等待MPMediaLibrary.RequestAuthorizationAsync();
if(MPMediaLibrary.AuthorizationStatus==MPMediaLibrary AuthorizationStatus.Authorized)
返回true;
其他的
返回false;
}
公共异步任务GetSongs(操作回调)
{
歌曲=新列表{};
如果(等待IsAuthorized())
{
var songs=MPMediaQuery.SongsQuery.Items;
双指数=0;
foreach(歌曲中的歌曲)
{
索引++;
callback.Invoke(index/songs.Length);
流艺术品=空;
if(song.Artwork!=null)
artwork=song.artwork.ImageWithSize(song.artwork.Bounds.Size).AsPNG().AsStream();
歌曲。添加(新歌)
{
歌手,
唱片集=歌曲.AlbumTitle,
Title=歌曲,Title,
艺术品=艺术品,
持续时间=TimeSpan.FromSeconds(歌曲播放持续时间),
});
}
}
返回歌曲;
}
}
}
。。。您将看到我已将进度值绑定到属性。也许出于这个功能的目的,我可以在ProgressBar对象运行时通过它进行更新,但我知道绑定可以工作

我就是搞不懂为什么它没有及时更新。如果我调试,它将进入回调并更新属性并触发OnPropertyChanged事件,但UI直到最后才更新

我认为这与整个异步/等待有关,但不能确定

我相信有人能为我找到答案,我很感激任何即将到来的帮助


谢谢

看起来像是因为您在ui线程中完成了所有耗时的计算。此线程应专用于更新ui。如果您在此线程中执行大型计算,并且希望更新ui,那么它将无法工作,因为ui线程正忙于您的计算。您必须做的是在另一个线程中启动计算(使用类似于
Task.Factory.StartNew
Task.Run
的方法,具体取决于您的需求)。 现在您正在另一个线程中运行长进程,必须通过调用
Device.BeginInvokeOnMainThread
更新ui线程中的ui

最后,这就是你能得到的:

var songs = await Task.Run(async () => await DependencyService.Get<IMedia>().GetSongs(progressUpdate));

对于您的头上流,我建议您将您的流转换为base64字符串,这将大大减少头上流。谢谢,我也会检查一下。这个线程的主要目的是让进度条正确更新,并且有额外的开销,它工作时更清晰,但是是的,我同意,任何性能方面的东西都会检查Base64。为什么要两次
任务。运行
?为了更好的可读性,引入带有first Wait结果的变量。我不得不稍微更改您提供的代码,因为它没有编译,但这很有效。。。wait Task.Run(async()=>{var songs=wait DependencyService.Get().GetSongs(progressUpdate);})。。。我知道这与整个async/Wait有关。我对这件事有点误解了:整个背景任务。谢谢你的回答!至于Device.BeginInvokeOnMainThread,我以前有过,但显然它从来没有工作过,因为我没有调用Task.Run这一等式。@VMAtm,你是对的。我不知道为什么我要放置两个
任务。运行
。我编辑
using TestApplication.Interfaces;
using TestApplication.Models;
using MediaPlayer;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.IO;

[assembly: Xamarin.Forms.Dependency(typeof(TestApplication.iOS.Media))]
namespace TestApplication.iOS
{
    public class Media : IMedia
    {
        public List<Song> Songs { get; private set; }

        public async Task<bool> IsAuthorised()
        {
            await MPMediaLibrary.RequestAuthorizationAsync();

            if (MPMediaLibrary.AuthorizationStatus == MPMediaLibraryAuthorizationStatus.Authorized)
                return true;
            else
                return false;
        }

        public async Task<List<Song>> GetSongs(Action<double> callback)
        {
            Songs = new List<Song> { };

            if (await IsAuthorised())
            {
                var songs = MPMediaQuery.SongsQuery.Items;
                double index = 0;

                foreach (var song in songs)
                {
                    index++;

                    callback.Invoke(index / songs.Length);

                    Stream artwork = null;

                    if (song.Artwork != null)
                        artwork = song.Artwork.ImageWithSize(song.Artwork.Bounds.Size).AsPNG().AsStream();

                    Songs.Add(new Song
                    {
                        Artist = song.AlbumArtist,
                        Album = song.AlbumTitle,
                        Title = song.Title,
                        Artwork = artwork,
                        Duration = TimeSpan.FromSeconds(song.PlaybackDuration),
                    });
                }
            }

            return Songs;
        }
    }
}
var songs = await Task.Run(async () => await DependencyService.Get<IMedia>().GetSongs(progressUpdate));
void UpdateProgress(double obj)
        {
            Device.BeginInvokeOnMainThread(() => Progress = (double)obj);
        }