为什么向MvxListView派生类添加Header视图会破坏绑定过程?

时间:2014-02-03 04:38:47

标签: xamarin.android xamarin mvvmcross

我目前正在使用MvvmCross进行新的应用程序项目,我正在尝试使用MvxListView显示标题视图。阅读诸如this之类的讨论使我理解在设置适配器后无法添加标题视图,因此必须重写构造函数,以便在设置MvxAdapter之前添加标头。这导致我实现了以下类:

public class HeaderListView : MvxListView
{
    public FrameLayout HeaderFrame { get; set; }

    public HeaderListView(Context context, IAttributeSet attrs)
        : this(context, attrs, new MvxAdapter(context))
    {
    }

    public HeaderListView(Context context, IAttributeSet attrs, IMvxAdapter adapter) 
        : base(context, attrs, null)
    {
        InitializeHeader(context);
        // Note: Any calling derived class passing a null adapter is responsible for setting
        // it's own itemTemplateId
        if (adapter == null)
            return;

        var itemTemplateId = MvxAttributeHelpers.ReadListItemTemplateId(context, attrs);
        adapter.ItemTemplateId = itemTemplateId;
        Adapter = adapter;
    }

    private void InitializeHeader(Context context)
    {
        HeaderFrame = new FrameLayout(context);
        AddHeaderView(HeaderFrame);
    }
}

特别注意第二个构造函数中的InitializeHeader调用。当我评论这一行时,测试应用程序启动正常,并且此HeaderListView与标准MvxListView无法区分。但是,取消注释该行,您会看到一个空白ListView和一些绑定错误:

MvxBind:Error: 10.86 Problem seen during binding execution for binding ItemsSource for Names - problem TargetInvocationException: Exception has been thrown by the target of an invocation.
02-02 23:33:01.948 I/mono-stdout( 2323): MvxBind:Error: 10.86 Problem seen during binding execution for binding ItemsSource for Names - problem TargetInvocationException: Exception has been thrown by the target of an invocation.
      at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0 
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <filename unknown>:0 
  at Cirrious.MvvmCross.Binding.Bindings.Target.MvxPropertyInfoTargetBinding.SetValueImpl (System.Object target, System.Object value) [0x00000] in <filename unknown>:0 
02-02 23:33:01.948 I/mono-stdout( 2323):      at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0 
02-02 23:33:01.948 I/mono-stdout( 2323):   at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <filename unknown>:0 
02-02 23:33:01.958 I/mono-stdout( 2323):   at Cirrious.MvvmCross.Binding.Bindings.Target.MvxPropertyInfoTargetBinding.SetValueImpl (System.Object target, System.Object value) [0x00000] in <filename unknown>:0 
  at Cirrious.MvvmCross.Binding.Bindings.Target.MvxConvertingTargetBinding.SetValue (System.Object value) [0x00000] in <filename unknown>:0 
  at Cirrious.MvvmCross.Binding.Bindings.MvxFullBinding.UpdateTargetFromSource (System.Object value) [0x00000] in <filename unknown>:0 
InnerException was NullReferenceException: Object reference not set to an instance of an object
      at Cirrious.MvvmCross.Binding.Droid.Views.MvxListView.set_ItemsSource (IEnumerable value) [0x00000] in <filename unknown>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
02-02 23:33:01.958 I/mono-stdout( 2323):   at Cirrious.MvvmCross.Binding.Bindings.Target.MvxConvertingTargetBinding.SetValue (System.Object value) [0x00000] in <filename unknown>:0 
02-02 23:33:01.968 I/mono-stdout( 2323):   at Cirrious.MvvmCross.Binding.Bindings.MvxFullBinding.UpdateTargetFromSource (System.Object value) [0x00000] in <filename unknown>:0 
02-02 23:33:01.968 I/mono-stdout( 2323): InnerException was NullReferenceException: Object reference not set to an instance of an object
02-02 23:33:01.968 I/mono-stdout( 2323):      at Cirrious.MvvmCross.Binding.Droid.Views.MvxListView.set_ItemsSource (IEnumerable value) [0x00000] in <filename unknown>:0 
02-02 23:33:01.968 I/mono-stdout( 2323):   at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0 

我对这个错误没有任何意义,因为导致其出现的唯一区别是我在分配适配器之前分配了一个标题视图,但这与适配器没有任何关系。这个错误的根源是什么,我该如何解决它?

我的示例项目的完整来源可以证明此问题on github

3 个答案:

答案 0 :(得分:3)

原因与标题和页脚添加到ListView的hacky方式有关。向ListView添加页眉或页脚时,在为列表视图设置适配器时,它会将适配器包装在HeaderViewListAdapter中,该页面和页脚将返回页眉和页脚,就像它们只是适配器返回的实际行之前和之后的行一样。

MvxListView失败的原因是它的Adpater属性如下所示:

    public new IMvxAdapter Adapter
    {
        get { return base.Adapter as IMvxAdapter; }
        set
        {
            // Code to copy ItemsSource and template ID

            base.Adapter = value;
        }
    }

它不存储您提供的IMvxAdapter实例。它只是将它设置在base.Adapter中并期望它何时获得base.Adapter以后它将与之前设置的值相同。但是当页眉和页脚添加到列表中时,情况并非如此。您从base.Adapter返回的类将是一个HeaderViewListAdapter,它包装先前设置的IMvxAdapter实例。

作为MvxList的用户(除了复制和更改MvxList之外)没有好办法解决这个问题。 Stuart的修复方法是不依赖于base.Adapter来获取getter,而是将值存储到setter中的一个字段中并从getter中返回。

答案 1 :(得分:3)

通过在.axml文件中添加ItemsSource会导致List设置适配器,并且在设置适配器时无法向列表视图添加页眉/页脚。

我有一个类似的问题,并结束使用 MvxListWithHeader - 它允许您向MvxListView添加页眉和页脚。

答案 2 :(得分:0)

我看不出错误背后的任何明显原因。

看起来“绑定过程”似乎没有从您的跟踪中断。相反,看起来适配器在某种程度上被卡在null

一个有助于进一步调试的建议 - 如果您将新的ItemsSource添加到自定义列表视图中,那么这将用于绑定 - 并且将为您提供放置断点的位置,以便您可以调查ListView的状态在set_ItemsSource空引用问题期间进行控制。

    [MvxSetToNullAfterBinding]
    public new IEnumerable ItemsSource
    {
        get { return Adapter.ItemsSource; }
        set { 
            // here is where null is being reported
            // add a breakpoint and examine Adapter and ((ListView)this).Adapter in the watch window
            Adapter.ItemsSource = value; 
        }
    }

除此之外,您可以尝试将所有MvxListView代码复制到视图中,看看是否有助于识别问题。