x:Bind Converter 和 FallbackValue 不协作 (UWP 10) [英] x:Bind Converter and FallbackValue not collaborating (UWP 10)
问题描述
我有一个涉及大量代码的问题,但我已将其隔离.如果你想要一个 TL;DR;再往下跳.如果你想要一些上下文,这是我的情况:
我为我的绑定创建了三个数据转换器.其中之一是字符串前缀":它使用固定字符串为您输入的任何内容添加前缀.在当前示例中,该固定字符串是 "ms-appx:///cache/"
.第二个将 string
类型转换为 ImageSource
,第三个将多个转换器链接在一起.
然后我创建了一个名为 LocalCacheFile
的 Xaml 资源.一切都如您所想.用于此的 Xaml 代码如下所示:
但是,我遇到了以下问题.如果我尝试使用 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}}"/>
没有!
我已经将它隔离到只有一个转换器,并且 x:Bind 似乎没有处理 DependencyProperty.UnsetValue
.
TL;DR;这是我的字符串前缀的代码,如果我单独使用它作为测试会触发相同的错误行为:
公共类 StringPrefix : IValueConverter{公共字符串前缀 { 获取;放;}公共对象转换(对象值,类型类型名称,对象参数,字符串语言){if (value == DependencyProperty.UnsetValue || value == null || (string)value == "")返回 DependencyProperty.UnsetValue ;返回(前缀 + value.ToString());}public object ConvertBack(object value, Type typeName, object parameter, string language){抛出新的 NotImplementedException();}}
在使用 Binding
时,上述转换器的工作原理与您期望的一样(即,如果输入字符串为空,则正确使用回退值).与 x:Bind
一起使用时会引发类型异常.
这是怎么回事?
<小时>有关异常的详细信息.
这是生成的代码:
private void Update_project_imageSource(global::System.String obj, int phase){if((相位 & ((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), 空, 空), 空);}}
异常详情:
System.InvalidCastException 未被用户代码处理HResult=-2147467262Message=无法将System.__ComObject"类型的对象转换为Windows.UI.Xaml.Media.ImageSource"类型.来源=测试堆栈跟踪:在 Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update_project_imageSource(字符串 obj,Int32 阶段)在 Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update_project(项目 obj,Int32 阶段)在 Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update_(ProjectView obj, Int32 阶段)在 Test.Pages.ProjectView.ProjectView_obj1_Bindings.Update()在 Test.Pages.ProjectView.<.ctor>b__6_0(FrameworkElement s, DataContextChangedEventArgs e)内部异常:
(对我来说,生成的代码似乎没有处理默认值的可能性.顺便说一句,__ComObject
是 DependencyProperty.UnsetValue
.>
Edit 2:我应该补充一点,如果我将 Convert 函数更改为返回 null 而不是 DependencyProperty.UnsetValue,x:Bind
函数,但随后都没有 x:Bind
和 Binding
使用 FallbackValue
Binding
和 x:Bind
中的 FallbackValue
是不同的.
在 Binding
中,FallbackValue
是绑定无法返回值时使用的值.
绑定使用 FallbackValue 来处理 Path 根本不评估数据源,或者如果尝试使用双向绑定在源上设置它会引发数据捕获的异常绑定引擎.如果源值是依赖属性标记值 DependencyProperty.UnsetValue.
但在 x:Bind
中,FallbackValue
指定无法解析源或路径时显示的值.它不能与 DependencyProperty.UnsetValue
一起使用.
您已经知道,x:Bind
在编译时生成代码并且它是强类型的.当你在x:Bind
中使用Converter
时,它会将Converter
的返回值视为目标属性的相同类型并对其进行强制转换就像在您的代码中一样:
(global::Windows.UI.Xaml.Media.ImageSource)this.LookupConverter("LocalCacheFile").Convert(obj, typeof(global::Windows.UI.Xaml.Media.ImageSource), null, 空值)
如果您在 Converter
中返回 DependencyProperty.UnsetValue
,它将抛出异常,因为 DependencyProperty.UnsetValue
无法转换为 图像源
.
对于您的场景,您可以使用 TargetNullValue
.
TargetNullValue 是具有类似场景的类似属性.不同之处在于,如果 Path 和 Source 求值,则绑定使用 TargetNullValue,但在那里找到的值为 null.
例如使用以下代码是 XAML.
<Image Source="{x:Bind imageSource, TargetNullValue='ms-appx:///Assets/default.png', Converter={StaticResource LocalCacheFile}}"/>
在Convert
中,返回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}}"/>
I have a problem that involves a bunch of code, but I've isolated it down. If you want a TL;DR; jump to it further down. If you want a bit of context, here's my situation:
I have created three data converters for my bindings. One of them is a "string prefixer": it prefixes whatever you put in with a fixed string. In the current example, that fixed string is "ms-appx:///cache/"
. The second one turns a string
type into an ImageSource
, and the third one chains multiple converters together.
I've then created a Xaml resource which is called LocalCacheFile
. Everything works as you would think. Xaml code for this looks like so:
<Image Source="{x:Bind imageSource,Converter={StaticResource LocalCacheFile}}" />
However, I'm having the following problem. If I try to use the FallbackValue to put a placeholder image for when imageSource
is empty, I get weird behaviour in x:Bind
only.
The following code works as one would expect:
<Image Source="{Binding imageSource,FallbackValue='ms-appx:///Assets/default.png',Converter={StaticResource LocalCacheFile}}" />
But
<Image Source="{x:Bind imageSource,FallbackValue='ms-appx:///Assets/default.png',Converter={StaticResource LocalCacheFile}}" />
does not!
I've isolated it down to just one converter and it is DependencyProperty.UnsetValue
that x:Bind seems not to be handling.
TL;DR; Here is the code for my string prefixer, which if I use alone as a test triggers the same faulty behaviour:
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();
}
}
The above converter works as you would expect it to (i.e. if the input string is empty, the fallback value is properly used) when using Binding
. It raises a type exception when used with x:Bind
.
What's up with this?
Edit: details about the exception.
This is the generated code:
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);
}
}
Exception details:
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:
(to me, it looks like the generated code just doesn't deal with the default value possibility. Btw, that __ComObject
is the DependencyProperty.UnsetValue
.
Edit 2: I should add that if I change the Convert function to return null instead of DependencyProperty.UnsetValue, x:Bind
functions, but then neither x:Bind
nor Binding
do their expected job of using the FallbackValue
The FallbackValue
in Binding
and x:Bind
is different.
In Binding
, FallbackValue
is the value to use when the binding is unable to return a value.
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.
But in x:Bind
, FallbackValue
specifies a value to display when the source or path cannot be resolved. It can't work with DependencyProperty.UnsetValue
.
As you've already know, x:Bind
generates code at compile-time and it's strongly typed. When you use Converter
in x:Bind
, it will regard the Converter
's return value of the same type as the target property and cast it like in your code:
(global::Windows.UI.Xaml.Media.ImageSource)this.LookupConverter("LocalCacheFile").Convert(obj, typeof(global::Windows.UI.Xaml.Media.ImageSource), null, null)
If you return DependencyProperty.UnsetValue
in your Converter
, it will throw exception as DependencyProperty.UnsetValue
can't cast to ImageSource
.
For your scenario, you can use 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.
For example using following code is XAML.
<Image Source="{x:Bind imageSource, TargetNullValue='ms-appx:///Assets/default.png', Converter={StaticResource LocalCacheFile}}" />
And in the Convert
, return null
instead of DependencyProperty.UnsetValue
.
This works when running the app and the imageSource
is empty. But to gain design time benefit, we still need use FallbackValue
. So we can use x:Bind
like following:
<Image Source="{x:Bind imageSource, TargetNullValue='ms-appx:///Assets/default.png', FallbackValue='ms-appx:///Assets/default.png', Converter={StaticResource LocalCacheFile}}" />
这篇关于x:Bind Converter 和 FallbackValue 不协作 (UWP 10)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!