在Xamarin.Forms中,运行时访问DataTemplate内的视图

5
我希望知道在Xamarin.Forms的ListView中,如何获取DataTemplate中的视图的引用。 假设我有以下XAML代码:
<ListView x:Name="ProductList" ItemsSource="{Binding Products}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout BackgroundColor="#eee" x:Name="ProductStackLayout"
                                     Orientation="Vertical" Padding="5" Tapped="ListItemTapped">
                            <Label Text="{Binding Name}" 
                                   Style="{DynamicResource ProductPropertiesStyle}"/>
                            <Label Text="{Binding Notes}" IsVisible="{Binding HasNotes}" 
                                    Style="{DynamicResource NotesStyle}"
                                />
                            <Label Text="{Binding Date}" 
                                   Style="{DynamicResource DateStyle}"
                            />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

我希望能够在ListView的每一行中获取名为“ProductStackLayout”的StackLayout的引用。当页面出现时,我需要这样做来动态操作其内容(对于无法通过数据绑定实现的某些内容),因此我无法利用从DataTemplate本身的元素发起的事件处理程序传递的视图引用,例如ItemTapped或类似事件。
据我所知,在WPF或UWP中,可以借助VisualTreeHelper类实现这样的功能,但我不认为Xamarin.Forms中有这个类的等效物。

1
为什么不在代码后台创建ListView呢?如果你想要使用每个引用,那似乎是这样做的方式。 - Greggz
2个回答

5

是的,可以在运行时访问使用 DataTemplate 创建的视图。在 XAML 中为 DataTemplate 内部的视图挂钩 BindingContextChanged 事件。在回调中,可以使用发送器参数访问从 DataTemplate 创建的视图。您需要对发送器进行类型转换才能访问视图,因为发送器被装箱为对象类型。

否则,您可以选择 DataTemplate 选择器根据您的对象创建视图。


你好@Dharmendar,感谢你的回答。我认为这正是我所需要的。 我已经在其他情况下使用了DataTemplate选择器,但我完全忘记了它们现在可以派上用场。但是挂钩BindingContextChanged事件的解决方案甚至更简单,我已经测试了基于项目视图模型状态在运行时附加视图的方法,它完美地工作。我想做的事情有点复杂,因为我想用SkiaSharp绘制一些图形,我需要一些时间来测试它,但我不明白为什么它不能同样工作。 - Francesco Blue
找到了我长期寻找的解决方案 :) 谢谢兄弟。 - Muhammad Adeel Shoukat

3

你也可以这样进行类型转换:

    ITemplatedItemsView<Cell> templatedItemsView = listView as ITemplatedItemsView<Cell>;
    ViewCell firstCell = templatedItemsView.TemplatedItems[0] as ViewCell;
    StackLayout stackLayout = firstCell.View as StackLayout;

这将为您提供对视图的引用。


但是您可能希望根据绑定上下文的更改做出反应,否则您将不得不手动更改视图的内容。

我认为使用 BindingContextChanged 将导致您将内容呈现两次-首先更改会像正常情况一样导致呈现-然后再次呈现。因此,如果例如字符串发生更改,则标签将重新呈现-然后您获取 BindingContextChanged 中的值并执行您实际想要的呈现。

您可以创建一个 ListView 的子类来防止这种情况:

public class CustomListView : ListView
{
    protected override void SetupContent(Cell content, int index)
    {
        // render differently depending on content.BindingContext
        base.SetupContent(content, index);
    }
}

这两个代码片段都很有趣。我之前不知道 ITemplatedItemsView<T> 接口,也没有想过继承 ListView 并重写 SetupContent 方法。如果此时 BindingContext 可用,这绝对是最好的解决方案。我的应用程序已经在生产中运行了多个月,并使用了 BindingContextChanged 方法:它基本上运行良好,但如果我再次需要类似的东西,我肯定会考虑继承方法以及 DataTemplateSelector 方法。 - Francesco Blue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接