Как в Xamarin следует использовать поведение и команды для инициирования действия в ViewModel?

#c# #xamarin #xamarin.forms #data-binding

#c# #xamarin #xamarin.forms #привязка данных

Вопрос:

Короче говоря, мне удалось подключить действие, которое будет запущено в моей ViewModel после события с элемента управления в моем представлении, используя поведение и команды, но в итоге это привело к обходному пути для ошибки в фреймворке, которая заставляет меня сомневаться, является ли это вообще правильным подходом для такого рода проблем.

В частности, в моей ViewModel выполняются некоторые вычисления, которые я хочу выполнить после того, как пользователь завершит обновление элемента управления вводом в моем представлении. Я решил, что это должно зависнуть от завершенного события. Для достижения этой цели я настроил свой код следующим образом;

EntryCommand.cs

 namespace SocialStocksCross.Command
{
    public class EntryCommand : ICommand
    {
        public Action _callback;

        public EntryCommand()
        {
        }

        public bool CanExecute(object test)
        {
            return true;
        }
        public void Execute(object test)
        {
            _callback();
        }

        public event EventHandler CanExecuteChanged;
    }
}
 

MainPageViewModel.cs

 public class MainPageViewModel : INotifyPropertyChanged
{
    public MainPageViewModel()
    {
        CommandVM = new EntryCommand();
        CommandVM._callback = CalculateStuff;
        UpdateLabel();
    }
    private EntryCommand _command;

    public EntryCommand CommandVM
    {
        get
        {
            return _command;
        }
        set
        {
            _command = value;
        }
    }
    
    public void CalculateStuff()
    {
      // does stuff
    }
}
 

Мой класс поведения был определен следующим образом;

 namespace SocialStocksCross.Behaviors
{
    class EntryBehavior : Behavior<Entry>
    {
        public static readonly BindableProperty CommandProperty =
               BindableProperty.Create("Command", typeof(ICommand), typeof(EntryBehavior), null);

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

        protected override void OnAttachedTo(Entry bindable)
        {
            base.OnAttachedTo(bindable);

            bindable.Completed  = OnEntryCompleted;
            // Perform setup
        }

        protected override void OnDetachingFrom(Entry bindable)
        {
            base.OnDetachingFrom(bindable);
            // Perform clean up
        }

        void OnEntryTextChanged(object sender, TextChangedEventArgs e)
        {
            string oldText = e.OldTextValue;
            string newText = e.NewTextValue;
        }

        void OnEntryCompleted(object sender, EventArgs args)
        {
            if (BindingContext == null)
            {
                return;
            }
            // do something
            if (Command == null)
            {
                return;
            }

            Command.Execute(args);
        }
    }
}
 

И соответствующая часть моего представления была такой;

 <Entry Grid.Column="1" Grid.Row="0" x:Name="companySymbol" Text="{Binding CompanySymbol}">
            <Entry.Behaviors>
                <localbehaviors:EntryBehavior Command="{Binding CommandVM, Mode=OneWayToSource}"></localbehaviors:EntryBehavior>
            </Entry.Behaviors>
        </Entry>
 

Насколько я мог судить из того, что я мог прочитать в Интернете, казалось, что это должно сработать, но команда для поведения никогда не была привязана к команде в ViewModel. После некоторого изучения оказалось, что BindingContext команды всегда был нулевым, возможно, из-за этой ошибки, хотя, поскольку он помечен как закрытый, я бы подумал, что это было исправлено…
https://github.com/xamarin/Xamarin .Forms/issues/2581

В итоге мне пришлось вручную установить BindingContext поведения в файле .cs для наследования от собственного BindingContext представления и настроить привязку таким образом. И хотя мой код теперь работает, если это не было исправлено в фреймворке, я должен сомневаться в том, есть ли какой-то другой способ обработки подобных вещей, и вся эта привязка поведения просто не рекомендуется?

Комментарии:

1. вы читали проблему, на которую ссылались? Они объясняют, почему этот BindingContext имеет значение null для поведения и как его обойти. Они не собираются это исправлять, потому что это было конкретное дизайнерское решение, а не ошибка.

2. Тот факт, что Стефан Делькруа неоднократно извиняется, сожалеет о принятых решениях и выражает желание скрыться, заставляет думать, что это не было конкретным решением… или, по крайней мере, не тот, которым они довольны. Если это так, как это должно работать, конечно, это отвечает на мой вопрос, я думаю

3. @P.Райт Привет, если вы нашли ответ, вы можете обновить его в ответе, когда у вас будет время.