Search code examples
xamlxamarinmvvmdata-binding

Xamarin MVVM Reference Content View and Pass Parameters Too


Working in xamarin forms. How do I reference a content view from a content page but also include passing information like below?

await Navigation.PushAsync(new SecondContentPage(new NextViewModel(FirstViewModel.id)));

I've tried xaml binding context but don't know where to go from there and it keeps giving me errors about a parameterless constructor.

xmlns:viewmodel="clr-namespace:Project.ViewModels"

       <StackLayout>
            <local:SecondContentView>
                <local:SecondContentView.BindingContext>
                    <viewmodel:NextViewModel></viewmodel:NextViewModel>
                </local:SecondContentView.BindingContext>
            </local:SecondContentView>
        </StackLayout>

I need the id passed along basically so that the list can run on the content view. thanks all

Updated - I created some new example code. I created one page to nest a second page with a Listview. Works great until I try to pass a parameter with x:Arguments or in a ViewModel constructor from first page to second page.

First page

**<StackLayout>

        <StackLayout>
            <Label Text="First Page Content"></Label>
        </StackLayout>

        <StackLayout>
            <local:SecondContentView>
                <local:SecondContentView.BindingContext>
                    <viewmodel:SecondViewModel>
                        <x:Arguments>102</x:Arguments>
                    </viewmodel:SecondViewModel>
                </local:SecondContentView.BindingContext>
            </local:SecondContentView>
        </StackLayout>

    </StackLayout>**

second page

        <ContentView.Content>

    <StackLayout>

        <Label Text="Second Page"></Label>

        <ListView x:Name="FirstListView">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Padding="10">
                            <Label Text="{Binding pType}"></Label>
                            <Label Text="{Binding fDepartment}"></Label>
                            <Label Text="{Binding Description}"></Label>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>

        </ListView>

    </StackLayout>

</ContentView.Content>

second page's code behind

SecondViewModel ViewModel;

    public SecondContentView(SecondViewModel viewmodel)
    {
        InitializeComponent();

        BindingContext = ViewModel = viewmodel;

        FirstListView.ItemsSource = ViewModel.TypeList;
    }

second page's view model

**public List<TypeModel> TypeList;

    public SecondViewModel(int parameter)
    {
        var p = parameter;

        TypeList = new List<TypeModel>()
        {
            new TypeModel { pType = 1, Title = "First Type", Description = "First Description", Version = "9.9.9", fDepartment = 101 , Comments = "None"},
            new TypeModel { pType = 2, Title = "Second Type", Description = "Second Description", Version = "9.9.9", fDepartment = 101 , Comments = "None"},
            new TypeModel { pType = 3, Title = "Third Type", Description = "Third Description", Version = "9.9.9", fDepartment = 102 , Comments = "None"},
            new TypeModel { pType = 4, Title = "Fourth Type", Description = "Fourth Description", Version = "9.9.9", fDepartment = 102 , Comments = "None"}
        };

    }**

Solution

  • I was able to find a solution with another mocked example. The key was x:Argument and an override method.

    Firstly, if you want to pass the simple type values we need to explicitly point out its type. Try this format:

    <StackLayout>
        <local:SecondContentView>
            <local:SecondContentView.BindingContext>
                <local:SecondViewModel>
                    <x:Arguments>
                        <x:Double>102</x:Double>
                    </x:Arguments>
                </local:SecondViewModel>
            </local:SecondContentView.BindingContext>
        </local:SecondContentView>
    </StackLayout>
    

    Change its constructor like:

    public SecondViewModel(double parameter)
    {
        var p = parameter;
    
        TypeList = new List<TypeModel>()
        {
            new TypeModel { pType = 1, Title = "First Type", Description = "First Description", Version = "9.9.9", fDepartment = 101 , Comments = "None"},
            new TypeModel { pType = 2, Title = "Second Type", Description = "Second Description", Version = "9.9.9", fDepartment = 101 , Comments = "None"},
            new TypeModel { pType = 3, Title = "Third Type", Description = "Third Description", Version = "9.9.9", fDepartment = 102 , Comments = "None"},
            new TypeModel { pType = 4, Title = "Fourth Type", Description = "Fourth Description", Version = "9.9.9", fDepartment = 102 , Comments = "None"}
        };
    }
    

    Secondly, you are not passing parameters to the second view's constructor. Instead, you consumed the defualt null parameter constructor and set its binding context directly. So the public SecondContentView(SecondViewModel viewmodel) won't be triggered. We could consume the binding context directly in your second view like:

    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();
    
        ViewModel = BindingContext as SecondViewModel;
        FirstListView.ItemsSource = ViewModel.TypeList;
    }