How can I stop a Device.StartTimer() when I change pages? - c#

I have a ListView that I am updating every 5 seconds using Device.StartTimer() and I would like to stop the timer when it leaves the ViewModel page. as you must intuit necsito do this because Device.StartTimer () is global and even when I change the page is still updating my ListView, how can I make ViewModel know that I'm changing pages?
This is part of my ViewModel:
private ObservableCollection sensors;
public ObservableCollection<PcData> Sensors
{
get { return sensors; }
set
{
sensors = value;
OnPropertyChanged();
}
}
public MonitoringTabsViewModel(string idCode, string description)
{
Description = description;
LoadSensors(idCode);
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
RefreshSensors(idCode);
return true;
});
}
private async void LoadSensors(string idCode)
{
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
private async void RefreshSensors(string idCode)
{
Sensors = null;
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
In the end I have come to the following implementation which actually does what I wanted:
ViewModel:
public class MonitoringTabsViewModel : Notificable
{
public string IdCode { get; set; }
public bool InPage { get; set; }
private string description;
public string Description
{
get { return description; }
set
{
description = value;
OnPropertyChanged();
}
}
private ObservableCollection<PcData> sensors;
public ObservableCollection<PcData> Sensors
{
get { return sensors; }
set
{
sensors = value;
OnPropertyChanged();
}
}
public MonitoringTabsViewModel(string idCode, string description)
{
IdCode = idCode;
Description = description;
LoadSensors(idCode);
MessagingCenter.Subscribe<MonitoringView>(this, "OnAppearing", (sender) =>
{
InPage = true;
});
MessagingCenter.Subscribe<MonitoringView>(this, "OnDisAppearing", (sender) =>
{
InPage = false;
});
Device.StartTimer(TimeSpan.FromSeconds(5), TimerCallBack);
}
private bool TimerCallBack()
{
if (InPage)
{
RefreshSensors(IdCode);
MessagingCenter.Unsubscribe<MonitoringView>(this, "OnAppearing");
return true;
}
else
{
MessagingCenter.Unsubscribe<MonitoringView>(this, "OnDisAppearing");
return false;
}
}
private async void LoadSensors(string idCode)
{
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
private async void RefreshSensors(string idCode)
{
Sensors = null;
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
}
View:
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Send<MonitoringView>(this, "OnAppearing");
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Send<MonitoringView>(this, "OnDisAppearing");
}
There are still two things that concern me:
1. I do not know if the management I'm giving to the MessagingCenter is appropriate, as you can see I'm unsubscribing in my TimerCallBack method, by putting breakpoints in the two calls to the unsubscribe method I see that while the timer is running every 5 seconds The unsubscribe method of the onAppearing message is still called.
2. Although this implmentacion works, I still have the problem that when sleeping the application or put it in the background is still running my method RefreshSensors () and I would like to be in segudno flat also stop the execution.
Could someone give me ideas of these two concerns that I still have?

Page has 2 indicator methods OnAppearing() & OnDisappearing() depends on your setup you should hookup to this events and notify the ViewModel.
This can be done in multiple ways:
Page may have a direct or indirect reference (BindingContext) to the ViewModel so just hookup.
You can use MessagingCenter.
If you have a custom handmade NavigationService you could hookup there.
Use existing MVVM Framework, there are plenty of them and most of them support this scenario

I still have the problem that when sleeping the application or put it
in the background is still running my method RefreshSensors ()
If you look in you App.xaml.cs file, you'll find the following methods:
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}

Related

Changing the icon in a button based off of MediaPlaybackState

I have a MediaPlayerElement and the following class to control it:
class MediaPlayer : INotifyPropertyChanged
{
private MediaPlaybackState _State;
public MediaPlaybackState State
{
get
{
return _State;
}
set
{
_State = value;
OnPropertyChanged();
}
}
public BitmapImage AlbumArt;
public string Title;
public string Subtitle;
private MediaPlayerElement mediaPlayer;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void PlayerStateChanged(MediaPlaybackSession session, object sender)
{
State = session.PlaybackState;
}
public void SetMediaElement(MediaPlayerElement param)
{
mediaPlayer = param;
}
public void PlayFromSearchResult(SearchResult result)
{
AlbumArt = new BitmapImage();
AlbumArt.UriSource = new Uri(result.StationImage);
Title = result.StationName;
Subtitle = result.Subtext;
PlayFromRemoteM3U(result.StreamURL);
}
public void Pause()
{
mediaPlayer.MediaPlayer.Pause();
}
public void Play()
{
mediaPlayer.MediaPlayer.Play();
mediaPlayer.MediaPlayer.PlaybackSession.PlaybackStateChanged += PlayerStateChanged;
}
public async void PlayFromRemoteM3U(string url)
{
--SNIP--
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(streamDownloadUrl));
Play();
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
I would like to have a play/pause button that changes it's content based on the current player state, I'm currently debugging with a TextBlock to display the current state:
<TextBlock x:Name="media_player_state" x:FieldModifier="public" FontSize="20" Text="{x:Bind MediaPlayerInstance.State, Mode=OneWay}">Play</TextBlock>
When I run the application and start a stream so the state changes, I get the following error:
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
I want to know if there I am trying to accomplish this the right way or how to fix this.
Thanks
Because you're calling it from another thread, you need to use a dispatcher
public void PlayerStateChanged(MediaPlaybackSession session, object sender)
{
DispatcherHelper.ExecuteOnUIThreadAsync(() =>
{
State = session.PlaybackState;
});
}
please note 2 things:
1- DispatcherHelper is part of the windows community toolkit nuget package (https://www.nuget.org/profiles/Microsoft.Toolkit)
2- DispatcherHelper will be deprecated in favor of the better DispatcherQueueHelper in the 7.0 release
Because you are not setting the state everytime you Play() or Pause().
Change the _State value and then use OnpropertyChanged("State")

How can I stop a Device.StartTimer () when I change the page and when my app is moved to the background? and correct use of MessagingCenter

I'm trying to create a ListView which refreshes its content every 5 seconds, for this I'm using a Device.StartTimer (), I've managed to make it work, but I've noticed that Device.StartTimer () is global to the application and what put in the callback is still running even when I change the page or change the application on my device.
My question is: How can I get the timer to stop when I change the page and change the application?
So far I have had some progress, which is that using OnAppearing, OnDisAppearing and the MessagingCenter I have managed to change the page when the timer stops.
With this implementation, I am worried that I really do not know if I am really unsubscribing from the message, it is the right place, I would like someone to tell me about it too.
Below fragments of my View and my ViewModel:
View:
public partial class MonitoringView : TabbedPage
{
MonitoringViewModel context = new MonitoringViewModel();
public MonitoringView()
{
InitializeComponent();
BindingContext = context;
}
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Send<MonitoringView>(this, "OnAppearing");
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Send<MonitoringView>(this, "OnDisAppearing");
}
}
ViewModel:
public class MonitoringTabsViewModel : Notificable
{
public string IdCode { get; set; }
public bool InPage { get; set; }
private string description;
public string Description
{
get { return description; }
set
{
description = value;
OnPropertyChanged();
}
}
private ObservableCollection<PcData> sensors;
public ObservableCollection<PcData> Sensors
{
get { return sensors; }
set
{
sensors = value;
OnPropertyChanged();
}
}
public MonitoringTabsViewModel(string idCode, string description)
{
IdCode = idCode;
Description = description;
LoadSensors(idCode);
MessagingCenter.Subscribe<MonitoringView>(this, "OnAppearing", (sender) =>
{
InPage = true;
});
MessagingCenter.Subscribe<MonitoringView>(this, "OnDisAppearing", (sender) =>
{
InPage = false;
});
Device.StartTimer(TimeSpan.FromSeconds(5), TimerCallBack);
}
private bool TimerCallBack()
{
if (InPage)
{
RefreshSensors(IdCode);
MessagingCenter.Unsubscribe<MonitoringView>(this, "OnAppearing");
return true;
}
else
{
MessagingCenter.Unsubscribe<MonitoringView>(this, "OnDisAppearing");
return false;
}
}
private async void LoadSensors(string idCode)
{
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
private async void RefreshSensors(string idCode)
{
Sensors = null;
Sensors = new ObservableCollection<PcData>(await App.WebApiManager.GetCurrentStatusDeviceAsync(idCode));
}
}
Solution:
With this implementation, I am worried that I really do not know if I
am really unsubscribing from the message
To test whether you really have unsubscribed from the message, you can add a breakpoint to the RefreshSensors function when you are debugging the app which is connecting with VS.
Then you can send a message the same as MessagingCenter.Send<MonitoringView>(this, "OnAppearing"); in another page after you leaving your MonitoringView to see if the breakpoint has been triggered, if not, it means you have unsubscribed from the message successfully.
And in you code of TimerCallBack, I think you should not unsubscribe the message here. You will never receive the message when you come back from another page since you have unsubscribed them. You can unsubscribe the message when this page is onDestory.
private bool TimerCallBack()
{
if (InPage)
{
RefreshSensors(IdCode);
//MessagingCenter.Unsubscribe<MonitoringView>(this, "OnAppearing");
return true;
}
else
{
//MessagingCenter.Unsubscribe<MonitoringView>(this, "OnDisAppearing");
return false;
}
}
Also, you should put Device.StartTimer(TimeSpan.FromSeconds(5), TimerCallBack); inside onappearing message.
public MonitoringTabsViewModel(string idCode, string description)
{
IdCode = idCode;
Description = description;
LoadSensors(idCode);
MessagingCenter.Subscribe<MonitoringView>(this, "OnAppearing", (sender) =>
{
InPage = true;
Device.StartTimer(TimeSpan.FromSeconds(5), TimerCallBack);
});
MessagingCenter.Subscribe<MonitoringView>(this, "OnDisAppearing", (sender) =>
{
InPage = false;
});
}
Once the OnAppearing message has been received, the Device.StartTimer will run in the page and once the OnDisAppearing message has been received, the callback will return false and Device.StartTimer will stop.

Refresh or update content page every few seconds automatically

I am using Xamarin.forms (PCL) and I need to refresh/update Content Page with its data every few seconds. The data is retrieved from API in the viewmodel.
Is there any method or handler that can be used periodically to call the Get Api periodically inside the page.xaml.cs, something like:
methodRunPeriodically()
{
userdata = await UserService.GetUserasync(_UserViewModel.EmployeeId);
}
Xamarin.Forms has an API for starting a timer that you might find useful for this, documented here.
Device.StartTimer (TimeSpan.FromSeconds(10), () => {
// If you want to update UI, make sure its on the on the main thread.
// Otherwise, you can remove the BeginInvokeOnMainThread
Device.BeginInvokeOnMainThread(() => methodRunPeriodically());
return shouldRunAgain;
});
Based on the code in the above question, you would ensure that:
Your userdata object implements IPropertyChange as follows:
//Other usings skipped for brevity
...
...
using System.ComponentModel;
using System.Runtime.CompilerServices;
// This is a simple user class that
// implements the IPropertyChange interface.
public class DemoUser : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private string userName = string.Empty;
private string phoneNumber = string.Empty;
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public DemoUser()
{
}
public string Id { get; set; }
public string UserName
{
get
{
return this.userName;
}
set
{
if (value != this.userName)
{
this.userName = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumber;
}
set
{
if (value != this.phoneNumber)
{
this.phoneNumber = value;
NotifyPropertyChanged();
}
}
}
}
In your ContentPage, you then try the following, (I slightly modified the code by others above):
public class UserPage : ContentPage
{
private DemoUser demoUser;
private int intervalInSeconds;
public UserPage()
{
//Assuming this is a XAML Page....
InitializeComponent();
}
public UserPage(DemoUser demoUser, int intervalInSeconds = 10) : this()
{
this.demoUser = demoUser;
this.intervalInSeconds = intervalInSeconds;
this.BindingContext = this.demoUser;
Device.StartTimer(TimeSpan.FromSeconds(this.intervalInSeconds), () =>
{
Device.BeginInvokeOnMainThread(() => refreshDemoUser());
return true;
});
}
private async void refreshDemoUser()
{
this.demoUser = await getDemoUserById(this.demoUser.Id);
}
}
You can do as follows to run a Task when 10 seconds has passed. Returning true in Device.StartTimer will ensure that the Timer keeps running. Also, you want to ensure that you invoke the method on the main thread to update the UI:
public MyConstructor()
{
StartTimer();
}
private void StartTimer()
{
Device.StartTimer(System.TimeSpan.FromSeconds(10), () =>
{
Device.BeginInvokeOnMainThread(UpdateUserDataAsync);
return true;
});
}
private async void UpdateUserDataAsync()
{
userdata = await UserService.GetUserasync(_UserViewModel.EmployeeId);
}
If your API doesn't expose an EventHandler that you can subscribe to, then you need to do as mentioned in my example above.
You should just bind the UI to properties in your ViewModel and then set those properties appropriately. Calling OnPropertyChanged() will trigger Xamarin.Forms to update the UI based on the bound properties. Something like below:
//Code in Page
public class MyPage : ContentPage
{
public MyPage()
{
var entry = new Entry();
BindingContext = new MyViewModel();
entry.SetBinding<MyViewModel>(Entry.TextProperty, vm=>vm.EntryText);
Content = entry;
}
}
//Code in ViewModel
public class MyViewModel() : INotifyPropertyChanged
{
public MyViewModel()
{
Task.Factory.StartNew(()=> methodRunPeriodically());
}
string entryText;
public string EntryText
{
get { return entryText; }
set
{
if(entryText == value)
return;
entryText = value;
OnPropertyChanged();
}
}
bool shouldRun = true;
async Task methodRunPeriodically()
{
while(shouldRun)
{
userdata = await UserService.GetUserasync(_UserViewModel.EmployeeId);
EntryText = userdata.FirstName;
await Task.Delay(5000); //Run this every 5 seconds
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In this pattern, we are kicking off a long-running task that will run in a loop. It is reaching out to refresh the userData every 5 seconds and then setting the EntryText property. In the setter of the EntryText property in our ViewModel, we are calling OnPropertyChanged() which will cause Xamarin.Forms to update the UI. Calling OnPropertyChanged() triggers Xamarin.Forms to switch thread context from the background task to the UI thread and then back to the background task.
I didn't write this in XAML, but the binding would be pretty much the same except the entry would be like below:
<Entry Text={Binding EntryText}/>
EDIT
#therealjohn's answer is good also. You could use that instead of my while loop like below:
bool shouldRun = true;
methodRunPeriodically()
{
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
userdata = await UserService.GetUserasync(_UserViewModel.EmployeeId);
EntryText = userdata.FirstName;
return shouldRun;
});
}
You can review what the Forms source code is doing with the Device.StartTimer on the native iOS and Android.
Update UI every one second:
Device.StartTimer(TimeSpan.FromMilliseconds(1000), loop2);
bool loop2()
{
Device.BeginInvokeOnMainThread(() => updateUI());
return true;
}
or:
Device.StartTimer(TimeSpan.FromMilliseconds(1000), loop2);
bool loop2()
{
Device.BeginInvokeOnMainThread(() => {
updateUI();
//more stuff;
});
return true;
}

Using new ViewModel each time I open a page

I didn't know how better to word the title so I went with solution that came to my mind.
Here is the problem. I have a page that has list and each item on the lists opens a detail page (on click). But the VM is reused, which causes me several problems.
Previous data can be seen for split second when opening a the detail page
I need certain properties to be set to specific values when the page open, but since the VM is reused it keeps all the values from the previous detail and this messes up my logic.
This UWP app. I'm using Template10 framework's NavigationService to move between pages.
Main Page ViewModel
public class MainPageViewModel : ViewModelBase {
private List<MangaItem> _mangaList;
public List<MangaItem> mangaList {
get { return _mangaList; }
set { Set(ref _mangaList, value); }
}
private string _mainSearchText;
public string mainSearchText {
get { return _mainSearchText; }
set { Set(ref _mainSearchText, value); }
}
public MainPageViewModel() {
_mangaList = new List<MangaItem>();
mangaList = new List<MangaItem>();
Initialize();
}
private async void Initialize() {
mangaList = await MangaListGet.GetListAsync();
}
public async void MainSearchSubmitted() {
mangaList = await MangaListGet.GetListAsync(_mainSearchText);
}
public void MangaSelected(object sender, ItemClickEventArgs e) {
var mangaItem = (MangaItem)e.ClickedItem;
NavigationService.Navigate(typeof(Views.MangaDetail), mangaItem.id);
}
}
And Detail Page ViewModel
class MangaDetailViewModel : ViewModelBase {
private MangaItem _mangaDetail;
public MangaItem mangaDetail {
get { return _mangaDetail; }
set { Set(ref _mangaDetail, value); }
}
private string _mangaId;
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState) {
_mangaId = parameter as string;
Initialize();
await Task.CompletedTask;
}
private async void Initialize() {
mangaDetail = await MangaDetailGet.GetAsync(_mangaId);
}
public void ChapterSelected(object sender, ItemClickEventArgs e) {
var _chapterId = (ChapterListItem)e.ClickedItem;
NavigationService.Navigate(typeof(Views.ChapterPage), _chapterId.id);
}
}
This code only shows the first problem is displaying previously loaded data for a split second. If needed I will add code that showcases the other problem, but I' not sure if it's really relevant right now. I'm thinking that maybe my entire logic is flawed or something.
EDIT:
<Page.DataContext>
<vm:ChapterPageViewModel x:Name="ViewModel" />
</Page.DataContext>
where vm is xmlns:vm="using:MangaReader.ViewModels".
Another solution is to use Bootstrapper.ResolveforPage() which is intended to handle dependency injection but would easily serve your needs. Like this:
[Bindable]
sealed partial class App : BootStrapper
{
static ViewModels.DetailPageViewModel _reusedDetailPageViewModel;
public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
if (page.GetType() == typeof(Views.DetailPage))
{
if (_reusedDetailPageViewModel == null)
{
_reusedDetailPageViewModel = new ViewModels.DetailPageViewModel();
}
return _reusedDetailPageViewModel;
}
else
{
return null;
}
}
}
The NavigationService will treat this the same as any other view-model. Meaning it will call OnNavTo() and the other navigation overrides you include.
Best of luck.
While Template10 documentation states the NavigationCacheMode is disabled by default, that isn't the case in it's example templates (as of writing this). This is set in View C# code (.xaml.cs file).
.xaml.cs file
namespace MangaReader.Views {
public sealed partial class MangaDetail : Page {
public MangaDetail() {
InitializeComponent();
//NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled; //this was set by default
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Disabled;
}
}
}
Now, new ViewModel will be created each time you access a this page.

How to watch a MediaPlayer and get feedback on it's CurrentProgress?

I'm writing an android app that uses the MediaPlayer. I've created a custom IAudioPlayer as a wrapper for the MediaPlayer so that I can eventually extend it to iOS.
public interface IAudioPlayer
{
bool IsPlaying { get; }
void Play(string fileName, int startingPoint);
void Play(string fileName);
void Pause();
void Stop();
int CurrentPosition();
bool HasFile();
void SkipForward(int seconds);
void SkipBackward(int seconds);
void SeekTo(int seconds);
}
Our app is structured using the Mvvm pattern and is using MvvmCross.
On our FragmentViewModel we've got commands such as IPlayCommand, IStopCommand, etc.
public class HomeFragmentViewModel : ViewModelBase
{
public HomeFragmentViewModel(IPlayCommand playCommand,
IStopCommand stopCommand,
ISkipForwardCommand skipForwardCommand,
ISkipBackwardCommand skipBackwardCommand)
{
_playCommand = playCommand;
_stopCommand = stopCommand;
_skipForwardCommand = skipForwardCommand;
_skipBackwardCommand = skipBackwardCommand;
}
private string _playPauseIcon = FontAwesome.icon_play;
public string PlayPauseIcon
{
get { return _playPauseIcon; }
set { _playPauseIcon = value; RaisePropertyChanged(() => PlayPauseIcon); }
}
private IPlayCommand _playCommand;
public IPlayCommand PlayCommand
{
get { return _playCommand; }
set { _playCommand = value; RaisePropertyChanged(() => PlayCommand); }
}
private IStopCommand _stopCommand;
public IStopCommand StopCommand
{
get { return _stopCommand; }
set { _stopCommand = value; RaisePropertyChanged(() => StopCommand); }
}
private ISkipForwardCommand _skipForwardCommand;
public ISkipForwardCommand SkipForwardCommand
{
get { return _skipForwardCommand; }
set { _skipForwardCommand = value; RaisePropertyChanged(() => SkipForwardCommand); }
}
private ISkipBackwardCommand _skipBackwardCommand;
public ISkipBackwardCommand SkipBackwardCommand
{
get { return _skipBackwardCommand; }
set { _skipBackwardCommand = value; RaisePropertyChanged(() => SkipBackwardCommand); }
}
}
On our View we've got buttons that bind to those commands, and all is working as expected.
However
Our view also has a SeekBar that we're going to use for quick scrubbing by the user. The seekbar needs to do two things...
allow the user quickly navigate to the spot that's required (this one "should" be easy
automatically update based on the track's current progress (this one I'm stuck on)
How can I write a notifier that triggers every second, and automatically update the seekbar binding? This notifier would need to live in the PCL with the Command Objects so that it can work cross platform. I'm struggling with getting started on this... I'm not sure where to create it or how to wire it up.
automatically update based on the track's current progress (this one I'm stuck on)
You should be able to do this using a binding to a View property. Here's some pseudo code:
In the ViewModel, add the SeekPosition property:
public double SeekPosition { /* normal INPC get/set */ }
In the View:
add a property and event pair like:
public event EventHandler CurrentPositionChanged;
public double CurrentPosition
{
get { return _mediaPlayer.CurrentPosition; }
set
{
_mediaPlayer.SeekTo(value);
}
}
add a timer to fire the CurrentPositionChanged event on the UI thread. For Android, create this timer in OnResume and destroy it in OnPause.
add a binding in OnCreate:
public override void OnCreate(args)
{
// normal base call and inflate
// ...
var set = this.CreateBindingSet<MyView, MyViewModel>();
set.Bind(this).For(v => v.CurrentPosition).To(vm => vm.SeekPosition);
set.Apply();
}
Note that this approach doesn't use a timer in the PCL. This is because other platforms like iOS and Windows shouldn't need the timer - as they should be able to use progress callbacks/events from the media players on those platforms instead.

Categories