所有
我对Xamarin来说相当新,更值得注意的是,C#所以,请善待......!
我有一个Xamarin.Forms应用程序,它使用DependencyService下拉到.iOS,然后遍历我的设备上的所有歌曲并使用自定义的“Song”模型返回。
毫无疑问,有更好的方法可以做到这一点但是为了将专辑图片还原回PCL,我已经使用iOS UIImage并将其转换为System.IO.Stream对象并通过歌曲返回模型。
添加此艺术作品功能会在处理每首歌曲时产生更大的开销。为了让用户了解正在发生的事情,我在页面上放了一个进度条,并希望每次处理一首歌时都要更新。
我的问题是,我无法让进度条实时更新。它只在过程完成后才会更新。
我现阶段没有使用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;
}
}
}
...这是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>
这是歌曲模型......
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界面......
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实现......
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;
}
}
}
...你会看到我已将进度值绑定到属性。也许为了这个功能的目的,我可以在运行时通过ProgressBar对象更新它,但我知道绑定有效。
我无法理解为什么它没有动态更新。如果我调试,它将进入回调并更新属性并触发OnPropertyChanged事件,但UI直到最后才更新。
我认为这与整个异步/等待事情有关,但无法确定。
我相信那里的人有我的答案,我很感激任何即将到来的帮助。
由于
答案 0 :(得分:4)
看起来是因为你在ui线程中完成所有耗费时间的计算。该线程应专用于更新ui。如果你在这个线程中进行大量计算并且想要更新ui,它将无法工作,因为ui线程忙于你的计算。您必须做的是在另一个线程中启动您的计算(根据您的要求,使用stmt_frei_list = " SELECT MBS.PartID, MBS.Supplier, MBS.Freigabe, V.VerwendID, V.PartID, V.SupplierID, V.Date, " & _
" SZ.SupplierID, SZ.Supplier " & _
" FROM Table1 as MBS INNER JOIN Table2 as V ON MBS.PartID = V.PartID " & _
" INNER JOIN Table3 AS SZ ON SZ.Supplier = MBS.Supplier AND V.SupplierID = SZ.SupplierID " & _
" WHERE MBS.PartID = " & id & " " & _
" ORDER BY V.Date DESC "
或@Repository
public interface MessageRepository extends MongoRepository<Message, Long> {
Long countByReadedWhereReceiverIs(boolean readed,String receiver);
}
之类的东西)。
现在您正在另一个线程中运行您的长进程,您必须通过调用Task.Factory.StartNew
来更新ui线程中的ui。
最后,您可以获得:
Task.Run
并且
Device.BeginInvokeOnMainThread