Displaying a ListView item from Page1 on Page2 – Part 8 and Final – Writing a Xamarin.Forms app

Just joining the series? Here’s a recap:

Welcome to the grand finale!

Whenever I try to go down the MVVM path, I always get stuck at “How do I get PageOneViewModel and/or Page1.xaml to know about the list of Fabrics?” For example, suppose I want my Page1.xaml to show the Name of the current Fabric and its corresponding seconds. How does Page2 get the Fabric data from Page1? Fortunately, this final blog post answers this question!

Recall how my #1 biggest issue was thinking the ObservableCollection should be the “source of truth” – meaning the app pulls its fabric data requirements from the ObservableCollection (which is wrong and don’t do this). In the past, I’d achieve this by making the ObservableCollection a global variable and other things I’d brute force or SO cut and paste.

But now we have this data service. Since none of my viewmodels are instantiating the data service (my #2 biggest issue blocking me from understanding MVVM), it’s fair game for my other viewmodels to use the data service as well.

To follow along, view this diff from the previous blog post.

I have a question!!!!

  • I’m not sure why I have to specify public Fabric CurrentFabric { get => currentFabric; set => SetProperty(ref currentFabric, value); } I figured the BaseViewModel took care of all things INotifyPropertyChanged related. I also thought Mode=TwoWay would work. My only guess is this is the first time in my app thus far that the code has changed the property and not the user input, so extra stuff is needed that BaseViewModel can’t do.

Getting a Fabric to display in PageTwo

First, let’s add a Fabric property to PageTwoViewModel to databind. We’ll talk about where we get this fabric from in a bit.

public Fabric CurrentFabric { get; private set; }

inside the PageTwoViewModel constructor, let’s grab a Fabric from the data service. Just like last 2 blog posts, let’s write our “object model” first, and then implement it later.

CurrentFabric = FabricsDataService.GetFabric(0);

Next, let’s update the Page2.xaml

    <ContentPage.Content>
        <StackLayout Padding="20" HorizontalOptions="Center">
            <Label Text="{Binding CurrentFabric.Name}" FontSize="20" />
            <Label Text="{Binding CurrentFabric.Seconds}" FontSize="20" />
        </StackLayout>
    </ContentPage.Content>

And in the FabricsDataService, add a GetFabric method

        public static Fabric GetFabric(int id) {
            return Application.Current.Properties[id.ToString()] as Fabric;
        }

And let’s run it.

And what the…. we crash on launch??

Exception throws on launch

Looking at the callstack, it’s when we try to get the first fabric. There is no first fabric on first launch…

Turns out I was wrong in a prior post. For a tabbed control, both tab pages are instantiated at launch and are *not* re-instantiated each time you tab to them.

This means we need a way to handle first launch. In Page2.xaml.cs code-behind, we’ll add a

        protected override void OnAppearing()
        {
            base.OnAppearing();

            pageTwoViewModel.InitializeForFirstRun();
        }

and in the PageTwoViewModel, let’s only try to get a fabric if the count is > 0.

        internal void InitializeForFirstRun()
        {
            if (FabricsDataService.GetAllFabrics().Count > 0)
            {
                CurrentFabric = FabricsDataService.GetFabric(0);
            }
        }

Now when you run, after you add a fabric, you’ll see it appear in Page2!!!!

Let’s go one step further.

Let’s add a command to go to the next fabric (provided you’ve added two).

You’ve seen how to add a command in previous posts, so here’s the delegate in PageTwoViewModel.cs

        public void NextFabric() {

            CurrentFabric = FabricsDataService.GetFabric(1);

        }

Like I said, make sure you have at least 2 fabrics in this list! This is just to demo calling out to the FabricsDataService!!!

Now I thought the BaseViewModel would handle all updates made to CurrentFabric if we specified TwoWay binding, but it doesn’t and I’m still not sure why. You still need to add the following to PageTwoViewModel

        private Fabric currentFabric;
        public Fabric CurrentFabric { get => currentFabric; set => SetProperty(ref currentFabric, value); }

Now the CurrentFabric changes will be picked up!

That’s it for this series!

At least for now. My goal was to explain how one page in a tabbed control could get access to another page’s data-bounded items in a ListView, which we now know is the wrong question. The more accurate question is how to use a “DataService” to get access to a ListView from a different tabbed page.

Where I’d like to go next:

  • How might I add unit testing to my app? If you have a link or an example, please let me know! I should be able to test my services and my ViewModels, right?
  • How might dependency inject help me? Is this a requirement for unit testing or do I have enough to add unit tests?

 

One thought on “Displaying a ListView item from Page1 on Page2 – Part 8 and Final – Writing a Xamarin.Forms app

  1. hello,

    I just read your blog post because I was thinking on what is the best way to sync collections of data between the viewmodel and the data service and by using a Messenger, I don’t know if it’s the best idea, I was thinking a way to go was using the data service to somehow let the viewmodel know something changed in the collection and it should reload the list, but this is where I get stuck since ideally would be to load just the new item not reload the whole list.

    Another issue I see is having the Fabric model implement INotifyPropertyChanged, usually I would make a separate viewmodel that would wrap that model and use an ObservableCollection of that FabricViewModel in the PageOneViewModel.

    Anyway if you are looking for a good tutorial on unit testing I would suggest this course https://www.pluralsight.com/courses/nunit-dotnet-testing-introduction. I would advice against making the service as a static since it will mess with your unit tests and you will need to be careful and reset it’s state between tests. That is where dependency injection would come in handy, adding an interface to the service and injecting it in the needed viewmodels will give you a way to control the data in the unit tests.

    Keep up the good work, and if you find a good answer to the issue I stated above I would be happy to talk about it

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s