MJ Hughes
MJ Hughes

Reputation: 83

Showing non-list updated value from secondary navigation page on primary page using Xamarin.Forms, binding, and MVVM

This is my first StackOverflow question! I'm trying to get data bound to a ViewModel to appear on one page, allow it to be changed on another page, then appear in its changed form on the first page.

I have a MainPage displaying a label bound to the property ContactName in the ViewModel ContactViewModel. A button on MainPage pushes the DataEntryPage. On the DataEntryPage I have an entry field also bound to the ContactName. A save button pops the page and returns to MainPage.

I want to enter a new value on DataEntryPage, press save, and have the new value display in the label on the MainPage.

If I initialize ContactName, the value appears both on the MainPage and in the entry field of the DataEntryPage. But when I enter a new value and press save, the old value is displayed on MainPage.

I can make the new value display if I add a static instantiation of the ContactViewModel, explicitly set it to the new value in the DataEntryPage save, then force a refresh by overriding the MainPage's OnAppearing and setting the label to that value. With an ObservableCollection and ListView it's super easy, but I can't make it happen with a single property. It seems like I'm missing some basic ability of MVVC.

The stripped down code is--

MainPage.xaml:
<Label Text="{Binding ContactName}">
    <Label.BindingContext>
        <local:ContactViewModel />
    </Label.BindingContext>
</Label>

(a Button which triggers code below) W MainPage.xaml.cs: (Button clicked code)

await Navigation.PushAsync(new DataEntryPage());

DataEntryPage.xaml

<ContentPage.BindingContext>
    <local:ContactViewModel />
</ContentPage.BindingContext>

(then in a StackLayout)

<Entry Text={Binding ContactName} />

(Button for save)

DataEntryPage.xaml.cs (Button clicked code)

await Navigation.PopAsync();

And finally the ContactViewModel.cs

    public class ContactViewModel : INotifyPropertyChanged
    {

        string contactName;
        public event PropertyChangedEventHandler PropertyChanged;

        public ContactViewModel()
        {                 
            contactName = "OriginalName";
        }

        public static ContactViewModel cvm = new ContactViewModel();     

        public string ContactName
        {
            set {
                if (contactName!=value)
                {
                    contactName = value;
                    if (PropertyChanged!=null )
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("ContactName"));
                    }
                }
            }
            get
            {
                return contactName;
            }
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

Is there a simple solution to this problem? I would prefer to use vanilla Xamarin.forms/C# unless it's absolutely impossible to do without. Thank you for your help!

Upvotes: 0

Views: 83

Answers (1)

zafar
zafar

Reputation: 2064

When you declare the following in XAML

<Control.BindingContext> <local:ContactViewModel/> <Control.BindingContext>

a new instance is created. So the DataEntryPage is creating its own instance of ContactViewModel. So the instance that you are updating on the DataEntryPage is different from the instance that you are using to bind the Text property of Label on the MainPage. Using same instance for the pages should resolve your issue

Upvotes: 0

Related Questions