MVVMCross 自定义控件和绑定
MVVMCross Custom Control and Binding
我创建了一个自定义控件 (CustomCard),它是 CardView 控件的子class。我想在我的项目中的不同地方使用这个控件。
例如,我可能会手动将 CustomCard 放置在 xml 布局中,或者我可能希望 CustomCard 成为 MvxListView 中的一个项目。关键是我想尽可能多地重复使用代码并从控制 CustomCard class.
中受益
实例化 CustomCard 时,我使用标准布局 inflater 对其布局进行充气,请参见代码:
using System;
using Android.Animation;
using Android.Content;
using Android.Support.V7.Widget;
using Android.Util;
using Android.Views;
using Android.Widget;
public class Card : CardView
{
private readonly Context _context;
public Card(Context context)
: base(context)
{
_context = context;
Init();
}
public Card(Context context, IAttributeSet attrs)
: base(context, attrs)
{
_context = context;
Init();
}
private void Init()
{
var inflater = (LayoutInflater) _context.GetSystemService(Context.LayoutInflaterService);
CardView = inflater.Inflate(Resource.Layout.base_card, this);
}
}
在布局 base_card.xml 中,我有一些要使用 MVVMCross 绑定的元素,例如
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:id="@+id/basecard_title"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Title Text-->
<TextView
android:id="@+id/tv_basecard_header_title"
style="@style/card.title"
android:text="title text"
local:MvxBind="Text Title"
/>
<!-- ImageView -->
<MvxImageView
android:id="@+id/ib_basecard_header_button_expand"
style="@style/card.image"
local:MvxBind="Bitmap ImageBytes,Converter=InMemoryImage"/>
</RelativeLayout>
</FrameLayout>
我的实际 base_card 布局要复杂得多。
如果我尝试在另一个 XML 布局中使用我的 CustomCard,则会发生 none 绑定。我认为这是因为我使用标准布局 inflater 在我的 CustomCard 中为我的 base_card 充气而不是 BindingInflate() 但我不能确定。
我已经在 SO 上和通过论坛进行了搜索,但我找不到任何对使用自定义控件的人的任何引用,该自定义控件在使用 MVVMCross 绑定实例化时会膨胀它自己的视图。
有没有人做过,或者我正在尝试做一些不可能的事情?
我 运行 遇到与 CardView 控件类似的问题。由于 CardView 直接继承自 FrameLayout,我决定使用与 MvxFrameControl 几乎相同的实现(感谢 Stuart 指出 MvxFrameControl 示例):
public class MvxCardView : CardView, IMvxBindingContextOwner
{
private object _cachedDataContext;
private bool _isAttachedToWindow;
private readonly int _templateId;
private readonly IMvxAndroidBindingContext _bindingContext;
public MvxCardView(Context context, IAttributeSet attrs)
: this(MvxAttributeHelpers.ReadTemplateId(context, attrs), context, attrs)
{
}
public MvxCardView(int templateId, Context context, IAttributeSet attrs)
: base(context, attrs)
{
_templateId = templateId;
if (!(context is IMvxLayoutInflater))
{
throw Mvx.Exception("The owning Context for a MvxCardView must implement LayoutInflater");
}
_bindingContext = new MvxAndroidBindingContext(context, (IMvxLayoutInflater)context);
this.DelayBind(() =>
{
if (Content == null && _templateId != 0)
{
Mvx.Trace("DataContext is {0}", DataContext == null ? "Null" : DataContext.ToString());
Content = _bindingContext.BindingInflate(_templateId, this);
}
});
}
protected MvxCardView(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected IMvxAndroidBindingContext AndroidBindingContext
{
get { return _bindingContext; }
}
public IMvxBindingContext BindingContext
{
get { return _bindingContext; }
set { throw new NotImplementedException("BindingContext is readonly in the list item"); }
}
protected View Content { get; set; }
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.ClearAllBindings();
_cachedDataContext = null;
}
base.Dispose(disposing);
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
_isAttachedToWindow = true;
if (_cachedDataContext != null
&& DataContext == null)
{
DataContext = _cachedDataContext;
}
}
protected override void OnDetachedFromWindow()
{
_cachedDataContext = DataContext;
DataContext = null;
base.OnDetachedFromWindow();
_isAttachedToWindow = false;
}
[MvxSetToNullAfterBinding]
public object DataContext
{
get { return _bindingContext.DataContext; }
set
{
if (_isAttachedToWindow)
{
_bindingContext.DataContext = value;
}
else
{
_cachedDataContext = value;
if (_bindingContext.DataContext != null)
{
_bindingContext.DataContext = null;
}
}
}
}
}
用法:
<YourNamespace.MvxCardView
android:layout_width="match_parent"
android:layout_height="match_parent"
local:MvxTemplate="@layout/base_card"
local:MvxBind="DataContext ." />
注意:使用自定义实现还解决了我使用 local:MvxBind="Click MyCommand"
将单击命令绑定到 CardView 控件的问题,直到子类化 CardView 才起作用。
我创建了一个自定义控件 (CustomCard),它是 CardView 控件的子class。我想在我的项目中的不同地方使用这个控件。
例如,我可能会手动将 CustomCard 放置在 xml 布局中,或者我可能希望 CustomCard 成为 MvxListView 中的一个项目。关键是我想尽可能多地重复使用代码并从控制 CustomCard class.
中受益实例化 CustomCard 时,我使用标准布局 inflater 对其布局进行充气,请参见代码:
using System;
using Android.Animation;
using Android.Content;
using Android.Support.V7.Widget;
using Android.Util;
using Android.Views;
using Android.Widget;
public class Card : CardView
{
private readonly Context _context;
public Card(Context context)
: base(context)
{
_context = context;
Init();
}
public Card(Context context, IAttributeSet attrs)
: base(context, attrs)
{
_context = context;
Init();
}
private void Init()
{
var inflater = (LayoutInflater) _context.GetSystemService(Context.LayoutInflaterService);
CardView = inflater.Inflate(Resource.Layout.base_card, this);
}
}
在布局 base_card.xml 中,我有一些要使用 MVVMCross 绑定的元素,例如
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:id="@+id/basecard_title"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Title Text-->
<TextView
android:id="@+id/tv_basecard_header_title"
style="@style/card.title"
android:text="title text"
local:MvxBind="Text Title"
/>
<!-- ImageView -->
<MvxImageView
android:id="@+id/ib_basecard_header_button_expand"
style="@style/card.image"
local:MvxBind="Bitmap ImageBytes,Converter=InMemoryImage"/>
</RelativeLayout>
</FrameLayout>
我的实际 base_card 布局要复杂得多。
如果我尝试在另一个 XML 布局中使用我的 CustomCard,则会发生 none 绑定。我认为这是因为我使用标准布局 inflater 在我的 CustomCard 中为我的 base_card 充气而不是 BindingInflate() 但我不能确定。
我已经在 SO 上和通过论坛进行了搜索,但我找不到任何对使用自定义控件的人的任何引用,该自定义控件在使用 MVVMCross 绑定实例化时会膨胀它自己的视图。
有没有人做过,或者我正在尝试做一些不可能的事情?
我 运行 遇到与 CardView 控件类似的问题。由于 CardView 直接继承自 FrameLayout,我决定使用与 MvxFrameControl 几乎相同的实现(感谢 Stuart 指出 MvxFrameControl 示例):
public class MvxCardView : CardView, IMvxBindingContextOwner
{
private object _cachedDataContext;
private bool _isAttachedToWindow;
private readonly int _templateId;
private readonly IMvxAndroidBindingContext _bindingContext;
public MvxCardView(Context context, IAttributeSet attrs)
: this(MvxAttributeHelpers.ReadTemplateId(context, attrs), context, attrs)
{
}
public MvxCardView(int templateId, Context context, IAttributeSet attrs)
: base(context, attrs)
{
_templateId = templateId;
if (!(context is IMvxLayoutInflater))
{
throw Mvx.Exception("The owning Context for a MvxCardView must implement LayoutInflater");
}
_bindingContext = new MvxAndroidBindingContext(context, (IMvxLayoutInflater)context);
this.DelayBind(() =>
{
if (Content == null && _templateId != 0)
{
Mvx.Trace("DataContext is {0}", DataContext == null ? "Null" : DataContext.ToString());
Content = _bindingContext.BindingInflate(_templateId, this);
}
});
}
protected MvxCardView(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected IMvxAndroidBindingContext AndroidBindingContext
{
get { return _bindingContext; }
}
public IMvxBindingContext BindingContext
{
get { return _bindingContext; }
set { throw new NotImplementedException("BindingContext is readonly in the list item"); }
}
protected View Content { get; set; }
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.ClearAllBindings();
_cachedDataContext = null;
}
base.Dispose(disposing);
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
_isAttachedToWindow = true;
if (_cachedDataContext != null
&& DataContext == null)
{
DataContext = _cachedDataContext;
}
}
protected override void OnDetachedFromWindow()
{
_cachedDataContext = DataContext;
DataContext = null;
base.OnDetachedFromWindow();
_isAttachedToWindow = false;
}
[MvxSetToNullAfterBinding]
public object DataContext
{
get { return _bindingContext.DataContext; }
set
{
if (_isAttachedToWindow)
{
_bindingContext.DataContext = value;
}
else
{
_cachedDataContext = value;
if (_bindingContext.DataContext != null)
{
_bindingContext.DataContext = null;
}
}
}
}
}
用法:
<YourNamespace.MvxCardView
android:layout_width="match_parent"
android:layout_height="match_parent"
local:MvxTemplate="@layout/base_card"
local:MvxBind="DataContext ." />
注意:使用自定义实现还解决了我使用 local:MvxBind="Click MyCommand"
将单击命令绑定到 CardView 控件的问题,直到子类化 CardView 才起作用。