UWP上关于x:Bind的一些坑

写UWP的都知道x:Bind是什么,就不再赘述
最近在写的时候发现,完全切换到x:Bind之后的确有一些性能提高,但是同时也带来了几个坑
Windows 10 1607之后,x:Bind支持使用方法,详情可以参见这里,简单来说可以看成是Converter的替代方案,比单纯的Converter更加方便。但是这里有一个坑。
ItemsControl(例如ListView/GridView)内如果用自定义控件作为ItemTemplate,并且在自定义控件内使用x:Bind,100%会抛NullReferenceException
举个实例:
我有这么个数据类型

public class SampleModel
{
public SampleModel InnerModel { get; set; }
public string Text { get; set; }
}

我这么定义一个ListView

<ListView ItemsSource="{x:Bind Source}">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="local:SampleModel">
            <local:MyUserControl1 DataContext="{Binding}"/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

MyUserControl1内是这样的

<StackPanel Orientation="Vertical">
    <TextBlock Text="{x:Bind local:MainPage.SampleMethod(DataContext.(local:SampleModel.Text))}"/>
    <TextBlock Text="{x:Bind local:MainPage.SampleMethod(DataContext.(local:SampleModel.InnerModel).Text)}"/>
</StackPanel>

多余的code behind略去,这样出来的代码100%会崩溃

稍微分析一下,在x:Bind生成对应.g.cs文件的时候,会有一个 Update_ 方法,如下类似

可以看到如果是绑定到了方法,会跳过null check直接执行,而执行的过程如下类似

很明显,两个红框的位置如果DataContext为空就会抛NullReferenceException,而监听MyUserControl1的DataContextChanged事件,第一次命中的时候e.NewValue一定为空,这当然会抛NullReferenceException了

有没有什么walkaround吗?

有,设置一个FallbackValue,这样x:Bind就会生成如下的代码

可以看到在生成的代码中x:Bind会对值进行null check,如果失败就为FallbackValue的值,这样就避免了出现抛NullReferenceException导致程序崩溃的尴尬

但是这里也有一个限制,就是你需要绑定到的方法的参数类型不能是基类类型,比如说如果是这样的情况

编译的时候就会报错

将参数改为SampleModel即可

另外,由于UWP的xaml还不支持泛型,所以要是想使用泛型方法也是不可以的

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据