Bind SelectedItem in ListView to element in another page

tvaltval Member ✭✭

I am new to Xaml, and have been scouring the web for examples, but so far all I have found is binding viewmodels commands to elements on the same page. But what I would like to do is bind elements on one page to trigger an event on another page. What I have is a Main Page:

<TabbedPage.Children>
    <pages:MapPage"  >
        <pages:MapPage.BindingContext>
            <viewModels:MapViewModel/>
        </pages:MapPage.BindingContext>
    </pages:MapPage>

    <pages:LayersPage">
        <pages:LayersPage.BindingContext>
            <viewModels:LayersViewModel/>
        </pages:LayersPage.BindingContext>-->
    </pages:LayersPage>

</TabbedPage.Children>

The Map Page holds a custom SkiaSharp control that loads maps. The LayersPage holds a listview with possible layers to load:

<ContentPage.Content>
    <StackLayout>
        <ListView x:Name="listView" 
                  ItemsSource="{Binding Layers}" 
                  Header="{Binding Intro}"
                  Footer="{Binding Summary}"
                  ItemTapped="OnItemTapped">
            <ListView.HeaderTemplate >
                <DataTemplate>
                    <StackLayout Orientation="Horizontal" 
        Padding="10,5,5,10"
        BackgroundColor="Yellow">
                        <Label Text="~~"/>
                        <Label Text="{Binding .}"/>
                        <Label Text="~~"/>
                    </StackLayout>
                </DataTemplate>
            </ListView.HeaderTemplate>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding Name}" Detail="{Binding Description}"></TextCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage.Content>

So when a layer is selected on the listview, it should raise a command/event to the MapPage, and subsequently the Map element to load layer (string) selected. I am trying to use MVVM, and I can get the list view to raise an even on the "ItemTapped" event in the LayersPage, but I am now unsure how to send that string to the MapViewModel, or even the element that has a method called AddLayer(string layer).

So I have read lots of article about commands and binding, and I feel slightly confident on binding to elements within the page, but not to other pages. Any help or step in the right direction would be appreciated.

Tagged:

Answers

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    but so far all I have found is binding viewmodels commands to elements on the same page.

    Well that's bad - and wrong. Because you shouldn't be doing any logic on the page. Are you sure you mean 'page'? And not that the command is executing in the VM or in a different VM?

    But what I would like to do is bind elements on one page to trigger an event on another page.

    Again bad... And problematic. Things shouldn't be happening on a page or from page to page. Pages are just UI. They are reflections of what's happening in your data/logic classes (the view models) - but really Views aren't even required for your app to work.

    I am trying to use MVVM, and I can get the list view to raise an even on the "ItemTapped" event

    ItemTapped is a UI event. Not the raising of a command for your ViewModel.

    I am now unsure how to send that string to the MapViewModel

    Raise a Command and send the string as the CommandParameter. The command would be in the ViewModel, and handled in the ViewModel.

    So I have read lots of article about commands and binding, and I feel slightly confident on binding to elements within the page,

    Sounds like you read articles, then tried to jump straight into your final production app. Have you tried some simplier "hello world" level apps first just to learn the techniques before attempting to put them in a more complex solution?

    Give this a whirl... Drop out of your production app for a couple hours. Work this series from start to end. Build the sample app etc. Work it don't just read it. See if that helps clear up your understanding because its pretty clear that you're a little mixed up on the architecture and where you should be trying to put your logic - and separating logic from UI.
    http://redpillxamarin.com/2018/03/12/2018-101-vs2017-new-solution/

  • tvaltval Member ✭✭
    edited January 21

    Wow, you are really sensitive about your verbiage and not focusing on the problem. Yes I have built the dozen samples from Xamarin and other sources. Yes I am trying to bind an element on a page (not the page or the element itself, but a property or event of it) through MVVM meaning through the viewmodel, to another view model, to a view, which will then update another element on another page, totally independent of the UI. I apologize I didn't write everything to specification, but I don't need a lecture, I need help.

  • NMackayNMackay GBInsider, University mod

    @tval said:
    Wow, you are really sensitive about your verbiage and not focusing on the problem. Yes I have built the dozen samples from Xamarin and other sources. Yes I am trying to bind an element on a page (not the page or the element itself, but a property or event of it) through MVVM meaning through the viewmodel, to another view model, to a view, which will then update another element on another page, totally independent of the UI. I apologize I didn't write everything to specification, but I don't need a lecture, I need help.

    I read your post twice and it wasn't clear to me at all so I think that's a fair response.

    If you want to handle that scenario where the two views are decoupled maybe look at message center and send a message from viewmodel a (tab1) to viewmodel b (tab2), you certainly don't want to strongly couple the pages via events if possible

  • NMackayNMackay GBInsider, University mod
    edited January 21

    Another approach is to have a singleton map service interface that's injected into both viewmodels and if viewmodel A changes a binding then view b could react to the data change in it's bound viewmodel by looking at the data change and then triggering the map draw, I did something like this before and it worked well, I used a 3rd party behaviors pluggin

    <ContentView.Behaviors>
          <b:DataChangedBehavior Binding="{Binding MapPosition}" ComparisonCondition="NotEqual" Value="{x:Null}">
            <b:InvokeMethodAction TargetObject="{Binding Source={x:Reference MapPageRoot}}"
                                  MethodName="MapRefresh" />
          </b:DataChangedBehavior>
        </ContentView.Behaviors>
    
    

    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/messaging-center
    I'd try the message center route 1st though.

  • LandLuLandLu Member, Xamarin Team Xamurai

    @tval I've noticed that you embedded your MapPage and LayersPage in a tabbed page. Then you can handle the view model on that page:

    // Define two view models as properties
    public partial class MyTabbedPage : TabbedPage
    {
        public MapViewModel mapViewModel { set; get; }
        public LayersViewModel layersViewModel { set; get; }
    
        public MyTabbedPage ()
        {
            InitializeComponent();
    
            mapViewModel = new MapViewModel();
            MapPage.BindingContext = mapViewModel;
    
            // Pass your mapViewModel as a parameter to your layersViewModel
            layersViewModel = new LayersViewModel(mapViewModel);
            LayersPage.BindingContext = layersViewModel;
            // Point out these two pages on your XAML with `x:Name` syntax.
        }
    }
    

    At last your can get the same singleton of LayersViewModel in your MapViewModel, I give you a sample of MapViewModel's constructor for referring:

    public class LayersViewModel
    {
        MapViewModel MyMapViewModel;
        public LayersViewModel(MapViewModel mapViewModel)
        {
            // ...
            MyMapViewModel = mapViewModel;
        }
    
        // In your Item tapped command you could use this map view model to call its events.
        // I believe you have bound your OnItemTapped to your view model
    }
    

    Also Messaging Center is another choice:
    Register on the MapPage:

    MessagingCenter.Subscribe<object, object>(this, "LayerChanged", (sender, args) =>
    {
    
    });
    

    When your item tapped command triggers, fire the message like:
    MessagingCenter.Send<object, object>(this, "LayerChanged", SomeObjects);

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    @tval said:
    Wow, you are really sensitive about your verbiage and not focusing on the problem.

    If the verbiage is not write then the reader doesn't understand the actual problem the writer is experiencing.
    So I focused on the underlying problem of what you described: Bad design and lack of understanding of MVVM.

    If you used misleading verbiage... meaning to say "communication between view models" when you actually said "communication between pages" - don't put that off on the reader for not being telepathic. Its simply not possible to answer what you meant to say - the best anyone can do is answer what you actually said.

    I'm bowing out of the thread now.
    I wish you the best of luck in your project.

Sign In or Register to comment.