【问题标题】:MvvmCross Android converter causes cursor to jumpMvvmCross Android 转换器导致光标跳转
【发布时间】:2016-11-01 12:28:04
【问题描述】:

我们在 MvvmCross 中与 Android 中的 EditText 控件相关的转换器存在问题:

在我们的应用中,用户插入用户数据。我们必须在转换器中对这些数据进行一些计算,然后将数据写入我们的视图模型中。

只要用户不恢复他的输入,这就会起作用。 这意味着,如果他使用后退键,则该值被正确编辑,直到他到达“。”之前的最后一位小数。 (例如:55.99,当他到达“55.9”时)。 “.9”将被正确删除,但光标会在剩余的“55”之前跳转。 我们如何解决这种烦人的行为?

视图模型提取:

private Nullable mdValue1 = null;

public Nullable<decimal> Value1
{
    get { return mdValue1; }
    set
    {
     SetProperty(ref mdValue1, value);
    }
}

private Nullable<decimal> mdValue2; 

public Nullable<decimal> Value2
{
    get { return mdValue2; }
    set
    {

        SetProperty(ref mdValue2, value, nameof(Value2));

    }
}

转换器(简体):

public class DecimalToStringValueConverter : MvxValueConverter<Nullable<decimal>, string>
{
protected override string Convert(Nullable<decimal> poValue, Type poTargetType, object poParameter, CultureInfo poCulture)
{
    if (!poValue.HasValue)
    {
        return null;
    }

    return poValue.Value.ToString();
}

protected override Nullable<decimal> ConvertBack(string value, Type targetType, object parameter, CultureInfo culture)
{
    if (string.IsNullOrWhiteSpace(value))
    {
        return null;
    }

    return decimal.Parse(value);
}
}

配置

安卓版本:4.4/5.1/7

平台:Xamarin

【问题讨论】:

    标签: c# android xamarin mvvmcross


    【解决方案1】:

    似乎正在发生的事情是当点被删除并转换为小数时,ViewModel 中值的结果变化与 EditText 不同,导致 ViewModel 设置 EditText 的值。

    示例: 用户输入59.9,然后退格9。这将 EditText 中的值保留为 59.,它被解析为 59 的小数到 ViewModel。由于59. 不等于59,ViewModel 会将 EditText 中的值更新为59,这就是导致光标跳到开头的原因。

    解决此问题的一种快速方法是创建自定义绑定,以确保在删除最后一个小数位后光标始终位于 EditText 的末尾。这可以使用SetSelection 来完成,它将光标定位在SetValueImpl 方法中。

    public class DecimalEditTextTargetBinding : MvxConvertingTargetBinding
    {
        protected EditText EditTextControl => Target as EditText;
    
        private IDisposable _subscription;
    
        public DecimalEditTextTargetBinding(EditText target) : base(target)
        {
            if (target == null)
                MvxBindingTrace.Error($"Error - EditText is null in {nameof(DecimalEditTextTargetBinding)}");
        }
    
        public override Type TargetType => typeof(string);
        public override MvxBindingMode DefaultMode => MvxBindingMode.TwoWay;
    
        protected override void SetValueImpl(object target, object value)
        {
            ((TextView)target).Text = (string)value;
            EditTextControl.SetSelection(EditTextControl.Text?.Length ?? 0);
        }
    
        public override void SubscribeToEvents()
        {
            if (EditTextControl == null)
                return;
    
            _subscription = EditTextControl.WeakSubscribe<TextView, AfterTextChangedEventArgs>(
                nameof(EditTextControl.AfterTextChanged),
                EditTextOnAfterTextChanged);
        }
    
        private void EditTextOnAfterTextChanged(object sender, AfterTextChangedEventArgs e)
        {
            FireValueChanged(EditTextControl.Text);
        }
    
        protected override void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                _subscription?.Dispose();
                _subscription = null;
            }
            base.Dispose(isDisposing);
        }
    }
    

    然后在你的Setup.cs注册自定义绑定:

    protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
    {
        base.FillTargetFactories(registry);
        registry.RegisterCustomBindingFactory<EditText>("DecimalText", inputField => new DecimalEditTextTargetBinding(inputField));
    }
    

    XML 用法:

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="numberDecimal|numberSigned"
        local:MvxBind="DecimalText DecimalToString(Value1)" />
    

    MvvmCross 4.4.0 之前的绑定示例:

    public class DecimalEditTextTargetBinding : MvxConvertingTargetBinding
    {
        private bool _subscribed;
    
        public DecimalEditTextTargetBinding(EditText target) : base(target)
        {
            if (target == null)
                MvxBindingTrace.Error($"Error - EditText is null in {nameof(DecimalEditTextTargetBinding)}");
        }
    
        protected EditText EditTextControl => Target as EditText;
    
        public override Type TargetType => typeof(string);
    
        public override MvxBindingMode DefaultMode => MvxBindingMode.TwoWay;
    
        protected override void SetValueImpl(object target, object value)
        {
            ((TextView)target).Text = (string)value;
            EditTextControl.SetSelection(EditTextControl.Text?.Length ?? 0);
        }
    
        public override void SubscribeToEvents()
        {
            if (EditTextControl == null)
                return;
    
            EditTextControl.AfterTextChanged += EditTextOnAfterTextChanged;
            _subscribed = true;
        }
    
        private void EditTextOnAfterTextChanged(object sender, AfterTextChangedEventArgs e)
        {
            FireValueChanged(EditTextControl.Text);
        }
    
        protected override void Dispose(bool isDisposing)
        {
            if (isDisposing && EditTextControl != null && _subscribed)
            {
                EditTextControl.AfterTextChanged -= EditTextOnAfterTextChanged;
                _subscribed = false;
            }
    
            base.Dispose(isDisposing);
        }
    }
    

    【讨论】:

    • 嗨!首先非常感谢您的宝贵时间!你帮了我们很多,我们对 Xamarin 很陌生。光标现在可以工作了。 “弱订阅”引发了另一个问题。找不到它,它被标记为红色。有要使用的组件吗?
    • WeakSubscribe 位于MvvmCross.Platform.WeakSubscription 下,该Mvvmcross 4.4.0. 已添加如果您使用的是旧版本,则可以直接订阅AfterTextChanged。我将更新答案以包含一个示例。
    • 我们现在是 4.4 并且可以正常工作!非常感谢,非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 2018-10-10
    • 2016-06-29
    • 2016-05-19
    • 1970-01-01
    相关资源
    最近更新 更多