简体   繁体   中英

Xamarin Forms FindByName always returns null

I have a ContentPage that is bound to a viewmodel. Still, at one point, I need to access a Picker inside a StackLayout which in turn is in a Syncfusion ListViewItem. The XAML is pretty straightforward:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sf="clr-namespace:Syncfusion.ListView.XForms;assembly=Syncfusion.SfListView.XForms"
             mc:Ignorable="d"
             x:Class="PerformanceTM.Views.NewCircuitView">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="Welcome to Xamarin.Forms!"
                VerticalOptions="CenterAndExpand" 
                HorizontalOptions="CenterAndExpand" />
            <sf:SfListView x:Name="ExerciseList" ItemsSource="{Binding Exercises}" DragStartMode="OnHold">
                <sf:SfListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout Orientation="Horizontal">
                                <Picker Title="Kategorie" x:Name="CategoryPicker" ItemsSource="{Binding Source={x:Reference ExerciseList}, Path=BindingContext.ExerciseCategories}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding Category}" SelectedIndexChanged="CategoryChanged"/>
                                <Picker Title="Übung" x:Name="ExerciseNamePicker" ItemDisplayBinding="{Binding Name}" SelectedIndexChanged="ExerciseSelected"/>
                                <Button Text="..." Clicked="ConfigureSetsClicked"/>
                                <Button Text="(-)" />
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </sf:SfListView.ItemTemplate>
            </sf:SfListView>
            <StackLayout Orientation="Horizontal">
            <Button Text="(+) Übung" Command="{Binding AddExerciseCommand}"/>
            <Button Text="Ok" Command="{Binding ApplyChangesCommand}"/>
            </StackLayout>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

As you can see, both Pickers in the ViewCell have x:Name assigned to them. When I select a value in the ExerciseCategoryPicker, I load and assign new values to to ExerciseNamePicker in Code behind, like so:

    private async void CategoryChanged(object sender, EventArgs e)
    {
        var picker = sender as Picker;
        var stackLayout = picker.Parent as StackLayout;
        ListViewItem listViewItem = stackLayout.Parent as ListViewItem;

        var ex = picker.BindingContext as Exercise;
        if (ex is null)
            return;

        ex.Category = picker.SelectedItem as ExerciseCategory;

        var apiService = new ApiServices();
        var exsForCategory = await apiService.GetExercisesForCategory(ex.Category.Name);

        Picker exnp = stackLayout.FindByName<Picker>("ExerciseNamePicker");
        if (exnp is null)
            exnp = stackLayout.Children.OfType<Picker>().Where(x => x.Title == "Übung").FirstOrDefault();

        exnp.ItemsSource = exsForCategory;
        if (exsForCategory.Count > 0)
            exnp.SelectedItem = exsForCategory.FirstOrDefault();

        var bc = this.BindingContext as NewCircuitViewModel;
        bc.ExercisePairing.Descriptor = bc.ExercisePairing.UpdateDescriptor();
    }

private void ExerciseSelected(object sender, EventArgs e)
    {
        try
        {
            var picker = (sender as Picker);
            var stackLayout = picker.Parent as StackLayout;
            var vc = picker.Parent.Parent as Syncfusion.ListView.XForms.ListViewItem;
            var ex = vc.BindingContext as Exercise;
            var se = picker.ItemsSource[picker.SelectedIndex] as Exercise;

            var exnp = stackLayout.FindByName("CategoryPicker") as Picker;
            if (exnp is null)
                exnp = stackLayout.Children.OfType<Picker>().Where(x => x.Title == "Kategorie").FirstOrDefault();

            var ec = exnp.SelectedItem as ExerciseCategory;

            ex.Category = ec;
            ex.Name = se.Name;
            ex.Id = se.Id;
            ex.Description = se.Description;
            ex.VideoUrl = se.VideoUrl;

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

Now what happens is that FindByName() returns null in each situation. Oddly enough, when I debug and inspect the Children of StackLayout just before calling FindByName, it does contain children with the appropriate IDs (ie, the x:Name). When I access them via get, I get the GUID (this somehow confuses me, as I thought there should only be a GUID in the first place, but well).

I have found a workaround by just selecting the element by Title, but this is a rather strange behaviour, especially considering that this has worked in the past. Only change I made since then was the integration of the SyncFusion ListView. Could that be an issue? Has anyone experienced this and/or can provide more insight?

PS: I have gone through all the "usual" fixes such as deleting the.v, bin and obj folders...

You can get the children element of SfListView.ItemTemplate using behavior for the parent element of the ItemTemplate. Please refer the following code snippet for getting the named elements inside DataTemplate,

Xaml : Define Behavior for StackLayout

<sf:SfListView x:Name="ExerciseList" ItemsSource="{Binding Exercises}" DragStartMode="OnHold">
<sf:SfListView.ItemTemplate>
    <DataTemplate>
        <ViewCell>
            <StackLayout Orientation="Horizontal">
                <StackLayout.Behaviors>
                    <local:Behavior/>
                </StackLayout.Behaviors>

                <Picker Title="Kategorie" x:Name="CategoryPicker" ItemsSource="{Binding Source={x:Reference ExerciseList}, Path=BindingContext.ExerciseCategories}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding Category}"/>
                <Picker Title="Übung" x:Name="ExerciseNamePicker" ItemDisplayBinding="{Binding Name}"/>
                <Button Text="..." Clicked="ConfigureSetsClicked"/>
                <Button Text="(-)" />
            </StackLayout>
        </ViewCell>
    </DataTemplate>
</sf:SfListView.ItemTemplate>

Behavior : Get element using FindByElement. Trigger SelectedIndexChanged event for Picker.

public class Behavior : Behavior<StackLayout>
{
    Picker CategoryPicker;
    Picker ExerciseNamePicker;

    protected override void OnAttachedTo(StackLayout bindable)
    {
        CategoryPicker = bindable.FindByName<Picker>("CategoryPicker");
        ExerciseNamePicker = bindable.FindByName<Picker>("ExerciseNamePicker");

        CategoryPicker.SelectedIndexChanged += CategoryPicker_SelectedIndexChanged;
        ExerciseNamePicker.SelectedIndexChanged += ExerciseNamePicker_SelectedIndexChanged;

        base.OnAttachedTo(bindable);
    }

    private void ExerciseNamePicker_SelectedIndexChanged(object sender, EventArgs e)
    {
        //Your logic here.
    }

    private void CategoryPicker_SelectedIndexChanged(object sender, EventArgs e)
    {
        //Your logic here.
    }
}

You can also refer our online document regarding the same from the following link, Document

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM