In my windows phone 8.1 application I have a singleton service DataService which should once in a while be downloading some data. Meanwhile on UI I should be displaying the amount of data received. DataService.StartGettingData() gets called when user logs into the application:
void StartGettingData() { if (getDataTaskCancellationTokenSource != null) getDataTaskCancellationTokenSource.Cancel(); getDataTaskCancellationTokenSource = new CancellationTokenSource(); var token = getDataTaskCancellationTokenSource.Token; Task.Factory.StartNew(async () => await ExecuteCycleAsync(token), token); } async Task ExecuteCycleAsync(CancellationToken cancellationToken) { while (true) { cancellationToken.ThrowIfCancellationRequested(); await LoadDataAsync(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); await Task.Delay(timeTillNextDownload, cancellationToken); } } This task will be cancelled when user logs out with the help of
if (getDataTaskCancellationTokenSource != null) getDataTaskCancellationTokenSource.Cancel(); The property containing the result of download looks like this:
List<DataType> Data = new List<DataType>(); public IEnumerable<DataType> Data { get { return Data; } set { Data = value.ToList(); OnDataUpdated(); } } void OnDataUpdated() { var handler = DataUpdated; if (handler != null) handler(this, EventArgs.Empty); } This part seemed to be working until I had to display the amount of data on the screen. My MainViewModel gets instance of DataService injected with Ninject.
readonly IDataService DataService; public MainViewModel(IDataService dataService) { DataService = dataService; DataService.DataUpdated += DataService_DataUpdated; UpdateDataCount(); } void DataService_DataUpdated(object sender, EventArgs e) { UpdateDataCount(); } void UpdateDataCount() { DataCount = DataService.Data.Count(); } In xaml I've got TextBlock binded to DataCount property of MainViewModel
int DataCount; public int DataCount { get { return DataCount; } set { DataCount = value; OnPropertyChanged(); } } protected void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } And here is where problem appears: OnPropertyChanged fails with "The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))" which get's caught in DataService.LoadDataAsync(). I understand the runtime is trying to tell me I am accessing UI element from non ui thread. But am I? I thought OnPropertyChanged is the magic place which disconnects UI from the rest of background tasks. Of course, the problem can be solved implementing OnPropertyChanged this way:
public CoreDispatcher Dispatcher { get; set; } protected async void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { handler(this, new PropertyChangedEventArgs(propertyName)); }); } } But should it really be implemented this way? Or am I missing something in DataService.ExecuteCycleAsync()?