ListView 中的 UWP ListView 不遵守 INotifyPropertyChanged
UWP ListView within ListView not respecting INotifyPropertyChanged
我正在尝试创建一个 Windows 10 应用程序来提供问题列表;每个问题都显示问题文本 (TextBlock) 和可能答案列表(ListView 指向由 ToggleButton 组成的 DataTemplate)。
程序启动并显示每个 Answer/PossibleAnswer,到目前为止一切顺利。现在我想将每个问题限制为一个答案,这意味着当一个 PossibleAnswer (ToggleButton) 被选中时,该问题的其他答案必须被取消选中。
问题是,尽管我已经在 PossibleAnswer class 中实现了 INotifyPropertyChanged 并调用了适当的方法,但这并没有发生。我什至为答案 class 实现了这个,希望它能有所帮助,但没有。
我是不是在做一些明显愚蠢的事情?任何帮助将不胜感激。
注意:我正在使用 ToggleButtons,因为选中一个问题中的 RadioButton 将取消选中所有其他问题中的 RadioButtons。
我的 XAML 文件:(指向由 TextBlock(问题文本)和 ListView [可能的答案] 组成的 DataTemplate 的 ListView [对于每个问题])
<Page x:Class="QuestionsnAnswers.MainPage" ... >
<Page.Resources>
<DataTemplate x:Key="myAnswersTemplate" x:DataType="local:Answer">
<RelativePanel>
<TextBlock Text="{Binding QuestionText}"/>
<ListView Margin="0, 20 ,0 ,0"
ItemsSource="{Binding PossibleAnswers}"
ItemTemplate="{StaticResource myPossibleAnswersTemplate}">
</ListView>
</RelativePanel>
</DataTemplate>
<DataTemplate x:Key="myPossibleAnswersTemplate" x:DataType="local:PossibleAnswer">
<ToggleButton Content="{Binding PossibleAnswerText}" IsChecked="{Binding IsChecked}" Click="PossibleAnswerClicked" />
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView Name="uiQuestionsnAnswers" ItemTemplate="{StaticResource myAnswersTemplate}"/>
</Grid>
</Page>
我的 MainPage 文件:(一个页面展示了一个答案列表,每个答案都有一个可能的答案列表)
public sealed partial class MainPage : Page
{
ObservableCollection<Answer> Answers = new ObservableCollection<Answer>();
public MainPage()
{
this.InitializeComponent();
//populate example answers
Answers.Add(new Answer { AnswerId = 1, QuestionText = "Question 1" });
Answers.Add(new Answer { AnswerId = 2, QuestionText = "Question 2" });
Answers.Add(new Answer { AnswerId = 3, QuestionText = "Question 3" });
uiQuestionsnAnswers.ItemsSource = Answers;
}
private void PossibleAnswerClicked(object sender, RoutedEventArgs e) {
//get the reference to this togglebutton's PossibleAnswer and update the model.
ListViewItemPresenter parent = VisualTreeHelper.GetParent(sender as DependencyObject) as ListViewItemPresenter;
PossibleAnswer possAnswer = parent.Content as PossibleAnswer;
possAnswer.IsChecked = true;
}
}
class Answer : INotifyPropertyChanged {
public int AnswerId { get; set; }
public string QuestionText { get; set; }
public ICollection<PossibleAnswer> PossibleAnswers { get; set; }
public int ChosenPossibleAnswerId { get; set; }
public Answer() {
PossibleAnswers = new List<PossibleAnswer>();
PossibleAnswers.Add(new PossibleAnswer { ThisAnswer = this, PossibleAnswerId = 1, PossibleAnswerText = "yes" });
PossibleAnswers.Add(new PossibleAnswer { ThisAnswer = this, PossibleAnswerId = 2, PossibleAnswerText = "no" });
PossibleAnswers.Add(new PossibleAnswer { ThisAnswer = this, PossibleAnswerId = 3, PossibleAnswerText = "I don't know" });
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName = "") {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
internal void setChosenAnswer(int possibleAnswerId) {
//record this possibleAnswer as the answer to this question.
ChosenPossibleAnswerId = possibleAnswerId;
foreach (PossibleAnswer possAnswer in PossibleAnswers) {
if (possAnswer.PossibleAnswerId != ChosenPossibleAnswerId) {
possAnswer.IsChecked = false;
Debug.WriteLine("Answer " + AnswerId + " has unchecked possible answer " + possAnswer.PossibleAnswerId);
}
}
NotifyPropertyChanged();
}
}
class PossibleAnswer : INotifyPropertyChanged {
public Answer ThisAnswer { get; set; }
public int PossibleAnswerId { get; set; }
public string PossibleAnswerText { get; set; }
private bool _IsChecked = false;
public bool IsChecked {
get {
return _IsChecked;
}
set {
_IsChecked = value;
if (value == true) {
ThisAnswer.setChosenAnswer(PossibleAnswerId);
}
else {
NotifyPropertyChanged("IsChecked");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName = "") {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
要解决此问题,我们可以将 IsChecked
的 Binding Mode 设置为 TwoWay
,如下所示:
<ToggleButton Click="PossibleAnswerClicked" Content="{Binding PossibleAnswerText}" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
对于{Binding} markup extension,在大多数情况下,默认模式是单向的。它适用于只读数据。如果我们希望用户对 UI 中的值所做的更改自动推送回数据源,我们需要使用双向绑定,它适用于读写数据。如果我们在这里使用单向绑定,当用户点击切换按钮后,绑定将失效,因此您的代码将无法运行。
我正在尝试创建一个 Windows 10 应用程序来提供问题列表;每个问题都显示问题文本 (TextBlock) 和可能答案列表(ListView 指向由 ToggleButton 组成的 DataTemplate)。
程序启动并显示每个 Answer/PossibleAnswer,到目前为止一切顺利。现在我想将每个问题限制为一个答案,这意味着当一个 PossibleAnswer (ToggleButton) 被选中时,该问题的其他答案必须被取消选中。 问题是,尽管我已经在 PossibleAnswer class 中实现了 INotifyPropertyChanged 并调用了适当的方法,但这并没有发生。我什至为答案 class 实现了这个,希望它能有所帮助,但没有。
我是不是在做一些明显愚蠢的事情?任何帮助将不胜感激。
注意:我正在使用 ToggleButtons,因为选中一个问题中的 RadioButton 将取消选中所有其他问题中的 RadioButtons。
我的 XAML 文件:(指向由 TextBlock(问题文本)和 ListView [可能的答案] 组成的 DataTemplate 的 ListView [对于每个问题])
<Page x:Class="QuestionsnAnswers.MainPage" ... >
<Page.Resources>
<DataTemplate x:Key="myAnswersTemplate" x:DataType="local:Answer">
<RelativePanel>
<TextBlock Text="{Binding QuestionText}"/>
<ListView Margin="0, 20 ,0 ,0"
ItemsSource="{Binding PossibleAnswers}"
ItemTemplate="{StaticResource myPossibleAnswersTemplate}">
</ListView>
</RelativePanel>
</DataTemplate>
<DataTemplate x:Key="myPossibleAnswersTemplate" x:DataType="local:PossibleAnswer">
<ToggleButton Content="{Binding PossibleAnswerText}" IsChecked="{Binding IsChecked}" Click="PossibleAnswerClicked" />
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView Name="uiQuestionsnAnswers" ItemTemplate="{StaticResource myAnswersTemplate}"/>
</Grid>
</Page>
我的 MainPage 文件:(一个页面展示了一个答案列表,每个答案都有一个可能的答案列表)
public sealed partial class MainPage : Page
{
ObservableCollection<Answer> Answers = new ObservableCollection<Answer>();
public MainPage()
{
this.InitializeComponent();
//populate example answers
Answers.Add(new Answer { AnswerId = 1, QuestionText = "Question 1" });
Answers.Add(new Answer { AnswerId = 2, QuestionText = "Question 2" });
Answers.Add(new Answer { AnswerId = 3, QuestionText = "Question 3" });
uiQuestionsnAnswers.ItemsSource = Answers;
}
private void PossibleAnswerClicked(object sender, RoutedEventArgs e) {
//get the reference to this togglebutton's PossibleAnswer and update the model.
ListViewItemPresenter parent = VisualTreeHelper.GetParent(sender as DependencyObject) as ListViewItemPresenter;
PossibleAnswer possAnswer = parent.Content as PossibleAnswer;
possAnswer.IsChecked = true;
}
}
class Answer : INotifyPropertyChanged {
public int AnswerId { get; set; }
public string QuestionText { get; set; }
public ICollection<PossibleAnswer> PossibleAnswers { get; set; }
public int ChosenPossibleAnswerId { get; set; }
public Answer() {
PossibleAnswers = new List<PossibleAnswer>();
PossibleAnswers.Add(new PossibleAnswer { ThisAnswer = this, PossibleAnswerId = 1, PossibleAnswerText = "yes" });
PossibleAnswers.Add(new PossibleAnswer { ThisAnswer = this, PossibleAnswerId = 2, PossibleAnswerText = "no" });
PossibleAnswers.Add(new PossibleAnswer { ThisAnswer = this, PossibleAnswerId = 3, PossibleAnswerText = "I don't know" });
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName = "") {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
internal void setChosenAnswer(int possibleAnswerId) {
//record this possibleAnswer as the answer to this question.
ChosenPossibleAnswerId = possibleAnswerId;
foreach (PossibleAnswer possAnswer in PossibleAnswers) {
if (possAnswer.PossibleAnswerId != ChosenPossibleAnswerId) {
possAnswer.IsChecked = false;
Debug.WriteLine("Answer " + AnswerId + " has unchecked possible answer " + possAnswer.PossibleAnswerId);
}
}
NotifyPropertyChanged();
}
}
class PossibleAnswer : INotifyPropertyChanged {
public Answer ThisAnswer { get; set; }
public int PossibleAnswerId { get; set; }
public string PossibleAnswerText { get; set; }
private bool _IsChecked = false;
public bool IsChecked {
get {
return _IsChecked;
}
set {
_IsChecked = value;
if (value == true) {
ThisAnswer.setChosenAnswer(PossibleAnswerId);
}
else {
NotifyPropertyChanged("IsChecked");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName = "") {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
要解决此问题,我们可以将 IsChecked
的 Binding Mode 设置为 TwoWay
,如下所示:
<ToggleButton Click="PossibleAnswerClicked" Content="{Binding PossibleAnswerText}" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
对于{Binding} markup extension,在大多数情况下,默认模式是单向的。它适用于只读数据。如果我们希望用户对 UI 中的值所做的更改自动推送回数据源,我们需要使用双向绑定,它适用于读写数据。如果我们在这里使用单向绑定,当用户点击切换按钮后,绑定将失效,因此您的代码将无法运行。