我正在使用MvvmCross和MonoDroid。
在viewmodel中的一个计时器中,每分钟我调用RaisePropertyChanged("MinutesRemaining")
- MinutesRemaining
是一个整数,指定当前条目结束前的持续时间(以及是,在UI线程上调用! )。
MinutesRemaining
使用MvvmCross绑定到TextView
。
在Xamarin的4.10.1
更新之前,应用程序将完全崩溃,并且没有错误消息打印到跟踪 - 它现在在调试时正确断开,并在调用PropertyChanged
事件时给出以下错误:
MvxBind:Error:281.24 Problem seen during binding execution for binding Text for MinutesRemaining - problem ArgumentException: 'jobject' must not be IntPtr.Zero.
Parameter name: jobject
at Android.Runtime.JNIEnv.CallVoidMethod (IntPtr jobject, IntPtr jmethod, Android.Runtime.JValue[] parms) [0x00010] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.10.1-branch/9d03ce3e/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:499
at Android.Widget.TextView.set_TextFormatted (ICharSequence value) [0x00034] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.10.1-branch/9d03ce3e/source/monodroid/src/Mono.Android/platforms/android-14/src/generated/Android.Widget.TextView.cs:1814
at Android.Widget.TextView.set_Text (System.String value) [0x00013] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.10.1-branch/9d03ce3e/source/monodroid/src/Mono.Android/platforms/android-14/src/generated/Android.Widget.TextView.cs:1823
at Cirrious.MvvmCross.Binding.Droid.Target.MvxTextViewTextTargetBinding.SetValueImpl (System.Object target, System.Object toSet) [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
它第一次绑定正确 - 只会在后续RaisePropertyChanged
次调用中发生这种情况。相同的代码也适用于Windows 8和Windows Phone。
在用于上述场景的适配器中使用JavaFinalise修复了问题(在此处找到:MVVMCross Binding Crashes Android Application)。我现在遇到的问题是相同的结果,但适配器中的第一个视图绑定到父视图模型中的属性(而不是项目)。
用于绑定的代码如下:
public class SubjectFilterAdapter : MvxAdapter {
private EntityListFragment<TEntity, TViewModel> _owner;
public SubjectFilterAdapter(Context context, EntityListFragment<TEntity, TViewModel> owner) : base(context, (IMvxAndroidBindingContext)owner.BindingContext) {
_owner = owner;
}
protected override View GetBindableView(View convertView, object dataContext, int templateId) {
var view = base.GetBindableView(convertView, dataContext, templateId);
if (templateId == ItemTemplateId && GetPosition(dataContext) == 0) {
var set = _owner.CreateBindingSet<EntityListFragment<TEntity, TViewModel>, TViewModel>();
set.Bind(view.FindViewById<TextView>(Resource.Id.SelectedScheduleText))
.To(x => x.SelectedScheduleText).WithClearBindingKey("SelectedScheduleTextFilterBinding");
set.Apply();
}
return view;
}
protected override void JavaFinalize() {
if (this.BindingContext != null)
this.BindingContext.ClearAllBindings();
base.JavaFinalize();
}
}
它可以正常工作(对于前几个更改)但在此之后抛出上述异常。使用MvvmCross 3.0.14-beta3
。
谢谢!
答案 0 :(得分:7)
通过将listitem / cell绑定上下文与父上下文混合,您将进入相当高级的区域。
为了帮助尝试解释/调试您正在进行的操作,您需要了解所有父生命周期,listitem / cell生命周期以及相应MvvmCross绑定上下文的生命周期。
在父生命周期级别,这通常是Android Activity
或Fragment
。为了简单起见,我将在本答案的其余部分使用Activity
。
此Activity
有几个关键的生命周期事件
OnCreate
仅在首次启动Activity
时被调用一次OnDestroy
不会再次显示时,Activity
才会被调用一次。MvvmCross截获这些事件并且:
OnCreate
内,它将ViewModel
设置为Activity
的{{1}}。用户代码 - 通常在DataContext
内膨胀的Xml代码 - 然后创建绑定。这些绑定存储在SetContentView
BindingContext
内
Activity
内,MvvmCross会破坏OnDestroy
在我们感兴趣的用户界面中,BindingContext
拥有Activity
,ListView
为其设置了ListView
。在此方案中,Adapter
及其DataContext
的{{1}}与其父级相同。
在ListView
的生命周期内,列表可能需要显示大量项目。随时显示的项目可能会发生变化 - 包括用户触摸操作和视图模型更改。要显示这些项目,Adapter
会向ListView
询问ListView
。对于每个项目,它显示Adapter
提供View
,并且可以重复使用这些Adapter
(使用View
参数)。但是,有时候,这些View
也不会被重用 - 在这种情况下,即使在Java / Dalvik convertView
被删除并且Java完成之后,有时候View对象仍然可以继续使用C#。
MvvmCross拦截其View
内的View
次来电。对于每次调用,它不仅会返回GetView
,还会返回MvxAdapter
。这是View
,添加了MvxListItemView
- 这样,MvvmCross用户就可以将每个View
绑定到其列表项BindingContext
。
当重复使用MvxListItemView
时,MvvmCross只需更改其DataContext
即可。
当MvxListItemView
未被重复使用时 - 当它从用户界面中删除,然后DataContext
d - MvvmCross拦截MvxListItemView
事件并使用它来切换JavaFinalize
到OnDetachedFromWindow
。它在DataContext
而不是null
上执行此操作,因为保证在UI线程上进行Window调用,并且感觉(对我来说)这样做更干净。
注意在最近的版本中,这种行为中的某些行为略有改变 - 但上面的描述对于v3.0.14是正确的
有了这样的背景,您目前正在尝试做的是在OnDetachedFromWindow
的{{1}}内为JavaFinalize
的内容创建绑定。
这意味着绑定对ListItemView
的生命周期并没有很好的理解 - 所以即使从屏幕上删除了BindingContext
之后绑定也可以说是活着的)最终确定。
要解决此问题......
Activity
“有点顽皮”,因为它会在ListItemView
的{{1}}期间清除父ListItemView
的BindingContext。这可能是可以做的,但它不应该是必要的 - JavaFinalize
自己的Activity
应该处理它。答案 1 :(得分:0)
通过将上述适配器代码更新为:
来解决 public class SubjectFilterAdapter : MvxAdapter {
private EntityListFragment<TEntity, TViewModel> _owner;
private MvxFluentBindingDescriptionSet<EntityListFragment<TEntity, TViewModel>, TViewModel> _scheduleBindingSet;
public SubjectFilterAdapter(Context context, EntityListFragment<TEntity, TViewModel> owner)
: base(context, (IMvxAndroidBindingContext)owner.BindingContext) {
_owner = owner;
}
protected override View GetBindableView(View convertView, object dataContext, int templateId) {
var view = base.GetBindableView(convertView, dataContext, templateId);
if (templateId == ItemTemplateId && GetPosition(dataContext) == 0) {
if (_scheduleBindingSet != null) {
_owner.BindingContext.ClearBindings("SelectedScheduleTextFilterBinding");
_scheduleBindingSet = null;
}
_scheduleBindingSet = _owner.CreateBindingSet<EntityListFragment<TEntity, TViewModel>, TViewModel>();
_scheduleBindingSet.Bind(view.FindViewById<TextView>(Resource.Id.SelectedScheduleText))
.To(x => x.SelectedScheduleText).WithClearBindingKey("SelectedScheduleTextFilterBinding");
_scheduleBindingSet.Apply();
}
return view;
}
protected override void JavaFinalize() {
if (this.BindingContext != null)
this.BindingContext.ClearAllBindings();
base.JavaFinalize();
}
}