Databinding to a command inside a DataTemplate

Hi there, I'm just having a play with Xamarin Forms and databinding and am having a problem working out the syntax. Say I've got a view model associated with some XAML. Inside the view model I've got a class Foo

    class Foo
    {
            public string Name {get; set;}
            public string Details {get; set;}
    }

I've also got a command

    public ICommand DoSomethingCommand { protected set; get; }

and an observable collection of Foos

    private ObservableCollection<Foo> _foos = new ObservableCollection<Foo>();
    public ObservableCollection<Foo> Foos
            {
                get { return _foos; }
            }

In my XAML, I've got a ListView. The binding to each field of my Foo works fine. However, I want to bind a command to each row, so that I can do something when the user taps on the row. The binding doesn't work though, presumably because the command isn't inside the Foo object? I've done some checking online and people suggest using RelativeSource to get to the command object but this doesn't work (I get a runtime xaml parse error). Anyone got any solution to this?

<ListView x:Name="FooView" ItemsSource="{Binding Foos}">
    <ListView.ItemTemplate>
      <DataTemplate>

                    <TextCell Text="{Binding Name}" Detail="{Binding Details}" Command="{Binding DoSomethingCommand}" />

      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>

Best Answer

Answers

  • RyanHatfieldOldRyanHatfieldOld USMember, Insider ✭✭✭
    edited July 2014

    WHY CAN"T YOU DELETE POSTS!!!

  • JotharryDevelopmentJotharryDevelopment USMember ✭✭
    edited July 2014

    Hi @RyanHatfield, the properties aren't the problem. My listview populated fine with the correct values. It's the command binding that's the issue. I've just checked and if I move the command inside the Foo class everything works as expected. I want the command inside the viewmodel though, so there must be an issue with the binding syntax. As I've mentioned, various people suggest using RelativeSource.

    For example

    http://stackoverflow.com/questions/18245936/binding-to-viewmodel-from-inside-a-datatemplate

    I thought I could do something like this, but the syntax doesn't work.

       Command="{Binding DoSomethingCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ViewModel}}"
    
  • RyanHatfieldOldRyanHatfieldOld USMember, Insider ✭✭✭

    I'm bad at XAML stuff... It makes no sense to me. But if I understand it right, it makes sense that you can only bind to things in the binding context?

  • JotharryDevelopmentJotharryDevelopment USMember ✭✭
    edited July 2014

    Hi @RyanHatfield‌, I think that's the general idea yes. I'm guessing that binding ItemsSource to 'Foos' means that the scope of further bindings are restricted to the Foo class. RelativeSource seems to be a solution for general WPF but it doesn't work for me with the Xamarin parser.

  • RyanWischkaemperRyanWischkaemper USMember, University

    Bump, running into this exact same problem

  • Same problem here...

    I have a VM with a collection and an int property, this colection is binded to a listview and the int property would represent the item font size, but I cant access the parent property on item. =(

  • Any updates on this issue?

  • Same here, according MVVM pattern, the commands must be in viewmodel, but with this limitation, we need to put in model.

  • @Alexandre.2342‌ I've found Xamarin.Forms.Behaviors: http://codeworks.it/blog/?p=216

    Haven't tried it yet, but it should work.

  • Hello @AlexeiVinidiktov‌ looks promising! I tried to download and run the project, no success, filled and issue on git https://github.com/corradocavalli/Xamarin.Forms.Behaviors/issues

  • CorradoCavalliCorradoCavalli ITInsider, University ✭✭
    edited October 2014

    Hi All,
    Xamarin.Forms.Behaviors has a sort of Relative Binding in DataTemplates, blogged about it here: http://codeworks.it/blog/?p=216, some limitation applies, like on indicated on Git issues, when using elements that do not propagate BindingContext.

  • RobertWallstrmRobertWallstrm SEMember
    edited December 2014

    Why not add a reference to your ViewModel on your Foo class?
    Foo {
    Public string Name;
    Public string Details;
    Public MyViewModel Model;
    }

    Doing so you could reference the command from XAML like so:
    Command="{Binding Model.DoSomethingCommand}".

    If you (like me) think the back reference to the Model in the Foo class is ugly you could transform all your Foos into anonymous objects with the model added just prior to binding them to the view. Like so:

    var myFos = ..
    var anonymousFos = myFos.Select(x=> new {x.Name, x.Details, Model = this});

  • JeremyHerbisonJeremyHerbison CAMember ✭✭

    Kind of infuriating, actually. Makes data binding in a listview pretty difficult.

  • TomStandaert.0575TomStandaert.0575 BEMember ✭✭

    would be nice if it could be "fixed" in one of the future releases of xamarin.forms, having to work around this is rather stupid.

  • If you're using MvvmLight you can bind to Locator.ViewModel.Command

    http://mvvmlight.codeplex.com/discussions/80447

  • NMackayNMackay GBInsider, University mod
    edited May 2015

    @DerekBeattie.6675

    Nice tip, I'm using view 1st binding and ViewModelLocator pattern & MVVM Light with Forms, curious to see if that works.

    I'm using CorradoCavalli's library at the moment and it works very well for binding to commands in data templates.

    <ListView Grid.Row="1" ItemsSource="{Binding Customers}" VerticalOptions="Fill" x:Name="ListviewCustomer"> <ListView.ItemTemplate> <DataTemplate> <customRenderers:CustomImageCell Text="{Binding CustomerName}" Detail="{Binding CustId}"> <b:Interaction.Behaviors> <b:BehaviorCollection> <b:EventToCommand CommandNameContext="{b:RelativeContext Details}" EventName="Tapped" CommandName="SelectCommand" CommandParameter="{Binding .}" /> </b:BehaviorCollection> </b:Interaction.Behaviors> </customRenderers:CustomImageCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

  • TomSoderlingTomSoderling USUniversity ✭✭✭
    edited August 2015

    There was a fix in Xamarin.Forms v1.4.4 for x:Reference inside a DataTemplate, so this is now supported.
    See my post here: http://forums.xamarin.com/discussion/comment/144204/#Comment_144204

  • I can vouch for @TomSoderling 's solution, works like a charm

  • Hi, I was just about to post a new question regarding a very similar issue i'm having. The difference is that I want to re-use the data template in other pages and allow it to bind to an external command.

    BooksPage.xaml - it has a corresponding BooksPageModel as it's binding context:

    <local:BasePage 
            xmlns:local="clr-namespace:Example;assembly=Example"
            x:Name="BasePage">
        <ContentPage.Content>
    
          <ListView 
                CachingStrategy="RecycleElement"
                x:Name="listView" 
                ItemsSource="{Binding Books}">
            <ListView.ItemTemplate>
    
              <DataTemplate x:Name="dataTemplate">
    
            <!-- Do I need to pass anything else in here?  -->
                <local:CustomViewCellComponent BindingContext="{Binding .}">
    
              </DataTemplate>
    
            </ListView.ItemTemplate>
          </ListView>
    
        </ContentPage.Content>
    </local:BasePage>
    

    CustomViewCellComponent.xaml - I want to reuse this component in several xaml pages:

    <ViewCell>
        <StackLayout>
            <Label Text="{Binding Title}"  />
    
            <!-- How to get this command to pick up the parent list view binding context, or parent page? -->
            <!-- OnMoreInfoCommand is a simple Command on the BooksPageModel -->
            <Button Command="{Binding Source={x:Reference listView}, Path=BindingContext.OnMoreInfoCommand}"
                    CommandParameter="{Binding .}"
                    Text="More Info" TextColor="White" 
                    BackgroundColor="Green">
            </Button>
    
        </StackLayout>
    </ViewCell>
    
  • Roberto.6713Roberto.6713 USUniversity ✭✭

    @DonalFarrell.8833 said:
    Hi, I was just about to post a new question regarding a very similar issue i'm having. The difference is that I want to re-use the data template in other pages and allow it to bind to an external command.

    BooksPage.xaml - it has a corresponding BooksPageModel as it's binding context:

    <local:BasePage 
          xmlns:local="clr-namespace:Example;assembly=Example"
          x:Name="BasePage">
      <ContentPage.Content>
    
        <ListView 
              CachingStrategy="RecycleElement"
              x:Name="listView" 
              ItemsSource="{Binding Books}">
          <ListView.ItemTemplate>
    
            <DataTemplate x:Name="dataTemplate">
    
          <!-- Do I need to pass anything else in here?  -->
              <local:CustomViewCellComponent BindingContext="{Binding .}">
    
            </DataTemplate>
    
          </ListView.ItemTemplate>
        </ListView>
    
      </ContentPage.Content>
    </local:BasePage>
    

    CustomViewCellComponent.xaml - I want to reuse this component in several xaml pages:

    <ViewCell>
      <StackLayout>
          <Label Text="{Binding Title}"  />
    
          <!-- How to get this command to pick up the parent list view binding context, or parent page? -->
          <!-- OnMoreInfoCommand is a simple Command on the BooksPageModel -->
          <Button Command="{Binding Source={x:Reference listView}, Path=BindingContext.OnMoreInfoCommand}"
                  CommandParameter="{Binding .}"
                  Text="More Info" TextColor="White" 
                  BackgroundColor="Green">
          </Button>
    
      </StackLayout>
    </ViewCell>
    

    Hi, did you resolve this??

  • wend0rlinwend0rlin DEMember ✭✭
    edited March 2017

    @CraigDunn said:
    Xamarin.Forms Xaml is not the same as WPF. Currently there's no RelativeSource capability in our binding resolution.

    You kinda already figured that out - just wanted to confirm.

    Has anything changed since July 2014 concerning this topic, I assume there is still no 'RelativeSource' in XF?

  • dpedrinhadpedrinha DEMember ✭✭✭
    edited April 2017

    @jesulink2514 said:
    You can achieve this using a mix of bindings , in order not to loose our Item as bindingcontext , you can bind your item to your context via Reference binding.

    <ContenPage x:Name="MainPage">
    <ListView Grid.Row="1"
                  ItemsSource="{Binding Customers}"
                  VerticalOptions="Fill"
                  x:Name="ListviewCustomer">
          <ListView.ItemTemplate>
            <DataTemplate>
          <Label Text="{Binding Property}"/>
              <Button Command="{Binding BindingContext.ItemCommand, Source={x:Reference MainPage}}" 
                             CommandParameter="{Binding .}">Click me</Button>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
    </ContentPage>
    

    Thant works like a charm! Including for other properties like Text.

    Thanks!

    @wend0rlin read the post comments. More than one solution is there for some time. Like this one.

  • wend0rlinwend0rlin DEMember ✭✭

    @dpedrinha said:

    @jesulink2514 said:
    You can achieve this using a mix of bindings , in order not to loose our Item as bindingcontext , you can bind your item to your context via Reference binding.

    <ContenPage x:Name="MainPage">
    <ListView Grid.Row="1"
                  ItemsSource="{Binding Customers}"
                  VerticalOptions="Fill"
                  x:Name="ListviewCustomer">
          <ListView.ItemTemplate>
            <DataTemplate>
          <Label Text="{Binding Property}"/>
              <Button Command="{Binding BindingContext.ItemCommand, Source={x:Reference MainPage}}" 
                             CommandParameter="{Binding .}">Click me</Button>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
    </ContentPage>
    

    Thant works like a charm! Including for other properties like Text.

    Thanks!

    @wend0rlin read the post comments. More than one solution is there for some time. Like this one.

    oh thank's for the hint, but even after reading it again and again, this solution only works if you use the template inside the content page. But it's not working if you want to resue the template in different pages. That's not a replacement for 'relative source'.

  • krispennerkrispenner CAMember

    Same issue here, none of the above works for me, x:Reference works great when inside a DataTemplate that is defined within the page where the referenced named element is also defined; but move that DataTemplate to App.xaml as a named/keyed resource, and x:Reference does not work anymore. If anyone knows how to use x:Reference or an alternative from within a DataTemplate defined as a resource in App.xaml so it can be re-used across multiple pages, please share.

    @wend0rlin please let me know if/when you find a solution.

    Thanks!

  • 15mgm1515mgm15 USMember ✭✭✭✭

    @krispenner Did you found a solution for this?

  • krispennerkrispenner CAMember

    @15mgm15 sorry for the delay in getting back to you I was away from my code.
    I never found a proper solution to this, but I did figure out a workaround/hack that gets me past this issue.

    I created a public static class with public fields to hold the the parent page of the ListView on and then databind to this.
    You need to have a different field for any page that has a list view on it that may have another child/nested page appear that also has a listview on it, because they can't both share one field.

    I'm not sure how clear that was but here is the code to help get the gist of what I did to get around this:

      public static class StaticReferences
      {
        #region Fields
        **public static BasePage CurrentProjectListPage;**
        public static BasePage CurrentResourceListPage;
        public static BasePage CurrentScheduleList;
        #endregion
      }
    
      public partial class ProjectListPage : BasePage
      {
        #region Constructors
        public ProjectListPage()
          : base(new ProjectListViewModel())
        {
          **StaticReferences.CurrentProjectListPage = this;**
          InitializeComponent();
        }
        #endregion
    
        #region Methods
        protected override void OnParentSet()
        {
          base.OnParentSet();
          **if (Parent == null && ReferenceEquals(StaticReferences.CurrentProjectListPage, this))
            StaticReferences.CurrentProjectListPage = null;**
        }
        #endregion
      }
    
          <DataTemplate x:Key="ProjecListItemTemplate">
            <ViewCell>
              <ViewCell.ContextActions>
                <MenuItem Command="{Binding **BindingContext**.ViewItemCommand, **Source={x:Static v:StaticReferences.CurrentProjectListPage}**}"
                          CommandParameter="{Binding .}"
                          Text="View" />
              </ViewCell.ContextActions>
              <Grid Style="{StaticResource ListItemGridStyle}">
              </Grid>
            </ViewCell>
          </DataTemplate>
    

    Again, sorry for the delay, I hope that helps, if you find a better proper solution please share!

  • marinheiromarinheiro PTMember ✭✭

    @krispenner you saved my day! Thanks!

Sign In or Register to comment.