Xamarin ProgressBar无法实时更新 [英] Xamarin ProgressBar not updating real time

查看:105
本文介绍了Xamarin ProgressBar无法实时更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

全部

我对Xamarin还是相当陌生,尤其是C#,请客气...!

I'm fairly new to Xamarin and more notably, C# so please, be kind ...!

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

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.

毫无疑问,这是一种更好的方法,但是为了将专辑封面返回到PCL,我已将iOS UIImage转换为System.IO.Stream对象,并通过Song将其返回.模型.

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.

添加此美术作品功能会导致在处理每首歌曲时产生更大的开销.为了使用户对正在发生的事情有所了解,我在页面上放置了一个进度栏,希望每次处理单首歌曲时它都会更新.

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.

我目前不在使用MVVM,所以这是后面的代码...

I'm not using MVVM at this stage, so this is the code behind ...

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;
        }
    }
}

...这是XAML页面...

... this is the XAML page ...

<?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>

这是歌曲模型...

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; }
    }
}

...这是IMedia界面...

... this is the IMedia interface ...

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);
    }
}

...这是.iOS项目中的DependencyService实现...

... and this is the DependencyService implementation within the .iOS project ...

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;
        }
    }
}

...您将看到我已将progress值绑定到一个属性.也许出于此功能的目的,我可以在运行时通过ProgressBar对象对其进行更新,但我知道绑定是有效的.

... 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.

我只是无法确定为什么它不是即时更新的.如果我进行调试,它将进入回调并更新属性,并触发OnPropertyChanged事件,但UI直到结束都不会更新.

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.

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

I'm thinking it has something to do with the whole async/await thing but can't be sure.

我确定外面有人会帮我解决这个问题,我很感激即将到来的帮助.

I'm sure someone out there has the answer for me and I'm appreciative of any forthcoming help.

谢谢

推荐答案

它看起来像是因为您在ui线程中进行了所有耗时的计算.该线程应专用于更新ui.如果您在此线程中进行大量计算并且想要更新ui,则它将无法正常工作,因为ui线程正忙于您的计算.您必须做的是在另一个线程中启动计算(根据您的要求使用Task.Factory.StartNewTask.Run之类的东西). 现在,您正在另一个线程中运行长进程,您必须通过调用Device.BeginInvokeOnMainThread来更新ui线程中的ui.

It looks like because you do all your time consuming computations in the ui thread. This thread should be dedicated to update the 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. 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). Now that you are running your long process in another thread, you must update the ui in the ui thread by calling Device.BeginInvokeOnMainThread.

最后,这就是您可以得到的:

In the end, this is what you can get :

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

还有

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

这篇关于Xamarin ProgressBar无法实时更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆