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:
(对我来说,生成的代码似乎并没有处理默认值的可能性。顺便说一句,__ComObject
是 DependencyProperty.UnsetValue
。
编辑 2:我应该补充一点,如果我将 Convert 函数更改为 return null 而不是 DependencyProperty.UnsetValue、x:Bind
函数,但是 x:Bind
和 Binding
都没有完成使用 FallbackValue
的预期工作
Binding
和x: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
的情况是完全没问题的。
我有一个涉及一堆代码的问题,但我已将其隔离。如果你想要一个 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:
(对我来说,生成的代码似乎并没有处理默认值的可能性。顺便说一句,__ComObject
是 DependencyProperty.UnsetValue
。
编辑 2:我应该补充一点,如果我将 Convert 函数更改为 return null 而不是 DependencyProperty.UnsetValue、x:Bind
函数,但是 x:Bind
和 Binding
都没有完成使用 FallbackValue
Binding
和x: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
的情况是完全没问题的。