型号:
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中没有任何数据。
答案 0 :(得分:0)
这是它们一起工作的方式。
BindingContext
是该对象,它将成为页面或其子项中任何绑定的作用域,除非您为某个子对象指定了不同的上下文,但现在不要让事情变得过于复杂。 / p>
这意味着,当您设置了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框架,则可能会自动发生。
在某些时候,您可能会遇到UI无法更新的麻烦,您将必须实现INotifyPropertyChanged
接口。仔细查看一下哪些对象不会在屏幕上更新,并在该对象上实现接口以及所需的管道,但是从我在这段代码中所看到的,现在不需要了。此外,您现在已经在自己的Question
中以正确的方式实现了它。
我希望这有道理!第一次缠头有点困难,但是一旦摆动一下,就很容易了!
答案 1 :(得分:0)
在答案设置器中,尝试:
set
{
float? temp = null;
if(float.TryParse(value, out temp)
{
_answer = temp;
NotifyPropertyChanged("Answer");
}
}
虽然必须调用您的setter,但似乎要使它起作用,并且您指出它不是,所以我认为必须是最小,最大绑定,才能消除错误。现在也许可以摆脱它,看看设置器是否会被调用。
在WPF中,通常使用转换器,我认为Xamarin也可以使用。有关如何实现IValueConverter的好示例,请参见this。