RealmObject PropertyChanged events not firing

Hello,

I am looking for help, have searched around everywhere but not seen a similar issue and I’m completely stuck on this one!

I am converting a Xamarin/.NET app of mine that previously used Sqlite and a web sync service to use MongoDB Realm. I am completely unable to get RealmObjects to fire PropertyChanged notifications. I have tried everything I can possibly think of including:

  • Forking the GitHub .NET samples and running them, works fine

  • Creating a new sample app with RealmObjects and data binding, works fine

  • Copying the exact pattern from this sample app into my current app, for some reason doesn’t work!!

  • Replace the RealmObject with a normal object implementing INotifyPropertyChanged, then the changes fire so no issue with XAML coding

  • Use the exact sync configuration in my sample app, works fine

This one has really got me stumped. I am at the point where I open straight to a new TestPage.xaml that looks like this:

	<ContentPage.BindingContext>
		<viewModels:TestPageViewModel />
	</ContentPage.BindingContext>

	<StackLayou>

		<Entry Text="{Binding TestObject.Name}"/>

		<Label Text="{Binding TestObject.Name}"/>

	</StackLayout>

With a viewmodel like this:

	public class TestPageViewModel:AppBaseViewModel
	{
		private readonly Realm realm;
		private Transaction transaction;

		private TestObject testObject;
		public TestObject TestObject
		{
			get => testObject;
			set => SetProperty(ref testObject, value);
		}

		public TestPageViewModel()
		{
			realm = Realm.GetInstance();

			var transaction = realm.BeginWrite();

			testObject = new TestObject();
			realm.Add(testObject);
		}
	}

and TestObject.cs like this:

	public class TestObject:RealmObject
	{
		public string Name { get; set; }
	}

So as you can see a very basic implementation and one that works if I use it exactly the same in my test sample app. Within my current app, if I subscribe to property changed events on the TestObject at the end of the viewmodel constructor, they are never fired. When I type in the entry the label is never updated.

So I am stuck!!! I am using the v10.0.0.2-beta.2 from Nuget and latest version of xamarin forms. Has been tested on iOS and Android devices with the same results. If anybody has any similar experience that could help, comments, suggestions, pointing out something that I’ve done completely wrong, I would be grateful for any help!! I could file a bug report but I have no idea what is even going wrong with this one.

Many thanks!!!
Will

Based on the code you posted, my guess is that this is caused by the fact you’re opening a write transaction in the ViewModel constructor. PropertyChanged events for managed objects are fired when the transaction is committed.

Thanks nirinchev. That means if I have for example a page with a List of Items, and then go to another view to add/edit an Item, how do I need to implement that to get PropertyChanged events to fire on the Item page? They are important for things like error checking on Entry boxes, and checking if a Command can fire. The two methods I have seen are like this (old) example: nirinchev/RealmObservableDemo: A very simple app that demonstrates data binding to Realm objects (github.com) which I have forked and upgraded to the latest Realm beta, and which then fires PropertyChanged events on the edit page, and realm-dotnet/examples/QuickJournal at master · realm/realm-dotnet (github.com) from your own samples, which is the same method I have been using and I guess will not fire PropertyChanged as the edit is commited upon Save().

The first example seems to work with data binding, property changed etc. but I have not been able to replicate it in my app as I always get the exception “Cannot modify managed objects outside of a write transaction.” when I pass the item from the list page to the edit page. I am using a custom navigation service as I am using Xamarin.Forms Shell and to pass anything other than a string on navigation I need my own service.

Thanks, I hope you can help.

Will

Let me try to address these one by one:

  1. Regarding PropertyChanged not firing while in transaction - that is indeed a bit unfortunate when you have multiple screens spanning the same transaction. Unfortunately, I don’t see an easy way out of this since it’s tied to the notifications mechanisms in the core database. As a workaround, you can probably manually raise propertychanged for the root model when you return to the parent page. Something like

    private Transaction transaction;
    
    // This is what the UI binds to
    public MyModel Model { get; private set; }
    
    // This is executed when the VM is first created
    public void OnCreate()
    {
        this.transaction = realm.BeginWrite();
        this.Model = realm.Add(new MyModel());
    }
    
    // This is executed when the user navigates to the page backed by this VM
    // Either the first time when the page is loaded or when a child page is dissmissed
    public void OnNavigatedTo()
    {
        this.RaisePropertyChanged(nameof(Model));
    }
    

    That way, every time your page comes into view, the VM will force a redraw, which will cause it to pick up any changes that happened in the child page.

  2. Alternatively, if you don’t need a Save/Cancel button, you can let the UI drive the updates - that way every change is going to get persisted as soon as the user makes it and PropertyChanged notifications will be raised immediately.

  3. Regarding the “Cannot modify managed objects outside of a write transaction.” error - this is thrown when you try to set a property on an object while the Realm is not in a write transaction. This is not allowed when doing it in code - you’ll need to wrap the property setting logic in realm.Write(() => { ... }). When doing it from the UI via a two-way databinding in Xamarin.Forms, Realm will automatically do that for you.

Hope that clarifies things, but if you have follow up questions, don’t hesitate to ask!

  • Nick
1 Like

Hi Nick, many many thanks for taking the time to respond and to help me out.

This is all pretty much as I thought but it just isn’t working for me!! :exploding_head: By methods 2 & 3 I think you are referring to something like in this example RealmObservableDemo/ContactsViewModel.cs at master · nirinchev/RealmObservableDemo (github.com) (which I’ve just noticed is written by yourself :slightly_smiling_face:). That would be the ideal way for me as like you say it doesn’t need to be done in a transaction and should get all the nice property changed stuff. But this method is causing the exception for me mentioned above.

Seems like I need to find out what is causing it…

Thanks again!

Will

If you have a simple project that exhibits the behavior, I’d be happy to take a look and try to track down what’s causing the issue. It is possible that there was a change in newer versions of Xamarin.Forms that broke the automatic behavior.

1 Like

Thank you so much @nirinchev. At the moment I am unable to get sync working and am trying to resolve that first, once that is sorted I will try to resolve the issue myself otherwise I can look at putting something together.

Many thank

Will

Hi @nirinchev. I managed to get sync up and running. I will set up my app structure to use the method you demonstrated above in Item 2, I have an example of this working nicely for me now, I just need to find a way of cancelling the changes if the user navigates back (otherwise for example an empty item could be created every time if the navigate forward and back).

I seem to have found the reason I was getting the ‘write transaction’ error every time I navigated to the detail page. I have been using compiled bindings (as described here Xamarin.Forms Compiled Bindings - Xamarin | Microsoft Docs). When I remove the x:DataType from the page then I no longer get the error. Is this something that would be considered a bug as it is recommended practice for Xamarin Forms.

Thanks!!

Will

Oh… yes, I can see the issue with compiled bindings. The way the .NET SDK handles two-way data bindings is by implementing a specific interface that the data binding engine looks for when resolving the bindings. We take advantage of the fact that this is likely to only ever be used by the binding engine to supply it with a fake setter for the persisted properties. When the binding engine invokes the setter (e.g. in response to changes from the UI), we check existing transaction, and if none exists, we wrap the set value call in an implicit transaction.

With compiled bindings, the binding engine no longer looks up properties using reflection, so it invokes the setter directly, thus skipping all of our custom logic. This is quite unfortunate and we’ll need to figure out a workaround - right now I don’t have any suggestions besides disabling compiled bindings, sorry about that :pensive:

2 Likes

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.