问题
I am creating a UIActivityIndicatorView in my Controller.ViewDidLoad
UIActivityIndicatorView spinner = new UIActivityIndicatorView();
spinner.StartAnimating();
spinner.Hidden = true;
this.Add(spinner);
Then I am binding it with MVVMCross
var set = this.CreateBindingSet<TipView, TipViewModel>();
set.Bind(spinner).For(v => v.Hidden).To(vm => vm.IsBusy).WithConversion("Inverse");
When the View initially loads the UIActivityIndicatorView is spinning and visible. This is incorrect as the IsBusy property is being explicitly set to False in the ViewModel's Init(). I can see this happening and I can see the Converter invert the value.
I know the binding is properly connected because if I fire a command that updates the IsBusy property the Indicator is shown and hidden as I would expect. It is just the initial state that is incorrect.
The StartAnimating method seems to cause the Hidden flag to be overridden. If I do not call StartAnimating the Indicator hides and shows as expected. Of course that means I have a non animating Indicator.
I could get a WeakReference to the VM, listen to PropertyChanged and call StartAnimating but that seems a bit rubbish.
Does anyone have any better ideas?
回答1:
Some options you can do:
Subscribe to PropertyChanged changes and write custom code in the event handler (as you suggest in your question)
Inherit from
UIActivityIndicatorView
and write a publicget;set;
property which provides the composite functionality (callingStart
andHidden
) in theset
handlerpublic class MyIndicatorView : UIActivityIndicatorView { // ctors private bool _superHidden; public bool SuperHidden { get { return _supperHidden; } set { _superHidden = value; if (!value) StartAnimating() else StopAnimating(); Hidden = value; } } }
Provide a
View
publicget;set;
property and put the composite functionality in that (e.g.set.Bind(this).For(v => v.MyAdvanced)...
private bool _myAdvanced; public bool MyAdvanced { get { return myAdvanced; } set { myAdvanced = value; if (!value) _spinner.StartAnimating() else _spinner.StopAnimating(); _spinner.Hidden = value; } }
Write a custom binding for Hidden which replaces the default functionality and contains the combined
Start
andHidden
calls (for more on custom bindings, there's a couple of N+1 tutorials)
回答2:
After reading @slodge's reply I went down the road of Weak Event Listener and ran the code to Hide and StartAnimating in the View. Having copy and pasted that approach 3 times I realised something had to change so I implemented his 4th suggestion and wrote a Custom Binding. FWIW here is that custom binding
/// <summary>
/// Custom Binding for UIActivityIndicator Hidden.
/// This binding will ensure the indicator animates when shown and stops when hidden
/// </summary>
public class ActivityIndicatorViewHiddenTargetBinding : MvxConvertingTargetBinding
{
/// <summary>
/// Initializes a new instance of the <see cref="ActivityIndicatorViewHiddenTargetBinding"/> class.
/// </summary>
/// <param name="target">The target.</param>
public ActivityIndicatorViewHiddenTargetBinding(UIActivityIndicatorView target)
: base(target)
{
if (target == null)
{
MvxBindingTrace.Trace(
MvxTraceLevel.Error,
"Error - UIActivityIndicatorView is null in ActivityIndicatorViewHiddenTargetBinding");
}
}
/// <summary>
/// Gets the default binding mode.
/// </summary>
/// <value>
/// The default mode.
/// </value>
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
/// <summary>
/// Gets the type of the target.
/// </summary>
/// <value>
/// The type of the target.
/// </value>
public override System.Type TargetType
{
get { return typeof(bool); }
}
/// <summary>
/// Gets the view.
/// </summary>
/// <value>
/// The view.
/// </value>
protected UIActivityIndicatorView View
{
get { return Target as UIActivityIndicatorView; }
}
/// <summary>
/// Sets the value.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="value">The value.</param>
protected override void SetValueImpl(object target, object value)
{
var view = (UIActivityIndicatorView)target;
if (view == null)
{
return;
}
view.Hidden = (bool)value;
if (view.Hidden)
{
view.StopAnimating();
}
else
{
view.StartAnimating();
}
}
}
来源:https://stackoverflow.com/questions/27906367/uiactivityindicatorview-startanimating-overriding-uiactivityindicatorview-hidden