简体   繁体   English

Xamarin ProgressBar 不实时更新

[英]Xamarin ProgressBar not updating real time

All,全部,

I'm fairly new to Xamarin and more notably, C# so please, be kind ...!我是 Xamarin 的新手,更值得注意的是,C# 所以请善待......!

I have a Xamarin.Forms application that drops down to .iOS using a DependencyService which then traverses all of the songs on my device and returns using a custom "Song" model.我有一个 Xamarin.Forms 应用程序,它使用 DependencyService 下降到 .iOS,然后遍历我设备上的所有歌曲并使用自定义“歌曲”模型返回。

There's no doubt a better way to do it but in order to return the album artwork back to the PCL, I've taken the iOS UIImage and turned it into a System.IO.Stream object and am returning that through the Song model.毫无疑问,有更好的方法可以做到这一点,但为了将专辑插图返回到 PCL,我将 iOS UIImage 转换为 System.IO.Stream 对象,然后通过 Song 模型返回它。

Adding this artwork functionality resulted in a much larger overhead when processing each song.添加此艺术作品功能会在处理每首歌曲时产生更大的开销。 To try and give the user an appreciation for what is going on, I've put a progress bar on the page and want it to update each time I process a single song.为了尝试让用户了解正在发生的事情,我在页面上放置了一个进度条,并希望它在我每次处理一首歌曲时更新。

My problem is, I've been unable to get the progress bar to update in real time.我的问题是,我一直无法让进度条实时更新。 It only updates once the process is complete.它仅在该过程完成后更新。

I'm not using MVVM at this stage, so this is the code behind ...在这个阶段我没有使用 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;
        }
    }
}

... this is the XAML page ... ... 这是 XAML 页面 ...

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:TestApplication" x:Class="TestApplication.TestApplicationPage">
    <StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
        <Button Text="No. of Songs" Clicked="GetNoOfSongs"></Button>
        <ProgressBar Margin="20" x:Name="progressBar" Progress="{Binding Progress}" WidthRequest="400"></ProgressBar>
    </StackLayout>
</ContentPage>

This is the Song model ...这是宋模型...

using System;
using System.IO;

namespace TestApplication.Models
{
    public class Song
    {
        public string Artist { get; set; }
        public string Album { get; set; }
        public string Title { get; set; }
        public Stream Artwork { get; set; }
        public TimeSpan Duration { get; set; }
    }
}

... this is the IMedia interface ... ... 这是 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);
    }
}

... and this is the DependencyService implementation within the .iOS project ... ...这是 .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;
        }
    }
}

... you'll see I've bound the progress value to a property. ...您会看到我已将进度值绑定到一个属性。 Maybe for the purpose of this functionality I could just update it through the ProgressBar object when it runs but I know the binding works.也许出于此功能的目的,我可以在运行时通过 ProgressBar 对象更新它,但我知道绑定有效。

I just can't put my finger on why it's not updating on the fly.我只是不明白为什么它没有即时更新。 If I debug, it's going into the callback and updating the property and firing the OnPropertyChanged event but the UI isn't updating until the end.如果我调试,它会进入回调并更新属性并触发 OnPropertyChanged 事件,但 UI 直到最后才会更新。

I'm thinking it has something to do with the whole async/await thing but can't be sure.我认为它与整个 async/await 事情有关,但不能确定。

I'm sure someone out there has the answer for me and I'm appreciative of any forthcoming help.我相信有人会为我解答,我很感激任何即将到来的帮助。

Thanks谢谢

It looks like because you do all your time consuming computations in the ui thread.看起来是因为您在 ui 线程中进行了所有耗时的计算。 This thread should be dedicated to update the ui.此线程应专用于更新 ui。 If you do large computation in this thread and that you want to update the ui, it will not work because the ui thread is busy with your computation.如果您在此线程中进行大量计算并且想要更新 ui,它将无法工作,因为 ui 线程正忙于您的计算。 What you must do is to launch your computations in another thread (with something like Task.Factory.StartNew or Task.Run depending on you requirement).您必须做的是在另一个线程中启动您的计算(根据您的要求使用Task.Factory.StartNewTask.Run类的东西)。 Now that you are running your long process in another thread, you must update the ui in the ui thread by calling Device.BeginInvokeOnMainThread .现在您正在另一个线程中运行您的长进程,您必须通过调用Device.BeginInvokeOnMainThread更新 ui 线程中的 ui。

In the end, this is what you can get :最后,这就是你能得到的:

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

And并且

void UpdateProgress(double obj)
        {
            Device.BeginInvokeOnMainThread(() => Progress = (double)obj);
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM