6
votes

Basically, I have a ListView with a DataTemplate Selector that uses a particular DataTemplate basing on the ListView item.

Now, in the DataTemplate - I have a button w/ a command that should be binded on the ViewModel of the Parent (or the ListView) itself.

Note that I only want to bind the Command property of the button as the Text and other properties would need to be binded to the current binding Context of the button.

The BindingContext of the DataTemplate is the ListView Item bindingContext (Message Model in this case) but I want to be able to bind just a specific button in the data template to the viewmodel of the parent listView.

How do I do that?

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage 
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
        x:Class="MobileMobile.Views.MobileMessageListView"
        Title="Message List"
        NavigationPage.BarBackgroundColor="#FF9100"
        NavigationPage.BarTextColor="White"
        xmlns:ViewSwitchers="clr-namespace:MobileMobile.ViewSwitchers;assembly=MobileMobile"
        xmlns:ViewCells="clr-namespace:MobileMobile.ViewCells;assembly=MobileMobile"
        xmlns:Extensions="clr-namespace:MobileMobile.Extensions;assembly=MobileMobile"
        >

    <ContentPage.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="botMessageDataTemplate">
                <ViewCell>
                    <Button Text="Hello!" Command="{Binding TestCommand, Source=???}" CommandParameter="Hello"/>
                </ViewCell>
            </DataTemplate>

            <ViewSwitchers:MobileMessageTemplateSwitcher x:Key="MobileMessageTemplateSwitcher" BotMessageDataTemplate="{StaticResource botMessageDataTemplate}" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentPage.Content>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

            <StackLayout Grid.Row="0" Orientation="Vertical" x:Name="MainViewStack">
                <ListView
                        CachingStrategy="RecycleElement"
                        BackgroundColor="Transparent"
                        ItemTemplate="{StaticResource MobileMessageTemplateSwitcher}"
                        IsPullToRefreshEnabled="true"
                        RefreshCommand="{Binding RefreshCommand}"
                        ItemsSource="{Binding Messages}"
                        HasUnevenRows="true"
                        IsRefreshing="{Binding IsLoading, Mode=OneWay}"
                        SeparatorVisibility="None">
                    <ListView.Footer/>
                </ListView>
            </StackLayout>
            <StackLayout Grid.Row="1" Orientation="Horizontal" HeightRequest="50">
                <Entry Text="{Binding CurrentMessageText}" Placeholder="{Binding MessageTextPlaceHolder}" HorizontalOptions="FillAndExpand"/>
                <Button Text="SEND" Command="{Binding SendMessageCommand}"/>
            </StackLayout>
        </Grid>
    </ContentPage.Content>
</ContentPage>
2

2 Answers

24
votes

Try this:

<Button
    Text="Hello!"
    Command="{Binding Path=BindingContext. TestCommand, Source={x:Reference Name=MessageListPage}}"
    CommandParameter="Hello" />

You would have to give the Page an x:Name property with the value MessageListPage, so it messes with your pure MVVM a bit, but since Xamarin XAML doesn't support relative binding (as far as I know..) this is the way to go.

1
votes

Instead of declaring the datatemplate as a static resource in a content dictionary, put the datatemplate in the itemtemplate of the list itself.

I'm not 100% sure of what's going on under the hood, but it would appear that the datatemplate is not associated with the binding context of the page when declared as a static resource. This means that when you leave and return to the page or change the contents of the listview then you may have issues rendering the update to the items.

This will enable you to bind your button command to the parent but also stop any problems if you leave and revisit the page

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    x:Class="MobileMobile.Views.MobileMessageListView"
    Title="Message List"
    NavigationPage.BarBackgroundColor="#FF9100"
    NavigationPage.BarTextColor="White"
    xmlns:ViewSwitchers="clr-namespace:MobileMobile.ViewSwitchers;assembly=MobileMobile"
    xmlns:ViewCells="clr-namespace:MobileMobile.ViewCells;assembly=MobileMobile"
    xmlns:Extensions="clr-namespace:MobileMobile.Extensions;assembly=MobileMobile"
    x:Name="DeclareYourCodeBehindHereOrDeclareAViewModel"
    >

<ContentPage.Resources>
    <ResourceDictionary>
        <ViewSwitchers:MobileMessageTemplateSwitcher x:Key="MobileMessageTemplateSwitcher" BotMessageDataTemplate="{StaticResource botMessageDataTemplate}" />
    </ResourceDictionary>
</ContentPage.Resources>

<ContentPage.Content>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <StackLayout Grid.Row="0" Orientation="Vertical" x:Name="MainViewStack">
            <ListView
                    CachingStrategy="RecycleElement"
                    BackgroundColor="Transparent"
                    IsPullToRefreshEnabled="true"
                    RefreshCommand="{Binding RefreshCommand}"
                    ItemsSource="{Binding Messages}"
                    HasUnevenRows="true"
                    IsRefreshing="{Binding IsLoading, Mode=OneWay}"
                    SeparatorVisibility="None">
                <ListView.ItemTemplate>
<DataTemplate x:Key="botMessageDataTemplate">
            <ViewCell>
                <Button Text="Hello!" Command="{Binding Source={x:Reference DeclareYourCodeBehindHereOrDeclareAViewModel}, Path=BindingContext.CommandName}" CommandParameter="Hello"/>
            </ViewCell>
        </DataTemplate>
                </ListView.ItemTemplate>
                <ListView.Footer/>
            </ListView>
        </StackLayout>
        <StackLayout Grid.Row="1" Orientation="Horizontal" HeightRequest="50">
            <Entry Text="{Binding CurrentMessageText}" Placeholder="{Binding MessageTextPlaceHolder}" HorizontalOptions="FillAndExpand"/>
            <Button Text="SEND" Command="{Binding SendMessageCommand}"/>
        </StackLayout>
    </Grid>
</ContentPage.Content>