Xamarin Forms FindByName 总是 returns null

Xamarin Forms FindByName always returns null

我有一个绑定到视图模型的 ContentPage。不过,有一次,我需要访问 StackLayout 中的 Picker,而 StackLayout 又位于 Syncfusion ListViewItem 中。 XAML 非常简单:

<?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>

如您所见,ViewCell 中的两个 Pickers 都分配了 x:Name。当我 select ExerciseCategoryPicker 中的一个值时,我加载新值并将其分配给代码后面的 ExerciseNamePicker,如下所示:

    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);
        }
    }

现在发生的是 FindByName() returns 在每种情况下都为空。奇怪的是,当我在调用 FindByName 之前调试和检查 StackLayout 的 Children 时,它确实包含具有适当 ID 的 children(即 x:Name)。当我通过 get 访问它们时,我得到了 GUID(这让我有些困惑,因为我认为首先应该只有一个 GUID,但是很好)。

我找到了一个解决方法,只需 select 按标题编辑元素,但这是一种相当奇怪的行为,尤其是考虑到这在过去曾奏效。从那时起我所做的唯一改变就是集成了 SyncFusion ListView。这可能是个问题吗?有没有人经历过这个and/or可以提供更多见解?

PS:我已经完成了所有 "usual" 修复,例如删除 .v、bin 和 obj 文件夹...

您可以使用 ItemTemplate 的父元素的行为来获取 SfListView.ItemTemplate 的子元素。请参考以下代码片段以获取 DataTemplate 中的命名元素,

Xaml:定义 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>

行为:使用 FindByElement 获取元素。触发选择器的 SelectedIndexChanged 事件。

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.
    }
}

您也可以从以下 link 中参考我们关于相同内容的在线文档, Document