Search code examples
xamarin.formsmvvmdata-bindingbinding-context

Xamarin Forms: How can i correctly bind data from two view models to a single view?


This is the short code for testing purpose. The problem is that the UI is not displaying the Text from the Label which is binded with ViewModelB. In debugging when I hover the mouse in xaml over the Text from the Label I see the right binding data is there, but the UI simply won't display. With ViewModelA there are no problems.

In XAML:

<StackLayout>
  <StackLayout>
        <StackLayout.BindingContext>
            <testbinding:ViewModelA/>
        </StackLayout.BindingContext>
        <Button Command ="{Binding Get}"/>
   </StackLayout>
    <StackLayout>
        <StackLayout.BindingContext>
            <testbinding:ViewModelB/>
        </StackLayout.BindingContext>
        <Label Text="{Binding Metadata}"/>
    </StackLayout>

ViewModelA: where BaseViewModel is a INotifyPropertyChanged interface

public ViewModelA:BaseViewModel
{   
 public ViewModelA()
 {
        Get = new Command(SendText);
        vmB = new ViewModelB();
 }
 ViewModelB vmB;
 public ICommand Get { get; }
 private void SendText()
 {
     string data = "someText";
     vmB.GetMetadata(data);
 }
}

ViewModelB is like this:

class ViewModelB:BaseViewModel
{
    private string _metadata = string.Empty;
    public string Metadata
    {
        get { return _metadata; }
        set
        {
            _metadata = value;
            OnPropertyChanged();
        }
    }
    GetMetadata()
    {
    Metadata = "Some text";
    }
}

In ViewModelA there are more properties which I need and in ViewModelB is just one property which gets data from a function. I could make just one ViewModel from both of them which works fine, but I'm trying to keep them smaller and organized. I already tried so many scenarios and is getting really frustrating. Thanks for helping.


Solution

  • In the second StackLayout in your xaml file you're not binding it's BindingContext property to the ViewModelB instance from ViewModelA but instead you are creating a new one.

    Here's a working solution for you:

        public class ViewModelA : BaseViewModel
        {
            public ViewModelB ViewModelB { get; }
            public ICommand GetMetadataCommand { get; }
    
            public ViewModelA()
            {
                ViewModelB = new ViewModelB();
                GetMetadataCommand = new Command((_) => GetMetadata());
            }
    
            private void GetMetadata()
            {
                string data = "someText";
                ViewModelB.GetMetadata(data);
            }
        }
    
        public class ViewModelB : BaseViewModel
        {
            private string _metadata;
            public string Metadata
            {
                get { return _metadata; }
                set 
                {
                    _metadata = value;
                    OnPropertyChanged();
                }
            }
    
            public void GetMetadata(string data)
            {
                Metadata = data;
            }
        }
    

    XAMl:

      <StackLayout>
            <StackLayout x:Name="StackLayout1">
                <StackLayout.BindingContext>
                    <local:ViewModelA />
                </StackLayout.BindingContext>
                <Button Command ="{Binding GetMetadataCommand}"/>
            </StackLayout>
            <StackLayout BindingContext="{Binding Source={x:Reference StackLayout1}, Path=BindingContext.ViewModelB}">
                <Label Text="{Binding Metadata}" />
            </StackLayout>
        </StackLayout>