DataTemplate with TemplateSelector - bind to parent command

MJoehlMJoehl USMember ✭✭

Hi all

I have a ListViewwith a TemplateSelectorand two different DataTemplates (ViewCells). Behind the ListViewis a ViewModel for the ConentPagewith an ObservableCollectionwhich contains some item-models. The Binding to this Item-Model in my templates work like a charm.

What I want: My ContentPage-ViewModel has some commands. The DataTemplate should use this command. How can I bind to this commands?

I know, that I can make the commands static and reference with x:Static to it. Or I can add the Command to my Item-Model. But both are no Option for my solution.

Thanks in advance

Best Answer

Answers

  • jiangzhiyijiangzhiyi USMember

    thanks for @MichaelJhl,you are great!

  • RianDutraRianDutra USMember ✭✭

    @MJoehl how can I do it with XAML?

    I tried the code below but it didn't work:

    <Button Text="Test"
          Command="{Binding Path=ParentBindingContext.OpenCommand, Source={x:Reference MainPage}}"
          CommandParameter="{Binding .}" />
    
  • LippelLippel DEMember ✭✭

    It does work, thanks alot!

    But I found one downside: You cant use CachingStrategy="RecycleElement" on the listview if you use this custom view cell :(

    Any thoughts on that?

  • StinkyTowelStinkyTowel USMember ✭✭

    @MichaelJhl, did you inherit from BaseTemplate for your datatemplates? When I do, I get Error CS0260: Missing partial modifier on declaration of type 'SomeDataTemplate'; another partial declaration of this type exists.

  • StinkyTowelStinkyTowel USMember ✭✭

    I was able to get this going and it works very well (code behind and xaml), thanks @MichaelJhl for sharing!

  • Liger_JeromeLiger_Jerome FRUniversity ✭✭

    Hi !

    Thanks for this tip, in theory I'm agree with it... but impossible to make it works on my side :-(

    First, in your override "DataTemplate OnSelectTemplate", you can't create a new DataTemplate for each row... because on Android you will reach the "23 template count limit"... and I don't use RecycleElement as @Lippel suggested

    So this is my code

    'public class CheckItem_DatatemplateSelector : DataTemplateSelector
    {
    DataTemplate Acceptedtemplate { get; set; }
    DataTemplate Refusedtemplate { get; set; }
    DataTemplate Uncheckedtemplate { get; set; }

        protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        {
            if (((CheckablePacketItem)item).IsChecked)
            {
                if (((CheckablePacketItem)item).IsAccepted)
                {
                    if (Acceptedtemplate == null)
                    {
                        Acceptedtemplate = new DataTemplate(typeof(CkeckItem_accepted));
                    }
                    Acceptedtemplate.SetValue(BaseItem.ParentBindingContextProperty, container.BindingContext);
                    return Acceptedtemplate;
                }
                else
                {
                    if (Refusedtemplate == null)
                    {
                        Refusedtemplate = new DataTemplate(typeof(CheckItem_refused));
                    }
                    Refusedtemplate.SetValue(BaseItem.ParentBindingContextProperty, container.BindingContext);
                    return Refusedtemplate;
                }
            }
            else
            {
                if (Uncheckedtemplate == null)
                {
                    Uncheckedtemplate = new DataTemplate(typeof(CheckItem_unchecked));
                }
                Uncheckedtemplate.SetValue(BaseItem.ParentBindingContextProperty, container.BindingContext);
                return Uncheckedtemplate;
            }
        }'
    

    The binding seems to work.

    But in my "CkeckItem_accepted" (or in the two other templates too), I try to bind a button with my "ParentBindingContext.m_CallExpCommand" and I don't know why but the command is never executed...

    ''

    Does anybody that has made this tips working, can post a complete sample ? (or help if you see errors...)

    Thanks in advance

  • KlimovGlebKlimovGleb RUMember
    edited October 2018

    @RianDutra Insted of ParentBindingContext u should use BindingContext.

  • UnreachableCodeUnreachableCode USMember ✭✭✭

    Is RecycleElementAndDataTemplate a suitable fix to the issue @Lippel and @Liger_Jerome mention?

    "The RecycleElementAndDataTemplate caching strategy builds on the RecycleElement caching strategy by additionally ensuring that when a ListView uses a DataTemplateSelector to select a DataTemplate, DataTemplates are cached by the type of item in the list. Therefore, DataTemplates are selected once per item type, instead of once per item instance."

  • LippelLippel DEMember ✭✭
    edited March 28

    @UnreachableCode
    Quite some time passed since I added my comment.
    I believe RecycleElementAndDataTemplate didnt exist at that time. You should give it a try, it seems very promising.

  • Liger_JeromeLiger_Jerome FRUniversity ✭✭

    Hum... yes there was some time passed ...

    But I remember that I finally got this working (and this is in production since months)

    Opening the source-code (my memory is failing), I can see that :

    • My Listview CachingStrategy is set to "RecycleElement"
    • My Listview ItemTemplate is set to my DataTemplateSelector
    • All my differents DataTemplates (used in my datatemplateSelector) inherits from a "BaseViewCell" where I have created a Bindable Property (to keep the "parentBindingContext of the cell/myPageViewModel" for triggering a Command (of my PageViewModel) on an item button click(with a property of the item as parameter of the command)

    If you want some sample code, MP-me

  • UnreachableCodeUnreachableCode USMember ✭✭✭

    Hm, I'm getting a pretty big performance hit when set to RecycleElementAndDataTemplate. I'm not sure I can use this. Will I have problems if I leave it set to RetainElement?

  • AndreSilva.6387AndreSilva.6387 USMember

    Hey Folks,
    Thank you very much for your update here
    I was stuck by too days to get call to command in from a partial class to public class TheClassViewModel : BindableObject

    So with your bread sticks here I could made it work.

    So, here is the code:


    public partial class TheClassCellOption : ViewCell
    {

        public static BindableProperty ParentBindingContextProperty = BindableProperty.Create(nameof(ParentBindingContext), typeof(object), typeof(BotCellOption), null);
    
        public TheClassCellOption()
        {
    
            InitializeComponent();
    
        }
    
    
        public object ParentBindingContext
        {
            get { return GetValue(ParentBindingContextProperty); }
            set { SetValue(ParentBindingContextProperty, value); }
        }
    
        void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
            string selectedItem = e.SelectedItem as string;
    
            Console.WriteLine(selectedItem);
    
    
            ((TheClassViewModel)ParentBindingContext).MsgSend = selectedItem;
        //
        // here is the SendCommand that I like to call in the TheClassViewModel
        // 
            ((TheClassViewModel)ParentBindingContext).SendCommand.Execute(null);
    
        }
    

    }


    Here is the selection class

    public class TheClassDataTemplateSelector : DataTemplateSelector
    {
    private readonly DataTemplate TheClassCellOption;
    private readonly DataTemplate OtherClassCellOption;

        public TheClassDataTemplateSelector()
        {
            this.OtherClassCellOptionTemplate = new DataTemplate(typeof(OtherClassCellOption));
            this.TheClassCellOptionTemplate = new DataTemplate(typeof(TheClassCellOption));
        }
    
        protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        {
    
       //
       // other code that you need before selecting template ...
     //  I just do a check if message is incoming ... them select it
      ///
    
            if (message.IsIncoming)
            { 
                    this.TheClassCellOptionTemplate.SetValue(TheClassCellOption.ParentBindingContextProperty, container.BindingContext);
                    return this.botCellOptionTemplate;
            }
        else  {
         this.OtherClassCellOptionTemplate.SetValue(OtherClassCellOption.ParentBindingContextProperty, container.BindingContext);
                    return this.botCellOptionTemplate;
       }
    
        }
    }
    

    the view model class

    public class TheClassViewModel : BindableObject
    {
    

    .
    .
    .

        public ICommand SendCommand => new Command(SendMessage);
    

    .
    .
    .

    }

Sign In or Register to comment.