Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin.Forms binding not updating after initial value

I'm binding the title of my Xamarin.Forms.ContentPage to a property BuggyTitle in my view model (VM). The VM derives from MvxViewModel. Here's the simplified version:

BuggyPage.xaml:

<?xml version="1.0" encoding="UTF-8"?>
<local:ContentPage Title="{Binding BuggyTitle}"
            xmlns="http://xamarin.com/schemas/2014/forms" 
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
            x:Class="MyProject.BuggyPage"
            xmlns:local="clr-namespace:Xamarin.Forms;assembly=MyProject">
<ContentPage.Content NavigationPage.HasNavigationBar="false">
        <Grid>
            <ScrollView>
                <!--and so on-->
</ContentPage.Content>
</local:ContentPage>

BuggyViewModel.cs:

namespace MyProject
{
    [ImplementPropertyChanged]
    public class BuggyViewModel : MvxViewModel
    {
      private Random _random;

      public string BuggyTitle {get; set;}

      public BuggyViewModel()
      {
          _random = new Random();
      }

      public override void Start()
        {
            base.Start();
            BuggyTitle = "" + _random.Next(1000);
            RaisePropertyChanged("BuggyTitle"); // this seems to make no difference
        }
    }
}

There's not much going on in the code behind other than a call to InitializeComponent() in the constructor.

The page is mapped to the VM generically in my project (not actually 'my' project, it's existing design), and it boils down to these (again, simplified) lines of code:

public static Page CreatePage(MvxViewModelRequest request)
{
    var viewModelName = request.ViewModelType.Name;
    var pageName = viewModelName.Replace ("ViewModel", "Page");
    var pageType = (typeof (MvxPagePresentationHelpers)).GetTypeInfo ().Assembly.CreatableTypes().FirstOrDefault(t => t.Name == pageName);
    var viewModelLoader = Mvx.Resolve<IMvxViewModelLoader>();
    var viewModel = viewModelLoader.LoadViewModel(request, null);
    var page = Activator.CreateInstance(pageType) as Page;
    page.BindingContext = viewModel;

   return page;
}

The problem:

When BuggyPage loads, I initially get the correct value for the title. Whenever it is displayed after that, even though I can see in the debugger that BuggyTitle is getting updated correctly, the change does not appear in the page.

Question:

Why don't updates to BuggyTitle get reflected in the page?

Edit 1:

To further describe the weirdness, I added a Label to my ContentPage, with x:Name="BuggyLabel" and Text="{Binding BuggyLabelText}". In my code-behind, I added this:

var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
    BuggyLabel.Text = binding_context.BuggyLabelText;
}

I set a breakpoint at BuggyLabel.Text =. It gets hit every time the page loads, and BuggyLabel.Text already seems to have the correct value (i.e, whatever binding_context.BuggyLabelText is set to). However, the actual page displayed only ever shows what the text in this label is initially set to.

And yes, have clean/built about a million times.

Edit 2 (further weirdness):

I put this in the code-behind so that it runs during page load:

var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
    Device.BeginInvokeOnMainThread(() =>
    {
        binding_context.RefreshTitleCommand.Execute(null);
    });
}

This again changes values in the debugger, but these changes don't get reflected in the displayed page.

I then added a button to the page and bound it to RefreshTitleCommand, and wham! the page updates its display.

Unfortunately I can't use this. Not only is it incredibly hackish, I can't have the user pressing buttons to have the page display what it's meant to on load.

I wonder if there's some caching going on with MvvmCross or Xamarin.

like image 586
Ash Avatar asked Nov 05 '17 03:11

Ash


People also ask

How do I set binding context in xamarin forms?

In code, two steps are required: The BindingContext property of the target object must be set to the source object, and the SetBinding method (often used in conjunction with the Binding class) must be called on the target object to bind a property of that object to a property of the source object.

How does binding work in xamarin forms?

Data binding is the technique of linking properties of two objects so that changes in one property are automatically reflected in the other property. Data binding is an integral part of the Model-View-ViewModel (MVVM) application architecture.

What is the default binding mode in xamarin forms?

The default binding mode is OneWayToSource . When a data binding is set on the SelectedItem property to reference a source property in a ViewModel, then that source property is set from the ListView selection. However, in some circumstances, you might also want the ListView to be initialized from the ViewModel.

Why is data binding important in xamarin Forms application?

Xamarin. Forms includes Data Binding, a way of keeping a user interface synchronized with its underlying data without having to write code for every aspect of managing those interactions. Data binding makes it possible to create rich user interface experiences for data-driven applications without writing a lot of code.


1 Answers

Answer

You need to add RaisePropertyChanged in BuggyTitle property declaration.

ViewModel

namespace MyProject
{
    [ImplementPropertyChanged]
    public class BuggyViewModel : MvxViewModel
    {
        private Random _random;

        string  _BuggyTitle { get; set; }

        public string BuggyTitle
        {
            get { return _BuggyTitle; }
            set { _BuggyTitle = value; RaisePropertyChanged(() => BuggyTitle); }
        }

        public BuggyViewModel()
        {
            _random = new Random();
        }

        public override void Start()
        {
            base.Start();
            BuggyTitle = "" + _random.Next(1000);
        }
    }
}

-----New Update------

Code behind code

var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
    Device.BeginInvokeOnMainThread(() =>
    {
        BuggyLabel.Text = binding_context.BuggyLabelText;
    });
}
like image 92
Pavan V Parekh Avatar answered Oct 12 '22 13:10

Pavan V Parekh