【问题标题】:CollectionView template calls converter incorrectlyCollectionView 模板调用转换器不正确
【发布时间】:2022-01-24 18:10:52
【问题描述】:

Xamarin.Forms 5.0.0.2012、Visual 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 实例。当 List 没有项目时,屏幕显示正确。当它确实有项目时,应用程序会在屏幕出现时崩溃。如果我启动附加到 VS 调试器的应用程序,屏幕会在崩溃前冻结,并且我永远不会得到任何信息。

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

标记中是否有我没有看到或没有意识到的东西导致了这种情况?或者任何人都可以建议我没有想到的进一步调试?


更新

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

EDIT 回应 cmets:

来自 EditPage 代码隐藏:

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

...

        protected override void OnAppearing( )
        {
            base.OnAppearing();

            picturesView.ItemsSource = PictureNames;
        }

PictureNames 属性实际上是由从 EditPage 导航到的另一个 Page 设置的:

        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 是一个 List,在该屏幕上充当 ListView.ItemsSource,其中图片被选中或取消选中。

更新

在设置了很多断点之后,我确定picturesView.ItemsSource = PictureNames; 行是崩溃发生的地方。看起来很奇怪,它只在模板显示 Image 而不是 Label 时发生,因为实际上从未调用转换器。

更新

添加延迟的技巧确实让我到达了断点。我发现比以往任何时候都更令人费解:传递给我们转换器的 Convert 方法的 value 参数是 null。这是从图片选择屏幕返回的情况,或者如果我设置绑定列表以响应 Button 而不是 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 方法的 value 参数为 null,即使在同一个模板实例中具有相同的绑定值显示在标签中。如果我像以前一样在分配 ItemsSource 属性的行上设置断点,调试器将冻结。

更新

最后开始怀疑我在框架中触发了一些极端情况错误,我将 CollectionView 替换为 CarouselView,它可以正常工作:

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

我将其添加为问题的更新而不是答案,因为我实际上仍然不知道解释。

【问题讨论】:

  • 如果您认为转换器导致崩溃,为什么不发布该代码?转换器有异常处理代码吗?
  • 我应该澄清一下标题。我不认为转换器导致崩溃。
  • 这似乎是我的罪魁祸首。如果您将图像换成标签并且崩溃停止,那么这似乎很重要。您是否检查过日志中的任何相关消息?您是否设置了任何崩溃报告工具?
  • 建议:使用try-catch 在转换器内包装代码。在转换器中放置一个断点。和/或转换器中的 Debug.WriteLine。听起来将列表中的一个字符串转换为图像存在一些问题。
  • 这是个好主意。从未到达转换器第一行的断点。

标签: xamarin.forms valueconverter


【解决方案1】:

我在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。
  • 重新开始,但这次按下按钮 - 这是有效还是崩溃?

也就是说,消除去另一个页面、查询值、返回这个页面的所有复杂性。

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

之后就是“分而治之”。开始让正常工作的人更像坏人,和/或反之亦然,直到找出罪魁祸首。

【讨论】:

  • 我已经更新了这个问题,以回应在这里尝试您的建议。我刚上老学校大约半天时间,然后“手动”一次将图像添加到 EditPage。
【解决方案2】:

您可以尝试在导航时通过以下方式传递数据。

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

使用 Navigation.PushAsync 导航页面并在页面导航之前重置BindingContext。您可以查看我之前完成的线程以获取更多详细信息。 Xamarin Public Class data how to acess it properly

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

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

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

有关这种方式的更多详细信息,请查看 MS 文档。 https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical#passing-data-through-a-page-constructor

【讨论】:

    【解决方案3】:

    虽然知道为什么 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 处理诸如跟踪适当目录的完整路径之类的琐事。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-21
      • 2019-12-23
      • 1970-01-01
      • 2018-11-25
      • 1970-01-01
      • 2018-12-20
      • 1970-01-01
      相关资源
      最近更新 更多