Custom control within DataTemplate of ListView can't be bound to CommandParameter

GrexyGrexy USMember ✭✭

Hi, I tried to find a solution for this issue but it seems i am overseeing something because others got it to work very the same way i tried. So I am reaching out for some help :wink:

I have a ListView thats using a TemplateSelector for getting the right DataTemplate for the given items. This works, here the code of the ListView(DBContentListview) and the DataTemplate(TextDashBoardContent) that is called when the error occurs:

        <ContentView x:Class="MyNS.Views.EventView"
                     x:Name="ThisContentView">
            <ContentView.Resources>
                <DataTemplate x:Key="TextDashBoardContent">
                    <ViewCell x:Name="TheViewCell" ChildAdded="Cell_OnAppearing">
                        <Grid x:Name="commentGrid"  Margin="3" Padding="3"  BackgroundColor="{StaticResource MySeaShell}" RowSpacing="1" ColumnSpacing="0">
                            <Grid.RowDefinitions>                        </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>                        </Grid.ColumnDefinitions>
                            <customControls:CommentEditorLayout Grid.Column="0" Grid.ColumnSpan="7" Grid.Row="2" BindingContext="{Binding Source={x:Reference ThisContentView}, Path=BindingContext}"
                                                                CommandContext="{Binding Source={x:Reference DBContentListview}, Path=BindingContext}"
                                                                SendDataCommand="{Binding SendCommentCommand}" SendDataCommandParameter="{Binding Source={x:Reference TheViewCell}}" ></customControls:CommentEditorLayout>
                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </ContentView.Resources>
            <ContentView.Content>
                <ListView x:Name="DBContentListview" SelectionMode="None" 
                              HasUnevenRows="True" Header="{Binding .}"
                              ItemTemplate="{StaticResource DbContentTemplateSelector}" ItemsSource="{Binding Events.DbContent}"  >
                    <ListView.HeaderTemplate>                </ListView.HeaderTemplate>
                </ListView>
            </ContentView.Content>
        </ContentView>

Here you can find my custom control called in the code before (CommentEditorLayout.xaml):

        <Grid x:Class="MyNS.CustomControls.CommentEditorLayout">
            <Grid.Resources>
                <ResourceDictionary>
                    <Style x:Key="commentEditorLayoutImageStyle" TargetType="Image">
                        <Style.Setters>
                            <Setter Property="HeightRequest" Value="{x:Static definitions:LayoutDefinitions.CommentGridHeight}"></Setter>
                            <Setter Property="BackgroundColor" Value="Transparent"></Setter>
                            <Setter Property="VerticalOptions" Value="EndAndExpand"></Setter>
                            <Setter Property="HorizontalOptions" Value="CenterAndExpand"></Setter>
                        </Style.Setters>
                    </Style>        
                </ResourceDictionary>
            </Grid.Resources>
            <Grid.ColumnDefinitions>            </Grid.ColumnDefinitions>
            <Image x:Name="attachmentTapImg" Style="{StaticResource commentEditorLayoutImageStyle}" Grid.Column="0" Source="camera.png" ></Image>
            <customRenderer:DynamicEditor x:Name="commentEditor" BackgroundColor="LightGray" TextColor="Black" 
                                    HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" MaxLength="512" 
                                    AutoSize="TextChanges"  ></customRenderer:DynamicEditor>
            <Image x:Name="sendDataTapImg" Style="{StaticResource commentEditorLayoutImageStyle}" Grid.Column="6" Source="smartphonesendingdata.png" >
                <Image.GestureRecognizers >
                    <TapGestureRecognizer x:Name="sendDataCommand" Command="{Binding SendDataCommand}" CommandParameter="{Binding SendDataCommandParameter}" ></TapGestureRecognizer>
                </Image.GestureRecognizers>
            </Image>
        </Grid>

When i tap on sendDataTapImg it calls the right command, but the parameter always is null:

private async Task ExecuteSendCommentCommand(object viewCell )
{
    if (viewCell != null)//<= is ALWAYS null
}

I also tried the following code instead which results in an string.Empty as viewCell:
<TapGestureRecognizer x:Name="sendDataCommand" ></TapGestureRecognizer>
Almost forgotten, the CommentEditorLayout.xaml.cs:

    public partial class CommentEditorLayout : Grid
    {
        public static readonly BindableProperty SendDataTapImgProperty = BindableProperty.Create(nameof(sendDataTapImg),
            typeof(Xamarin.Forms.ImageSource),
            typeof(CommentEditorLayout),
            default(Xamarin.Forms.ImageSource),
            Xamarin.Forms.BindingMode.OneWay);
        public ImageSource SendDataTapImg
        {
            get
            {
                return (ImageSource)GetValue(SendDataTapImgProperty);
            }

            set
            {
                SetValue(SendDataTapImgProperty, value);
            }
        }

        public static readonly BindableProperty SendDataCommandProperty = BindableProperty.Create(nameof(sendDataCommand),
            typeof(ICommand),
            typeof(CommentEditorLayout),
            null);
        public ICommand SendDataCommand
        {
            get
            {
                return (ICommand)GetValue(SendDataCommandProperty);
            }

            set
            {
                SetValue(SendDataCommandProperty, value);
            }
        }

        public static readonly BindableProperty SendDataCommandParameterProperty = BindableProperty.Create(nameof(sendDataCommand),
            typeof(object),
            typeof(CommentEditorLayout),
            null);
        public object SendDataCommandParameter
        {
            get
            {
                return (object)GetValue(SendDataCommandParameterProperty);
            }

            set
            {
                SetValue(SendDataCommandParameterProperty, value);
            }
        }

        public static readonly BindableProperty CommandContextProperty = BindableProperty.Create(nameof(sendDataCommand),
            typeof(object),
            typeof(CommentEditorLayout),
            null);
        public object CommandContext
        {
            get
            {
                return (object)GetValue(CommandContextProperty);
            }

            set
            {
                SetValue(CommandContextProperty, value);
            }
        }

        public CommentEditorLayout ()
        {
            InitializeComponent ();
        }

        protected override void OnPropertyChanged(string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);

            else if (propertyName == SendDataTapImgProperty.PropertyName)
            {
                this.sendDataTapImg.Source = SendDataTapImg;
            }
            else if (propertyName == SendDataCommandProperty.PropertyName)
            {
                this.sendDataCommand.Command = SendDataCommand;
            }
            else if (propertyName == SendDataCommandParameterProperty.PropertyName)
            {
                this.sendDataCommand.CommandParameter = SendDataCommandParameter;
            }
            else if (propertyName == CommandContextProperty.PropertyName)
            {
                this.sendDataCommand.BindingContext = this.CommandContext;
                this.sendDataCommand.CommandParameter = this.SendDataCommandParameter;
            }
        }
    }

However, it works like a charm without wrapping this into a custom control like this:

<Image x:Name="ImgRef" Grid.Column="0" Grid.Row="2" Source="camera.png" 
VerticalOptions="EndAndExpand" HorizontalOptions="CenterAndExpand" 
HeightRequest="{x:Static definitions:LayoutDefinitions.CommentGridHeight}">
</Image>
<customRenderer:DynamicEditor x:Name="CommentEntry" Grid.Column="1" Grid.ColumnSpan="5" Grid.Row="2" 
                            FontSize="Medium" Margin="0" BackgroundColor="LightGray" TextColor="Black" 
                            HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" MaxLength="511" 
                            AutoSize="TextChanges"  >
</customRenderer:DynamicEditor>
<Image Grid.Column="6" Grid.Row="2" Source="smartphonesendingdata.png" 
VerticalOptions="EndAndExpand" HorizontalOptions="CenterAndExpand" 
HeightRequest="{x:Static definitions:LayoutDefinitions.CommentGridHeight}" BackgroundColor="Transparent">
                        <Image.GestureRecognizers >
                            <TapGestureRecognizer BindingContext="{Binding Source={x:Reference DBContentListview}, Path=BindingContext}" Command="{Binding SendCommentCommand}"   CommandParameter="{Binding Source={x:Reference TheViewCell}}"          ></TapGestureRecognizer>
                        </Image.GestureRecognizers>
</Image>

So I guess i am missing something very basic on the custom control...But i do not see any mistake as the command execute call itselve is working fine in every case, only the commandparameter is wrong. I also tried to pass a simple string like this:

CommandParameter="blablub"
what also ends in a object viewCell == null, so i do not think it is a failure within the x:Reference syntax or similar. Every help is highly appreciated, thank you very much!!!

Best regards

Grexy

Answers

  • GrexyGrexy USMember ✭✭
    edited September 2018

    Hum... it took me about 8 hours to come to a point where i decided to ask here for help and immediatly after i posted this i found my error:

            public static readonly BindableProperty SendDataCommandParameterProperty = BindableProperty.Create(**_nameof(sendDataCommand)_**,
                typeof(object),
                typeof(CommentEditorLayout),
                null);
            public object SendDataCommandParameter
            {
                get
                {
                    return (object)GetValue(SendDataCommandParameterProperty);
                }
    
                set
                {
                    SetValue(SendDataCommandParameterProperty, value);
                }
            }
    

    It came out that i have a basic misunderstanding of this Create-Method. I copied this part out of a very simple example i found without completly understanding it. Source of the error is the nameof(sendDataCommand), which i thought is the name of the control, in this case the TapGestureRecognizer, but it only seems to be the name of the property. I have to clear why this works at some properties these upcoming days and will post the fix for completion of this question. However any further suggestions and help for understanding are appreciated! Thank you :smiley:

Sign In or Register to comment.