"System.InvalidOperationException: Collection was modified" in MvvmCross TableView binding - uitableview

Hopefully someone can help me with the following, because I am completely stuck.
I receive the exception below in my MvvmCross Xamarin.iOS application when I bind on my TableView. This only happens when I change the datasource (each time I change the date, the TableView needs to get updated).
Incident Identifier: 7E7C2B15-7CC4-4AE7-9891-C4FD82358009
CrashReporter Key: 46CC21C0-DDE1-4313-9658-EC79D767939B
Hardware Model: iPhone7,2
Process: UurwerkiOS [4326]
Path: /var/containers/Bundle/Application/75969477-A516-44C3-A5A3-5B24DDDC89C8/UurwerkiOS.app/UurwerkiOS
Identifier: com.route2it.uurwerk
Version: 1.0 (1.0.96)
Code Type: ARM-64
Parent Process: ??? [1]
Date/Time: 2016-07-04T13:16:38Z
Launch Time: 2016-07-04T13:16:31Z
OS Version: iPhone OS 9.3.2 (13F69)
Report Version: 104
Exception Type: SIGABRT
Exception Codes: #0 at 0x1816ac11c
Crashed Thread: 5
Application Specific Information:
*** Terminating app due to uncaught exception 'System.AggregateException', reason: 'System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---> System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x10044bec0 + 0x00024> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNextRare () <0x1003bf900 + 0x0002f> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNext () <0x1003bf830 + 0x0009f> in <filename unknown>:0
at MvvmCross.Binding.BindingContext.MvxTaskBasedBindingContext.<OnDataContextChange>b__20_0 () <0x1007c1990 + 0x0023f> in <filename unknown>:0
at System.Threading.Tasks.Task.InnerInvoke () <0x10043f1f0 + 0x0005f> in <filename unknown>:0
at System.Threading.Tasks.Task.Execute () <0x10043ea20 + 0x00043> in <filename unknown>:0
--- End of inner exception stack trace ---
---> (Inner Exception #0) System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x10044bec0 + 0x00024> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNextRare () <0x1003bf900 + 0x0002f> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNext () <0x1003bf830 + 0x0009f> in <filename unknown>:0
at MvvmCross.Binding.BindingContext.MvxTaskBasedBindingContext.<OnDataContextChange>b__20_0 () <0x1007c1990 + 0x0023f> in <filename unknown>:0
at System.Threading.Tasks.Task.InnerInvoke () <0x10043f1f0 + 0x0005f> in <filename unknown>:0
at System.Threading.Tasks.Task.Execute () <0x10043ea20 + 0x00043> in <filename unknown>:0
At first I thought it was related to one of my Async methods (which maybe not complete in time while the next one was already running). So I removed all my async code, but the exception still occurs. I also made sure I don't change the enumerable collection myself. I fetch the data (which is simply a in memory array) and return it as a new list to the property to which the TableView is bound. Below are the code snippets that make up the binding (it's a lot of information but I wanted to be as complete as possible):
CalendarViewController:
public override void ViewDidLoad()
{
base.ViewDidLoad();
if (NavigationController != null)
NavigationController.NavigationBarHidden = false;
InitCalendar();
InitNavigationItem();
InitTableView();
ApplyConstraints();
var shiftForDateTableViewSource = new MvxSimpleTableViewSource(_tableView, CalendarTableViewCell.Key, CalendarTableViewCell.Key);
shiftForDateTableViewSource.DeselectAutomatically = true;
_tableView.RowHeight = 45;
_tableView.Source = shiftForDateTableViewSource;
var set = this.CreateBindingSet<CalendarView, CalendarViewModel>();
set.Bind(shiftForDateTableViewSource).To(vm => vm.ShiftsForSelectedDate);
set.Bind(shiftForDateTableViewSource).For(vm => vm.SelectionChangedCommand).To(vm => vm.ShiftSelectedCommand);
set.Apply();
_tableView.ReloadData();
}
private void InitTableView()
{
_tableView = new UITableView();
_tableView.RegisterClassForCellReuse(typeof(UITableViewCell), CalendarTableViewCell.Key);
Add(_tableView);
}
CalendarTableViewCell:
public partial class CalendarTableViewCell : MvxTableViewCell
{
public static readonly NSString Key = new NSString("CalendarTableViewCell");
public static readonly UINib Nib;
static CalendarTableViewCell()
{
Nib = UINib.FromName("CalendarTableViewCell", NSBundle.MainBundle);
}
protected CalendarTableViewCell(IntPtr handle) : base(handle)
{
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
var set = this.CreateBindingSet<CalendarTableViewCell, Shift>();
set.Bind(StartTimeLabel).To(vm => vm.StartDate).WithConversion("StringFormat", "HH:mm");
set.Bind(EndTimeLabel).To(vm => vm.EndDate).WithConversion("StringFormat", "HH:mm");
set.Bind(ColorBarView).For("BackgroundColor").To(vm => vm.Color).WithConversion("RGB");
set.Bind(TitleLabel).To(vm => vm).WithConversion("ConcatenatedEventTitle");
set.Bind(LocationLabel).To(vm => vm.Location);
set.Apply();
}
}
CalendarViewModel:
public class CalendarViewModel
: MvxViewModel
{
private readonly IShiftService _shiftService;
public CalendarViewModel(IShiftService shiftService)
{
if (shiftService == null)
throw new ArgumentNullException(nameof(shiftService));
_shiftService = shiftService;
}
public override void Start()
{
base.Start();
Shifts = _shiftService.GetShiftsForEmployeeAsync(1);
}
private IEnumerable<Shift> _shifts;
public IEnumerable<Shift> Shifts
{
get { return _shifts; }
set
{
SetProperty(ref _shifts,
value,
nameof(Shifts));
}
}
private IEnumerable<Shift> _shiftsForSelectedDate;
public IEnumerable<Shift> ShiftsForSelectedDate
{
get { return _shiftsForSelectedDate; }
private set
{
if (_shiftsForSelectedDate == value)
return;
SetProperty(ref _shiftsForSelectedDate,
value,
nameof(ShiftsForSelectedDate));
}
}
private DateTime? _selectedDate;
public DateTime? SelectedDate
{
get { return _selectedDate; }
set
{
if (_selectedDate == value)
return;
SetProperty(ref _selectedDate,
value,
nameof(SelectedDate));
if (_selectedDate.HasValue)
FetchShiftsForSelectedDate();
}
}
private void FetchShiftsForSelectedDate()
{
ShiftsForSelectedDate = _shiftService.GetShiftsForSelectedDateAsync(_selectedDate.Value);
}
}
MockShiftService (implements the IShiftService interface):
public class MockShiftService
: IShiftService
{
private IList<Shift> _shifts;
public MockShiftService()
{
Initialize();
}
public IEnumerable<Shift> GetShiftsForEmployeeAsync(int employeeId)
{
return _shifts;
}
public IEnumerable<Shift> GetShiftsForSelectedDateAsync(DateTime selectedDate)
{
var endDate = selectedDate.Date.Add(new TimeSpan(23, 59, 59));
return _shifts
.Where(s => s.StartDate <= endDate && s.EndDate >= selectedDate)
.ToList();
}
public Shift GetShiftByIdAsync(int shiftId)
{
return _shifts.First((shift) => shift.Id == shiftId);
}
private void Initialize()
{
var shifts = new List<Shift>();
// The in memory array gets populated here which
// is straight forward creating instances of the
// 'Shift' class and assigning it's properties before
// adding it to the 'shifts' collection. I left
// this code out to keep it as short as possible.
}
}
UPDATE:
I have referenced my project directly to the debug assemblies of MvvmCross and figured out that the exception is thrown on line 127 of the MvxTaskBasedBindingContext class and always happens on the second iteration. From this I conclude that the collection is changed during the first iteration. Unfortunately I cannot figure out why or how.
I noticed that the MvxTaskBasedBindingContext replaces the MvxBindingContext (changed by softlion on 11-5-2016). When I force my application to use the MvxBindingContext class all works well (although a bit laggy). This makes me believe the problem is in the MvxTaskBasedBindingContext but I really can't figure out why, any help would be greatly appreciated.
UPDATE 2:
After some more debugging and fiddling around I found out that the exception is related to the bindings set by my CalendarTableViewCell class (which should provide the layout for each item in the tableview defined in my CalendarViewController. When I comment out the bindings in the CalendarTableViewCell class the exception does not occur (see my code above). I still don't know what could be wrong though.

You can make use of DelayBind in your CalendarTableViewCell to delay binding until your DataContext gets set on your BindingContext
public partial class CalendarTableViewCell : MvxTableViewCell
{
...
public override void LayoutSubviews()
{
base.LayoutSubviews();
this.DelayBind(() =>
{
var set = this.CreateBindingSet<CalendarTableViewCell, Shift>();
set.Bind(StartTimeLabel).To(vm => vm.StartDate).WithConversion("StringFormat", "HH:mm");
set.Bind(EndTimeLabel).To(vm => vm.EndDate).WithConversion("StringFormat", "HH:mm");
set.Bind(ColorBarView).For("BackgroundColor").To(vm => vm.Color).WithConversion("RGB");
set.Bind(TitleLabel).To(vm => vm).WithConversion("ConcatenatedEventTitle");
set.Bind(LocationLabel).To(vm => vm.Location);
set.Apply();
});
}
}

The problem will not be fixed with delay binding.
The problem is that the Lists are enumerated in a Task, which can be modified while the enumeration is going on.
Task.Run(() =>
{
foreach (var binding in this._viewBindings)
{
foreach (var bind in binding.Value)
{
bind.Binding.DataContext = this._dataContext;
}
}
foreach (var binding in this._directBindings)
{
binding.Binding.DataContext = this._dataContext;
}
});
Before enumerating need to create copy of collection ToList() or ToArray().
This bug has beed already reported.
Link

Related

Xamarin forms Android timeout issue on StartUp

Hi, my android application sometimes stucks on startup. On Xamarin test cloud the failure seems like;
SetUp : System.TimeoutException : Timed out waiting...
at Xamarin.UITest.Shared.WaitForHelper.WaitFor (System.Func1 predicate, System.String timeoutMessage, Nullable1 timeout, Nullable1 retryFrequency, Nullable1 postTimeout) <0x7e4c998 + 0x004db> in :0
at Xamarin.UITest.Android.AndroidApp..ctor (IAndroidAppConfiguration appConfiguration) <0x7cacab8 + 0x0073b> in :0
at Xamarin.UITest.Configuration.AndroidAppConfigurator.StartApp (AppDataMode appDataMode) <0x7cac008 + 0x00063> in :0
at YH.MB.Test.AppInitializer.StartApp (Platform platform) <0x7cab060 + 0x00027> in :0
at YH.MB.Test.Tests.BeforeEachTest () <0x7cab010 + 0x00013> in :0
at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) <0x3249110 + 0x00093> in :0
When i tried it on Samsung s3(on xamarin test cloud) there was a StackTrace too;
java.lang.NullPointerException: key == null
at android.util.LruCache.get(LruCache.java: 113)
at com.android.providers.settings.SettingsProvider$SettingsCache.isRedundantSetValue(SettingsProvider.java: 872)
at com.android.providers.settings.SettingsProvider.insert(SettingsProvider.java: 574)
at android.content.ContentProvider$Transport.insert(ContentProvider.java: 201)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java: 148)
at android.os.Binder.execTransact(Binder.java: 367)
at dalvik.system.NativeStart.run(Native Method: )
I can not repeat that issue on debug mode. What can it be?
My Android OnCreate method is;
protected override void OnCreate(Bundle bundle)
{
// set the layout resources first
ToolbarResource = Resource.Layout.toolbar;
TabLayoutResource = Resource.Layout.tabs;
base.OnCreate(bundle);
Forms.Init(this, bundle);
#if ENABLE_TEST_CLOUD
Xamarin.Forms.Forms.ViewInitialized += (object sender, Xamarin.Forms.ViewInitializedEventArgs e) =>
{
if (!string.IsNullOrWhiteSpace(e.View.AutomationId))
{
e.NativeView.ContentDescription = e.View.AutomationId;
}
};
#endif
var container = new SimpleContainer();
container.Register<IDevice>(t => AndroidDevice.CurrentDevice);
container.Register<IDisplay>(t => t.Resolve<IDevice>().Display);
container.Register<IMediaPicker>(t => new MediaPicker());
container.Register<IFontManager>(t => new FontManager(t.Resolve<IDisplay>()));
container.Register<INetwork>(t => t.Resolve<IDevice>().Network);
container.Register<IJsonSerializer, MBJsonSerializer>();
container.Register<IPhoneService, PhoneService>();
container.Register<IUserDialogs>(t => UserDialogs.Instance);
if (!Resolver.IsSet)
Resolver.SetResolver(container.GetResolver());
UserDialogs.Init(this);
LoadApplication(new App());
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
My UITest SetUp Method ;
[TestFixture(Platform.Android)]
[TestFixture(Platform.iOS)]
public class Tests
{
IApp app;
Platform platform;
public Tests(Platform platform)
{
this.platform = platform;
}
[SetUp]
public void BeforeEachTest()
{
app = AppInitializer.StartApp(platform);
}
}
Thanks in advance.

Detecting when a template was loaded in wpf

I am working with an attached behavior for logging user actions on a ScrollBar.
my code:
class ScrollBarLogBehavior : Behavior<ScrollBar>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
}
void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
...
var track = (Track)AssociatedObject.Template.FindName("PART_Track", AssociatedObject);
// ** HERE is the problem: track is null ! **
...
}
How can I detect that the template has loaded and I can find the Track?
(when I call AssociatedObject.Template.LoadContent() the result containt the requested Track, so it i a matter of timing and not a matter of wrong template or naming)
Override the method OnApplyTemplate
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var textBox = Template.FindName("PART_Textbox", this) as TextBox;
}
I did not find any good way to detect when the template was loaded. However, I did find a way to find the Track:
in OnAttached() - register to Scroll event fo the ScrollBar (this can only happen after the entire template is loaded, of course):
protected override void OnAttached()
{
base.OnAttached();
_scrollHandler = new ScrollEventHandler(AssociatedObject_Scroll);
AssociatedObject.AddHandler(ScrollBar.ScrollEvent, _scrollHandler, true);
}
When handling the Scroll event, remove registration and find the Thumb:
void AssociatedObject_Scroll(object sender, ScrollEventArgs e)
{
var track = (Track)AssociatedObject.Template.FindName("PART_Track", Associated
if (track == null)
return;
AssociatedObject.RemoveHandler(ScrollBar.ScrollEvent, _scrollHandler);
// do my work with Track
...
}
If I understand correctly, you wish to create an attached behavior that will reference a template part after the ScrollBar has been loaded.
The following should work:
internal static class ScrollBarLogBehavior
{
public static readonly DependencyProperty LogUserActionProperty = DependencyProperty.RegisterAttached(
"LogUserAction",
typeof(bool),
typeof(ScrollBarLogBehavior),
new UIPropertyMetadata(default(bool), LogUserActionChanged));
public static bool GetLogUserAction(DependencyObject obj)
{
return (bool)obj.GetValue(LogUserActionProperty);
}
public static void SetLogUserAction(DependencyObject obj, bool value)
{
obj.SetValue(LogUserActionProperty, value);
}
public static void LogUserActionChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
if (s is ScrollBar scrollBar)
{
scrollBar.Loaded += OnScrollBarLoaded;
}
}
private static void OnScrollBarLoaded(object sender, RoutedEventArgs e)
{
if (sender is ScrollBar scrollBar)
{
if (scrollBar.Template != null)
{
// I'm not sure, but the `name` in the following method call might be case sensitive.
if (scrollBar.Template.FindName("PART_Track", scrollBar) is Track track)
{
// do work with `track` here
}
}
}
}
}
where you would "attach" the behavior in your XAML with:
<ScrollBar guiControls:ScrollBarLogBehavior.LogUserAction="True">
<!-- more here -->
</ScrollBar>
BE ADVISED: this implementation completely ignores the bool value that is being set for LogUserAction

How can i instantiate a MvxTableViewController directly?

I want to show a UIPopoverController by clicking on a UIBarButtonItem on the Navigation bar. This UIPopoverController needs a "ContentViewController", that would be a MvxTableViewController with some data-binding. The problem is, if I instantiate some class derived from MvxTableViewController directly - instead of doing ShowViewModel<blah>(), I got an exception on "base.ViewDidLoad" on the overriden method ViewDidLoad.
What am I missing?
Thanks in advance!
EDIT:
If i use, for instance, a MvxViewController with an UITableView:
public class Test : MvxViewController
{
public override void ViewDidLoad()
{
View = new UIView() { BackgroundColor = UIColor.White };
//TableView = new UITableView(new RectangleF(0, 0, 300, 300));
base.ViewDidLoad();
var table = new UITableView(new RectangleF(0, 0, 300, 300));
// ios7 layout
if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
EdgesForExtendedLayout = UIRectEdge.None;
var source = new MvxStandardTableViewSource(table, "TitleText Nome");
table.Source = source;
var set = this.CreateBindingSet<Test, Core.ViewModels.FirstViewModel>();
set.Bind(source).To(vm => vm.Distritos);
set.Apply();
table.ReloadData();
}
}
And if i have, on FirstViewModel's ViewDidLoad, during the construction of a Navigation Bar:
var buttonLocalizacao = new UIBarButtonItem("Localização", UIBarButtonItemStyle.Plain, (s, e) => {
distritoViewController = new Test();
nc = new UINavigationController(distritoViewController);
var distritoPopOver = new UIPopoverController(nc);
distritoPopOver.ContentViewController = nc;
distritoPopOver.PopoverContentSize = new SizeF(300, 300);
distritoPopOver.PresentFromBarButtonItem((UIBarButtonItem)s, UIPopoverArrowDirection.Up, true);
});
Everytime i hit the UIBarButtonItem "Localização" i get an exception on Test's "base.ViewDidLoad". Does this make sense?
EDIT 2:
This is the exception i receive:
"Unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object
at Cirrious.MvvmCross.ViewModels.MvxViewModelLoader.LoadViewModel (Cirrious.MvvmCross.ViewModels.MvxViewModelRequest request, IMvxBundle savedState) [0x00000] in :0
at Cirrious.MvvmCross.Touch.Views.MvxViewControllerExtensionMethods.LoadViewModel (IMvxTouchView touchView) [0x00000] in :0
at Cirrious.MvvmCross.Touch.Views.MvxViewControllerExtensionMethods+<>c__DisplayClass1.b__0 () [0x00000] in :0
at Cirrious.MvvmCross.Views.MvxViewExtensionMethods.OnViewCreate (IMvxView view, System.Func`1 viewModelLoader) [0x00000] in :0
at Cirrious.MvvmCross.Touch.Views.MvxViewControllerExtensionMethods.OnViewCreate (IMvxTouchView touchView) [0x00000] in :0
at Cirrious.MvvmCross.Touch.Views.MvxViewControllerAdapter.HandleViewDidLoadCalled (System.Object sender, System.EventArgs e) [0x00000] in :0 "
Finally got it:
Instead of:
var buttonLocalizacao = new UIBarButtonItem("Localização", UIBarButtonItemStyle.Plain, (s, e) => {
distritoViewController = new Test();
nc = new UINavigationController(distritoViewController);
var distritoPopOver = new UIPopoverController(nc);
distritoPopOver.ContentViewController = nc;
distritoPopOver.PopoverContentSize = new SizeF(300, 300);
distritoPopOver.PresentFromBarButtonItem((UIBarButtonItem)s, UIPopoverArrowDirection.Up, true);
});
I had to create a distinct ViewModel to hold my MvxTableViewController (each MvxTableViewController, or MvxViewController, demands a corresponding ViewModel?), meaning, i could not bind "Test", that is a MvxTableViewController, to another, different, ViewModel (FirstViewModel for instance). "Test" must have a ViewModel of it's own.
So, eventually the corresponding ViewModel must be instantiated previously, and only after that can we instantiate the MvxTableViewController using "CreateViewControllerFor". Replacing the above "Test" for "DistritoViewModel", the rest is straightforward:
var buttonLocalizacao = new UIBarButtonItem("Localização", UIBarButtonItemStyle.Plain, (s, e) => {
if (distritoPopOver == null)
{
var viewModel = new DistritoViewModel();
var secondv = this.CreateViewControllerFor(viewModel) as MvxTableViewController;
nc = new UINavigationController(secondv);
distritoPopOver = new UIPopoverController(nc);
distritoPopOver.PopoverContentSize = new SizeF(300, 300);
}
distritoPopOver.PresentFromBarButtonItem((UIBarButtonItem)s, UIPopoverArrowDirection.Up, true);
});
Keep in mind: the variable distritoPopover, that holds the UIPopoverController, must be a class variable, or instantiated outside the Action in UIBarButtonItem, otherwise it will be garbage collected and cause an immediate crash after display!
Thanks everyone for the tips :)

MVVMCross binding text on Ended event UITextField

I am trying to implement a custom binding on a subclass of UITextField so that the bound value is set when the user is done editing instead of with each keystroke because some interim values are invalid in the viewmodel (for example, while setting 'Age' to '26', a value of '2' is invalid so I'd like to wait to set the value until both digits are there). Something similar to setting UpdateSourceTrigger in xaml. I looked at several examples here:
MvvmCross UITextField custom binding is similar, as is MvvmCross: change update source trigger property of binding on MonoDroid (but for Android). I've also watch N=28 custom binding and looked at the source for MvxUITextFieldTextTargetBinding.
I think I'm close, but my custom binding never gets created and the UITextFields in my app still FireValueChanged with every keystroke.
I created the following Custom Binding:
public class UITextFieldFocusChangedBinding : MvxTargetBinding
{
private bool _subscribed;
private UITextField _view;
public UITextFieldFocusChangedBinding(UITextField target) : base(target)
{
_view = target;
}
public override void SetValue(object value)
{
if (_view == null) return;
_view.Text = (string)value;
}
public override void SubscribeToEvents()
{
var view = _view;
if (view == null)
return;
view.Ended += TextFieldOnEnded;
}
private void TextFieldOnEnded(object sender, EventArgs eventArgs)
{
var view = _view;
if (view == null)
return;
if (!view.IsFirstResponder)
FireValueChanged(view.Text);
_subscribed = true;
}
public override Type TargetType
{
get { return typeof(string); }
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.TwoWay; }
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
var view = _view;
if (view != null && _subscribed)
{
view.Ended -= TextFieldOnEnded;
_subscribed = false;
}
}
base.Dispose(isDisposing);
}
}
My setup.cs contains the following:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterPropertyInfoBindingFactory(typeof(Bindings.UITextFieldFocusChangedBinding),typeof(UITextField), "Text");
}
and in my MvxViewController I have:
var set = this.CreateBindingSet<LifeNeedsView, LifeNeedsViewModel>();
set.Bind(_txtFinMedExpenses).To(vm => vm.FinalMedicalExpenses);
set.Apply();
The bindings work (values are passing correctly) but with every keystroke. Any suggestions on what I might be missing?

GCM xamarin: Register

I am using xamarin android 4.10.1 and GooglePlayServices Rev. 12 from xamarin component store. It is mvvmcross application, so I have Core and Android projects. I need push notifications support in my app. So I start with GooglePlayServices component. I write this code:
var gcm = GoogleCloudMessaging.GetInstance(this);
var key = gcm.Register(new[] { "senderId" });
and it doesn't work, I fount that I need to run it on async thread, one solution I found here: http://forums.xamarin.com/discussion/8420/error-calling-googlecloudmessaging-register
ThreadPool.QueueUserWorkItem(o =>
{
var gcm = GoogleCloudMessaging.GetInstance(this);
var key = gcm.Register(new[] { Settings.GmcSenderId });
});
This code works, my service handle registration message, but I need this registration key in my mvvmcross ViewModel. So I start to register with Task approach:
var task = Task.Factory.Startnew(() =>
{
var gcm = GoogleCloudMessaging.GetInstance(this);
return gcm.Register(new[] { Settings.GmcSenderId });
});
var key = task.Result; // wait for result
// key is needed to execute code here
// ViewModel.Key = key;
But every time I receive SERVICE_NOT_AVAILABLE Exception, also I have try to sync with ManualResetEvent object, but still have exceptions.
Maybe some one know solution, how to bring registration Id to ViewModel class from View (activity). Or maybe you have some example with mvvmcross and receiving registration Id in view model...
My code with Task:
public string Register(string senderId)
{
var task = Task.Factory.StartNew(() =>
{
var context = Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity;
var gcm = GoogleCloudMessaging.GetInstance(context);
return gcm.Register(senderId);
});
return task.Result; // exception here!
}
Detailed exception:
InnerException {Java.IO.IOException: Exception of type 'Java.IO.IOException' was thrown.
at Android.Runtime.JNIEnv.CallObjectMethod (IntPtr jobject, IntPtr jmethod, Android.Runtime.JValue[] parms) [0x00064] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.10.1-branch/d23a19bf/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:194
at Android.Gms.Gcm.GoogleCloudMessaging.Register (System.String[] p0) [0x00000] in <filename unknown>:0
at Fiocx.Android.Code.NotificationService+<>c__DisplayClass1.<Register>b__0 () [0x00013] in d:\ASUS\Work\Programming\.NET\Fiocx.CloudApp\mobile_src\Fiocx.Mobile\Fiocx.Android\Code\NotificationService.cs:33
at System.Threading.Tasks.TaskActionInvoker+FuncInvoke`1[System.String].Invoke (System.Threading.Tasks.Task owner, System.Object state, System.Threading.Tasks.Task context) [0x00000] in <filename unknown>:0
at System.Threading.Tasks.Task.InnerInvoke () [0x00000] in <filename unknown>:0
at System.Threading.Tasks.Task.ThreadStart () [0x00000] in <filename unknown>:0
--- End of managed exception stack trace ---
java.io.IOException: SERVICE_NOT_AVAILABLE
at com.google.android.gms.gcm.GoogleCloudMessaging.register(Unknown Source)
at dalvik.system.NativeStart.run(Native Method)
} Java.IO.IOException
Similar problem with a solution: GCM SERVICE_NOT_AVAILABLE on Android 2.2
Btw have you tried using PushSharp?
https://github.com/Redth/PushSharp

Resources