Должны ли ItemSource и BindingContext быть установлены при использовании MVVM (Xamarin.Froms ListView)? - PullRequest
0 голосов
/ 08 января 2019

Модель:

public class Question : INotifyPropertyChanged
{
  private float? _answer;

  public float? Answer
  {
    get => _answer;
    set
    {
      _answer = value;
      NotifyPropertyChanged();
    }
  }

  protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }

  public event PropertyChangedEventHandler PropertyChanged;
}

Посмотреть модель:

public class QuestionViewModel
{
    private ObservableCollection<Question> _questions;

    public ObservableCollection<Question> Questions
    {
        get => _questions;
        set
        {
            if (_questions != value)
            {
                _questions = value;
            }
        }
    }
}

XAML:

<ListView x:Name="ListViewQuestions" SelectionMode="Single" HasUnevenRows="True" HeightRequest="250" VerticalOptions="FillAndExpand">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Entry x:Name="EntryAnswer" Text="{Binding Answer,Mode=TwoWay}" Keyboard="Numeric" FontSize="Medium" VerticalOptions="End" 
                               HorizontalOptions="FillAndExpand" Grid.Row="0" Grid.Column="1" >
                    <Entry.Behaviors>
                        <behaviors:EntryMaxValueBehavior MaxValue="{Binding MaxVal}" BindingContext="{Binding BindingContext, Source={x:Reference EntryAnswer}}" />
                        <behaviors:EntryMinValueBehavior MinValue="{Binding MinVal}" BindingContext="{Binding BindingContext, Source={x:Reference EntryAnswer}}" />
                    </Entry.Behaviors>
                </Entry>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

На моей странице методом OnAppearing я установил ListViewQuestions следующим образом:

var questions = await DataStore.GetQuestions(_inspection.Id);
var questionsViewModel = new QuestionViewModel { Questions = new ObservableCollection<Question>(questions) };

ListViewQuestions.ItemsSource = null;
ListViewQuestions.ItemsSource = questionsViewModel.Questions;

Однако, когда значения вводятся в EntryAnswer, установщик в модели Вопроса не вызывается, как я ожидал. Я подумал, что это может быть связано с тем, что необходимо установить BindingContext для ListView, поэтому я установил его так:

ListViewQuestions.BindingContext = questionsViewModel;

Однако сеттер в модели Вопроса по-прежнему не вызывается. Я также попытался реализовать INotifyPropertyChanged в QuestionViewModel, но все равно не радость. Я проверил, что ObservableCollection в модели представления установлен правильно, с фактическими данными, и это так. Кто-нибудь может определить, что здесь может пойти не так?

Редактировать 1: я также пытался не устанавливать ItemSource, а только устанавливать ListViewQuestions.BindingContext для модели представления, но затем ListView не заполнялся никакими данными.

Ответы [ 2 ]

0 голосов
/ 08 января 2019

В вашем наборе ответов попробуйте:

set
{
    float? temp = null;
    if(float.TryParse(value, out temp)
    {
        _answer = temp;
        NotifyPropertyChanged("Answer");
    }
}

Похоже, что это сработает, хотя ваш сеттер должен быть вызван, и вы указываете, что это не так, поэтому я думаю, что это должно быть минимальное, максимальное связывание, где это выдает ошибку. А пока, возможно, избавимся от этого и посмотрим, будет ли вызван сеттер.

В WPF использование конвертера является типичным, и я думаю, что он будет работать и с Xamarin. См. this для хорошего примера того, как реализовать IValueConverter.

0 голосов
/ 08 января 2019

Вот как это работает вместе.

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

Это означает, что когда вы установили BindingContext, все Bindings теперь начнут смотреть на объект, на который ссылается BindingContext. В вашем случае вы устанавливаете BindingContext для экземпляра QuestionViewModel.

Вы хотите, чтобы ваш ListView получил свои предметы из свойства QuestionViewModel.Questions. Итак, вы устанавливаете привязку так:

<ListView x:Name="ListViewQuestions" ItemsSource="{Binding Questions}" ...>.

Questions должно быть публичной собственностью в BindingContext, в нашем случае QuestionViewModel. Вы уже поняли это правильно.

Теперь, когда вы назначаете что-то для Questions, это также должно распространяться на ваш ListView из-за привязки.

Внутри ListView вы используете ViewCell, теперь обратите внимание, что область действия здесь меняется. Каждая ячейка представляет экземпляр объекта внутри ItemsSource. В нашем случае каждая ячейка будет содержать Question. Вы используете это:

<Entry x:Name="EntryAnswer" Text="{Binding Answer,Mode=TwoWay}" ...>

Это означает, что Answer должно быть публичной собственностью внутри Question. Вы уже поняли это правильно.

Когда вы реализуете его таким образом, в основном, единственное, что вы делаете, - заполняете модель представления и назначаете ее для BindingContext вашей страницы. Если вы используете инфраструктуру MVVM, это может произойти автоматически.

В какой-то момент вы можете столкнуться с некоторыми проблемами, когда пользовательский интерфейс не обновляется, и вам придется реализовать интерфейс INotifyPropertyChanged. Внимательно посмотрите, какой объект не обновляется на экране, и реализуйте интерфейс этого объекта вместе с необходимой сантехникой, но, как я вижу в этом коде, это сейчас не нужно. И, кроме того, вы правильно реализовали это в своем Question прямо сейчас.

Надеюсь, это имеет смысл! В первый раз немного сложно обернуть голову, но как только вы это сделаете, это довольно просто!

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