使用MVVM(Xamarin.Froms ListView)时是否应该同时设置ItemSource和BindingContext?

时间:2019-01-08 15:18:53

标签: mvvm xamarin.forms

型号:

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中时,正如我期望的那样,不调用Question模型中的设置器。我认为这可能是因为需要设置ListView的BindingContext,所以我将其设置如下:

ListViewQuestions.BindingContext = questionsViewModel;

但是,问题模型中的设置器仍未调用。我也尝试在QuestionViewModel中实现INotifyPropertyChanged,但仍然没有乐趣。我检查了视图模型中的ObservableCollection是否已正确设置,并带有实际数据。任何人都可以发现这里可能出了什么问题吗?

编辑1:我也尝试不设置ItemSource,而只是将ListViewQuestions.BindingContext设置为视图模型,但是ListView中没有任何数据。

2 个答案:

答案 0 :(得分:0)

这是它们一起工作的方式。

BindingContext是该对象,它将成为页面或其子项中任何绑定的作用域,除非您为某个子对象指定了不同的上下文,但现在不要让事情变得过于复杂。 / p>

这意味着,当您设置了BindingContext之后,所有Bindings都将开始查找BindingContext中引用的对象。您需要将BindingContext设置为QuestionViewModel的实例。

您希望您的ListViewQuestionViewModel.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框架,则可能会自动发生。

在某些时候,您可能会遇到UI无法更新的麻烦,您将必须实现INotifyPropertyChanged接口。仔细查看一下哪些对象不会在屏幕上更新,并在该对象上实现接口以及所需的管道,但是从我在这段代码中所看到的,现在不需要了。此外,您现在已经在自己的Question中以正确的方式实现了它。

我希望这有道理!第一次缠头有点困难,但是一旦摆动一下,就很容易了!

答案 1 :(得分:0)

在答案设置器中,尝试:

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

虽然必须调用您的setter,但似乎要使它起作用,并且您指出它不是,所以我认为必须是最小,最大绑定,才能消除错误。现在也许可以摆脱它,看看设置器是否会被调用。

在WPF中,通常使用转换器,我认为Xamarin也可以使用。有关如何实现IValueConverter的好示例,请参见this