Как получить доступ к событиям в дочерней ViewModel при использовании формата MVVM - PullRequest
1 голос
/ 03 апреля 2020

Я работаю с приложением в Xamarin, но хочу убедиться, что я все делаю правильно, как я go. У меня есть элемент управления Dialpad, который отображается с MainPage по <ctrl:Dialpad x:Name="dialpad" HeightRequest="600"/>. MainPage имеет BindingContext MainPageViewModel, а Dialpad имеет DialpadViewModel. У меня есть событие в DialpadViewModel, которое вызывается всякий раз, когда на цифровой клавиатуре нажимается цифровая клавиша. Для простоты эти кнопки предназначены для обновления метки на MainPage. Для этого у меня есть этот код в файле с выделенным кодом MainPage.xaml.cs.

public MainPage()
    {
        InitializeComponent();

        //add an event handler for a button pressed
        InitializeEvents();

    }

    void InitializeEvents ()
    {
        //whenever the keypad is pressed
        ((DialpadViewModel)dialpad.BindingContext).OnKeyPressed += ((MainPageViewModel)BindingContext).DialpadButtonPressed;
        ((DialpadViewModel)dialpad.BindingContext).OnDial += ((MainPageViewModel)BindingContext).DialAddress;
        ((DialpadViewModel)dialpad.BindingContext).OnBackspace += ((MainPageViewModel)BindingContext).Backspace;
    }

Я знаю, что в соответствии с форматом MVVM в коде должно быть НЕТ кода за файлом. Однако я понятия не имею, как подойти к этому иначе. Любой совет?

Ответы [ 2 ]

1 голос
/ 03 апреля 2020

Использование EventToCommandBehavior.

Обзор

Класс EventToCommandBehavior - это настраиваемое поведение Xamarin.Forms многократного использования, которое выполняет команду в ответ на any Событие стрельбы. По умолчанию аргументы события передаются в команду и могут быть дополнительно преобразованы реализацией IValueConverter.

Для использования следующих параметров поведения необходимо установить Поведение:

  • EventName - имя события, которое слушает поведение.
  • Команда - ICommand для выполнения , Ожидается, что поведение найдет экземпляр ICommand в BindingContext подключенного элемента управления, который может быть унаследован от родительского элемента.

Следующие необязательные свойства поведения могут также можно установить:

  • CommandParameter - object, который будет передан команде.
  • Преобразователь - IValueConverter реализация, которая изменит формат данных аргумента события при передаче между source и target механизмом привязки.

Примечание: EventToCommandBehavior - это пользовательский класс, который может находиться в образце EventToCommand Behavior и не является частью Xamarin.Forms.

Создание поведения

Класс EventToCommandBehavior является производным от класса BehaviorBase<T>, который, в свою очередь, является производным от класса Behavior. Целью класса BehaviorBase<T> является предоставление базового класса для любых поведений Xamarin.Forms, которые требуют, чтобы BindingContext поведения было установлено для присоединенного элемента управления. Это гарантирует, что поведение может связываться и выполнять ICommand, заданное свойством Command, при использовании поведения.

Класс BehaviorBase<T> предоставляет переопределяемый OnAttachedTo метод, который устанавливает BindingContext поведения и переопределяемый OnDetachingFrom метод, который очищает BindingContext. Кроме того, класс хранит ссылку на присоединенный элемент управления в свойстве AssociatedObject.

Реализация привязываемых свойств

Класс EventToCommandBehavior определяет четыре BindableProperty экземпляры, которые выполняют пользовательскую команду при возникновении события. Эти свойства показаны в следующем примере кода:

C#:

public class EventToCommandBehavior : BehaviorBase<View> {  
    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 { ... }
    public ICommand Command { ... }  
    public object CommandParameter { ... }
    public IValueConverter Converter { ...  }  
    ...  
}

Когда используется класс EventToCommandBehavior, свойство Command должно быть связано с данными ICommand должно быть выполнено в ответ на запуск события, определенный в свойстве EventName. Поведение будет ожидать нахождения ICommand в BindingContext подключенного элемента управления.

По умолчанию аргументы события будут переданы команде. Эти данные могут быть дополнительно преобразованы, поскольку они передаются между source и target механизмом привязки, указав реализацию IValueConverter в качестве значения свойства Converter , Кроме того, параметр можно передать в команду, указав значение свойства CommandParameter.

Реализация переопределений

Класс EventToCommandBehavior переопределяет OnAttachedTo и OnDetachingFrom методы класса BehaviorBase<T>, как показано в следующем примере кода:

C#:

public class EventToCommandBehavior : BehaviorBase<View> {
  ...  
  protected override void OnAttachedTo (View bindable) {
    base.OnAttachedTo (bindable);
    RegisterEvent (EventName);
  }

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

* Метод 1134 * выполняет настройку, вызывая метод RegisterEvent, передавая значение свойства EventName в качестве параметра. Метод OnDetachingFrom выполняет очистку, вызывая метод DeregisterEvent, передавая значение свойства EventName в качестве параметра.

Реализация функциональности поведения

Цель этого поведения - выполнить команду, определенную свойством Command, в ответ на запуск события, определенный свойством EventName. } Функциональность основного поведения показана в следующем примере кода:

C#:

public class EventToCommandBehavior : BehaviorBase<View> {
...  
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 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);
    }
  }
...
}

Метод RegisterEvent выполняется в ответ на присоединение EventToCommandBehavior к элементу управления и получает значение свойства EventName в качестве параметра , Затем метод пытается найти событие, определенное в свойстве EventName, на подключенном элементе управления. При условии, что событие может быть найдено, метод OnEvent регистрируется как метод-обработчик события.

Метод OnEvent выполняется в ответ на запуск события, определенный в EventName имущество. При условии, что свойство Command ссылается на действительный ICommand, метод пытается извлечь параметр для передачи в ICommand следующим образом:

  • Если свойство CommandParameter определяет параметр, он извлекается.
  • В противном случае, если свойство Converter определяет реализацию IValueConverter, преобразователь выполняется и преобразует данные аргумента события при передаче между source и target механизмом привязки.
  • В противном случае аргументы события считаются параметром.

Тогда привязка данных ICommand выполняется, передавая параметр команде, при условии, что метод CanExecute возвращает true.

Хотя здесь и не показано, EventToCommandBehavior также включает метод DeregisterEvent выполняется методом OnDetachingFrom. Метод DeregisterEvent используется для обнаружения и отмены регистрации события, определенного в свойстве EventName, для устранения возможных утечек памяти.

Использование поведения

Класс EventToCommandBehavior может быть прикреплен к коллекции Behaviors элемента управления, как показано в следующем примере кода XAML:

XAML:

<ListView ItemsSource="{Binding People}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextCell Text="{Binding Name}" />
        </DataTemplate>
    </ListView.ItemTemplate>
    <ListView.Behaviors>
        <local:EventToCommandBehavior EventName="ItemSelected" Command="Binding OutputAgeCommand}" Converter="{StaticResource SelectedItemConverter}" />
    </ListView.Behaviors>
</ListView>
<Label Text="{Binding SelectedItemText}" />

Эквивалентный код C# показано в следующем примере кода:

C#:

var listView = new ListView();
listView.SetBinding(ItemsView<Cell>.ItemsSourceProperty, "People");
listView.ItemTemplate = new DataTemplate(() =>
{
    var textCell = new TextCell();
    textCell.SetBinding(TextCell.TextProperty, "Name");
    return textCell;
});
listView.Behaviors.Add(new EventToCommandBehavior {
    EventName = "ItemSelected",
    Command = ((HomePageViewModel)BindingContext).OutputAgeCommand,
    Converter = new SelectedItemEventArgsToSelectedItemConverter()
});

var selectedItemLabel = new Label();
selectedItemLabel.SetBinding(Label.TextProperty, "SelectedItemText");

Свойство поведения Command - это данные, привязанные к свойству OutputAgeCommand соответствующего ViewModel, а Свойство Converter устанавливается на экземпляр SelectedItemConverter, который возвращает SelectedItem ListView из SelectedItemChangedEventArgs.

Во время выполнения поведение будет реагировать на взаимодействие с элементом управления. Когда элемент выбран в ListView, сработает событие ItemSelected, которое выполнит OutputAgeCommand в ViewModel. В свою очередь это обновляет свойство ViewModel SelectedItemText, с которым связывается Label, как показано на следующих снимках экрана:

Sample application with EventToCommandBehavior

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

Источник: Повторно используемый EventToCommandBehavior

1 голос
/ 03 апреля 2020

Это не MVVM.

У вас есть два варианта:

  1. Предоставить экземпляр DialpadViewModel из класса MainPageViewModel. Последние могут затем подписаться на события первого без какого-либо участия кода.

Элемент Dialpad в MainPage связывается со свойством DialpadViewModel MainPageViewModel:

<ctrl:Dialpad x:Name="dialpad" BindingContext="{Binding DialpadViewModel}" HeightRequest="600"/>

Убедитесь, что вы не установили явно BindingContext свойство dialpad где-либо еще.

Используйте агрегатор событий или мессенджер для связи между DialpadViewModel и MainPageViewModel в слабосвязанной форме. Это сообщение в блоге объясняет концепцию.

Короче говоря, нет прямой ссылки от одной модели вида к другой. Вместо этого они общаются друг с другом через какой-то общий объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...