CollectionView模板调用转换器不正确



Xamarin.Forms 5.0.0.2012Visual Studio 2019 for Mac 8.10.16

我遇到了一个问题(在iOS和Android上,在模拟器和物理设备上),试图在XAML中调用一个转换器来描述我们应用程序中的新屏幕。这是标记:

<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:internal="clr-namespace:Views.Shared"
x:Class="Views.EditPage">
<ContentPage.Resources>
<internal:PictureNameToImageSourceConverter x:Key="PictureNameToImageSourceConverter" />
</ContentPage.Resources>

<CollectionView
x:Name="picturesView"
ItemsLayout="HorizontalList"
HeightRequest="90">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="False" Padding="5">
<Image
HeightRequest="80"
Source="{Binding Path=., Converter={StaticResource PictureNameToImageSourceConverter}}" />
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>

CollectionView.ItemsSource值在代码中设置为List<字符串>实例。列表中没有项目时,屏幕将正确显示。当它确实有项目时,应用程序会在屏幕出现时崩溃。如果我启动连接到VS调试器的应用程序,屏幕会在崩溃前冻结,我永远不会得到任何信息。

这个转换器经过了很好的测试,在应用程序的其他几个地方使用都没有问题。当我用<Label Text="{Binding Path=.}"/>替换图像时,文本项将按预期显示,因此看起来不像是绑定或Path语法错误。

在标记中是否有我没有看到或没有意识到的东西导致了这种情况?或者有人能建议我进一步调试吗?


更新

转换器第一行上的断点从未到达。

编辑以回应评论:

编辑页面代码隐藏:

public partial class EditPage : ContentPage
{
internal List<string> PictureNames;

protected override void OnAppearing( )
{
base.OnAppearing();
picturesView.ItemsSource = PictureNames;
}

PictureNames属性实际上是由不同的Page设置的,从EditPage:导航到该页面

private void saveSelection_Click(object sender, System.EventArgs args)
{
creator.PictureNames = new List<string>();
foreach (SelectableItem<Picture> item in pictureItems)
{
if (item.IsSelected)
{
creator.PictureNames.Add(item.Item.PictureName);
}
}
Navigation.PopAsync();
}

pictureItems是一个列表,充当该屏幕上的ListView.ItemsSource。在该屏幕上,可以选择或取消选择图片。

更新

在多次设置断点之后,我已经确定picturesView.ItemsSource = PictureNames;行是崩溃发生的地方。奇怪的是,只有当模板显示图像,而不是标签时,才会发生这种情况,因为转换器实际上从未被调用。

更新

添加延迟的技巧确实让我达到了断点。我发现比以往任何时候都更令人困惑的是:传递给我们转换器的Convert方法的参数是null。无论是从图片选择屏幕返回,还是我响应按钮而不是在OnAppearing中设置绑定列表,或者我只是在页面构造函数中正确设置它,都是这种情况。

此外,当在崩溃线上设置断点时,当模板中的显示元素是Label时,一切都如预期,但当显示元素是Image,调试器在尝试查看这些值时会冻结。问题显然是关于在这种精确的情况下调用转换器的事实。

我通过在模板中添加一个不同的转换器进行了测试:

<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="False" Padding="5" HeightRequest="{Binding Path=., Converter={StaticResource PictureNameToHeightConverter}}">
<Label Text="{Binding Path=.}" />
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>

完全相同的结果:传递给Convert方法的参数为null,即使相同模板实例中的相同绑定值显示在Label中。如果像以前一样在分配ItemsSource属性的行上设置断点,调试器将冻结。

更新

最后,我开始怀疑我在框架中触发了一些角落案例错误,我用CarouselView替换了CollectionView

<CarouselView
x:Name="picturesView"
HeightRequest="90">
<CarouselView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="False" Padding="5">
<Image
HeightRequest="80"
Source="{Binding Path=., Converter={StaticResource PictureNameToImageSourceConverter}}" />
</Frame>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>

我添加这个作为问题的更新,而不是作为答案,因为我实际上还不知道解释。

OnAppearing中更新页面详细信息时,我看到了一些奇怪的行为。

很难准确地指出什么时候会出现问题,但事实上你导航到了另一个页面,所以这是OnAppearing在";背面";来自另一页可能是一个因素。

试试这个:

protected override void OnAppearing( )
{
base.OnAppearing();
Device.BeginInvokeOnMainThread( async () => {
// Let the page appear on screen, before setting ItemsSource.
await System.Threading.Tasks.Task.Delay(200);
picturesView.ItemsSource = PictureNames;
});
}

这至少应该允许Xamarin到达转换器中的断点。

-------------

更新

我看不出你发布的代码有任何明显的缺陷。

您已经将它缩小到崩溃的行。
然而,转换器开始时的断点永远不会到达
正如你所说,这是一个令人费解的事实组合。

这里有一个测试要做:

  • 在崩溃的行上设置断点
  • 将列表中的值复制到文本编辑器中
  • 在当前页面上添加一个按钮,当按下该按钮时,将用相同的字符串填充列表,硬编码为文字,然后设置ItemSource
  • 重新开始,但这次按下按钮-这是有效还是崩溃

也就是说,消除了转到另一个页面、查询值和返回此页面的所有复杂性。

我打赌这会奏效的。然后你就有了调试的最佳情况:一个有效的案例与一个无效的案例。

之后,它的"分而治之"。开始让工作的更像坏的,反之亦然,直到找出罪魁祸首。

在进行导航时,可以尝试使用以下方式传递数据。

一种方法是将BindingContext设置为要导航到的页面。

使用导航。PushAsync导航页面并在导航页面之前重置BindingContext。你可以查看我之前做的帖子,了解更多细节。Xamarin公共类数据如何正确访问

或者,您可以尝试使用MVVM绑定到路径属性。

<Image HeightRequest="80" Source="{Binding path, Converter={StaticResource PictureNameToImageSourceConverter}}" />

另一种方法是将数据传递到要通过页面构造函数导航的页面

有关这种方式的更多详细信息,请查看MS文档。https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical#passing-一页数据结构

虽然很想知道CollectionView绑定在这种特定情况下为什么没有按预期运行,但花在计算上的时间肯定已经超过了回报递减的点,所以我只是用老式的方式添加了图像。我用StackLayout:替换了CollectionView

<StackLayout x:Name="picturesLayout" Orientation="Horizontal" />

然后只是添加了带边框的图像";"手工";在OnAppearing方法中:

picturesLayout.Children.Clear();
foreach (string pictureName in PictureNames)
{
picturesLayout.Children.Add(new Frame { Padding = 5, HasShadow = false, Content = new Image { Source = ImageManager.ReadFromDisk(pictureName), HeightRequest = 80 } });
}

(ImageManager处理诸如跟踪适当目录的完整路径之类的杂务。)

最新更新