x:Bind Converter 和 FallbackValue 未协作 (UWP 10)

x:Bind Converter and FallbackValue not collaborating (UWP 10)

我有一个涉及一堆代码的问题,但我已将其隔离。如果你想要一个 TL;DR;跳到更远的地方。如果您想了解一些上下文,这是我的情况:

我已经为我的绑定创建了三个数据转换器。其中之一是 "string prefixer":它会在您输入的任何内容前加上一个固定字符串作为前缀。在当前示例中,该固定字符串是 "ms-appx:///cache/"。第二个将 string 类型转换为 ImageSource,第三个将多个转换器链接在一起。

然后我创建了一个名为 LocalCacheFile 的 Xaml 资源。一切都如您所想。 Xaml 代码如下所示:

<Image Source="{x:Bind imageSource,Converter={StaticResource LocalCacheFile}}" />

但是,我遇到了以下问题。如果我尝试使用 FallbackValue 在 imageSource 为空时放置占位符图像,我只会在 x:Bind 中出现奇怪的行为。

下面的代码可以正常工作

<Image Source="{Binding imageSource,FallbackValue='ms-appx:///Assets/default.png',Converter={StaticResource LocalCacheFile}}" />

但是

<Image Source="{x:Bind imageSource,FallbackValue='ms-appx:///Assets/default.png',Converter={StaticResource LocalCacheFile}}" />

不会!

我已经将它隔离到只有一个转换器,DependencyProperty.UnsetValue x:Bind 似乎没有处理。

TL;DR; 这是我的字符串前缀的代码,如果我单独使用它作为测试会触发相同的错误行为:

public class StringPrefix : IValueConverter
{
    public string prefix { get; set; }

    public object Convert(object value, Type typeName, object parameter, string language)
    {
        if (value == DependencyProperty.UnsetValue || value == null || (string)value == "")
            return DependencyProperty.UnsetValue ;

        return (prefix + value.ToString());
    }

    public object ConvertBack(object value, Type typeName, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

当使用 Binding 时,上述转换器的工作方式与您预期的一样(即,如果输入字符串为空,则正确使用回退值)。与 x:Bind.

一起使用时会引发类型异常

这是怎么回事?


编辑: 有关异常的详细信息。

这是生成的代码:

        private void Update_project_imageSource(global::System.String obj, int phase)
        {
            if((phase & ((1 << 0) | NOT_PHASED | DATA_CHANGED)) != 0)
            {
                XamlBindingSetters.Set_Windows_UI_Xaml_Controls_Image_Source(this.obj16, (global::Windows.UI.Xaml.Media.ImageSource)this.LookupConverter("LocalCacheFile").Convert(obj, typeof(global::Windows.UI.Xaml.Media.ImageSource), null, null), null);
            }
        }

异常详情:

System.InvalidCastException was unhandled by user code
  HResult=-2147467262
  Message=Unable to cast object of type 'System.__ComObject' to type 'Windows.UI.Xaml.Media.ImageSource'.
  Source=Test
  StackTrace:
       at Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update_project_imageSource(String obj, Int32 phase)
       at Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update_project(Project obj, Int32 phase)
       at Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update_(ProjectView obj, Int32 phase)
       at Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update()
       at Test.Pages.ProjectView.<.ctor>b__6_0(FrameworkElement s, DataContextChangedEventArgs e)
  InnerException: 

(对我来说,生成的代码似乎并没有处理默认值的可能性。顺便说一句,__ComObjectDependencyProperty.UnsetValue

编辑 2:我应该补充一点,如果我将 Convert 函数更改为 return null 而不是 DependencyProperty.UnsetValue、x:Bind 函数,但是 x:BindBinding 都没有完成使用 FallbackValue

的预期工作

Bindingx:Bind中的FallbackValue不同。

Binding 中,FallbackValue 是绑定无法 return 值时使用的值。

A binding uses FallbackValue for cases where the Path doesn't evaluate on the data source at all, or if attempting to set it on the source with a two-way binding throws an exception that's caught by the data binding engine. FallbackValue is also used if the source value is the dependency property sentinel value DependencyProperty.UnsetValue.

但是在 x:Bind 中,FallbackValue 指定了一个当无法解析源或路径时要显示的值。它不能与 DependencyProperty.UnsetValue.

一起使用

如您所知,x:Bind 在编译时生成代码并且是强类型的。当你在 x:Bind 中使用 Converter 时,它会将 Converter 的 return 值视为与目标 属性 相同类型的值,并像在你的代码:

(global::Windows.UI.Xaml.Media.ImageSource)this.LookupConverter("LocalCacheFile").Convert(obj, typeof(global::Windows.UI.Xaml.Media.ImageSource), null, null)

如果你在 Converter 中 return DependencyProperty.UnsetValue,它会抛出异常,因为 DependencyProperty.UnsetValue 无法转换为 ImageSource

对于您的场景,您可以使用 TargetNullValue

TargetNullValue is a similar property with similar scenarios. The difference is that a binding uses TargetNullValue if the Path and Source do evaluate, but the value found there is null.

例如使用下面的代码是XAML.

<Image Source="{x:Bind imageSource, TargetNullValue='ms-appx:///Assets/default.png', Converter={StaticResource LocalCacheFile}}" />

并且在 Convert 中,return null 而不是 DependencyProperty.UnsetValue

这在 运行 应用程序和 imageSource 为空时有效。但是为了获得设计时间的好处,我们仍然需要使用 FallbackValue。所以我们可以像下面这样使用 x:Bind

<Image Source="{x:Bind imageSource, TargetNullValue='ms-appx:///Assets/default.png', FallbackValue='ms-appx:///Assets/default.png', Converter={StaticResource LocalCacheFile}}" />

x:Bind 中,FallBackValue 实际上仅用于设计时数据。现在,让我们谈谈更重要的事情。为什么使用 x:Bind。考虑到启动 IValueConverter 的成本,您认为 x:Bind 值得吗?我不是。当我看到开发人员努力让 x:Bind 正确处理列表外部的绑定时,我的建议是切换到 binding。每次。在列表中,已编译的绑定有一个 "repeat" 值,但在其他任何地方,你必须向我证明这是值得的——如果否则很难。通常 x:bind 很棒。但在这种情况下,以及 UpdateSourceTrigger 回落或默认为 binding 的情况是完全没问题的。