【问题标题】:How to change when the Data Binding (back to Source) happens in Xamarin Forms?如何在 Xamarin 表单中发生数据绑定(返回源)时进行更改?
【发布时间】:2020-04-03 10:51:41
【问题描述】:

我的页面中有多个Entry 视图使用数据绑定连接到视图模型。一切都按预期工作,除了我需要在用户输入时计算一个值。

    public decimal DownPayment
    {
        get => this.loanValues.DownPayment;
        set
        {
            this.loanValues.DownPayment = value;
            this.CalculateValues();
        }
    }

    public decimal TradeInPrice
    {
        get => this.loanValues.TradeInPrice;
        set
        {
            this.loanValues.TradeInPrice = value;
            this.CalculateValues();
        }
    }

    // Other properties

现在的问题是,只要用户输入值,就会设置属性。无论如何要更改发生数据绑定的事件(即在Completed 而不是TextChanged)?

我看到 XAML 绑定中有 UpdateSourceEventName 属性,但我不知道为什么它不起作用:

<Entry Placeholder="Down Payment ($)" Text="{Binding DownPayment, UpdateSourceEventName=Completed}" />

更新:UpdateSourceEventName 的文档说它供 Xamarin.Forms 平台内部使​​用

【问题讨论】:

  • 而不是在集合中调用CalculateValues,而是从Completed事件中调用它
  • @Jason 谢谢,虽然这个应该很快解决我目前的情况,但我仍然对解决它的“正确”方法感兴趣。但是,是的,这是一个很好的解决方案。

标签: xaml xamarin events xamarin.forms data-binding


【解决方案1】:

您可以使用 EventToCommandBehavior 将 Entry 的 Event Completed 转换为 Command 并处理 ViewModel 中的逻辑。

using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;

namespace App18
{
    public class BehaviorBase<T> : Behavior<T> where T : BindableObject
    {
        public T AssociatedObject { get; private set; }

        protected override void OnAttachedTo(T bindable)
        {
            base.OnAttachedTo(bindable);
            AssociatedObject = bindable;

            if (bindable.BindingContext != null)
            {
                BindingContext = bindable.BindingContext;
            }

            bindable.BindingContextChanged += OnBindingContextChanged;
        }

        protected override void OnDetachingFrom(T bindable)
        {
            base.OnDetachingFrom(bindable);
            bindable.BindingContextChanged -= OnBindingContextChanged;
            AssociatedObject = null;
        }

        void OnBindingContextChanged(object sender, EventArgs e)
        {
            OnBindingContextChanged();
        }

        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
            BindingContext = AssociatedObject.BindingContext;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms;

namespace App18
{
    public class EventToCommandBehavior : BehaviorBase<View>
    {
        Delegate eventHandler;

        public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
        public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);
        public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null);
        public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null);

        public string EventName
        {
            get { return (string)GetValue(EventNameProperty); }
            set { SetValue(EventNameProperty, value); }
        }

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        public IValueConverter Converter
        {
            get { return (IValueConverter)GetValue(InputConverterProperty); }
            set { SetValue(InputConverterProperty, value); }
        }

        protected override void OnAttachedTo(View bindable)
        {
            base.OnAttachedTo(bindable);
            RegisterEvent(EventName);
        }

        protected override void OnDetachingFrom(View bindable)
        {
            DeregisterEvent(EventName);
            base.OnDetachingFrom(bindable);
        }

        void RegisterEvent(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                return;
            }

            EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
            if (eventInfo == null)
            {
                throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
            }
            MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
            eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
            eventInfo.AddEventHandler(AssociatedObject, eventHandler);
        }

        void DeregisterEvent(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                return;
            }

            if (eventHandler == null)
            {
                return;
            }
            EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
            if (eventInfo == null)
            {
                throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
            }
            eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
            eventHandler = null;
        }

        void OnEvent(object sender, object eventArgs)
        {
            if (Command == null)
            {
                return;
            }

            object resolvedParameter;
            if (CommandParameter != null)
            {
                resolvedParameter = CommandParameter;
            }
            else if (Converter != null)
            {
                resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
            }
            else
            {
                resolvedParameter = eventArgs;
            }

            if (Command.CanExecute(resolvedParameter))
            {
                Command.Execute(resolvedParameter);
            }
        }

        static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var behavior = (EventToCommandBehavior)bindable;
            if (behavior.AssociatedObject == null)
            {
                return;
            }

            string oldEventName = (string)oldValue;
            string newEventName = (string)newValue;

            behavior.DeregisterEvent(oldEventName);
            behavior.RegisterEvent(newEventName);
        }
    }
}

在xml中

</StackLayout>

  <Entry WidthRequest="100" Text="{Binding Value1,Mode=OneWayToSource}"  Keyboard="Numeric"  >
        <Entry.Behaviors>
            <local:EventToCommandBehavior EventName="Unfocused" Command="{Binding CompletedCommand}"  />

        </Entry.Behaviors>

    </Entry>


    <Entry WidthRequest="100" Text="{Binding Value2,Mode=OneWayToSource}" Keyboard="Numeric">
        <Entry.Behaviors>
            <local:EventToCommandBehavior EventName="Unfocused" Command="{Binding CompletedCommand}"  />

        </Entry.Behaviors>

    </Entry>


    <Label WidthRequest="100" Text="{Binding Sum}"  TextColor="Black" />

</StackLayout>

在 ViewModel 中

public class MyViewModel : INotifyPropertyChanged
{


    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    double value1;

    public double Value1
    {
        get
        {
            return value1;
        }

        set
        {
            if (value1 != value)
            {
                value1 = value;
                NotifyPropertyChanged("Value1");
            }
        }
    }

    double value2;

    public double Value2
    {
        get
        {
            return value2;
        }

        set
        {
            if (value2 != value)
            {
                value2 = value;
                NotifyPropertyChanged("Value2");
            }
        }
    }




    private string sum;
    public string Sum
    {
        get { return sum; }
        set
        {
            if (sum != value)
            {
                sum = value;
                NotifyPropertyChanged("Sum");
            }
        }
    }

    public ICommand CompletedCommand { get; set; }

    public MyViewModel()
    {

        CompletedCommand = new Command(()=> {

            Sum = (Value1 + Value2).ToString();


        });
    }



}

【讨论】:

  • 很抱歉,这不是一个无效的答案,但是当我可以简单地将事件处理程序附加到我的所有Entry 控件时,为什么还要经历这样的麻烦呢?与处理 Completed 事件相比,从我的页面调用 CalculateValues 有什么好处?
  • 既然你已经使用了数据绑定,你最好在 ViewModel 中处理所有的逻辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-06
  • 2018-09-25
  • 1970-01-01
  • 2015-07-28
  • 2017-08-30
  • 2022-09-29
相关资源
最近更新 更多