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));
        }
    }
}

要解决此问题,我们可以将 IsCheckedBinding Mode 设置为 TwoWay,如下所示:

<ToggleButton Click="PossibleAnswerClicked" Content="{Binding PossibleAnswerText}" IsChecked="{Binding IsChecked, Mode=TwoWay}" />

对于{Binding} markup extension,在大多数情况下,默认模式是单向的。它适用于只读数据。如果我们希望用户对 UI 中的值所做的更改自动推送回数据源,我们需要使用双向绑定,它适用于读写数据。如果我们在这里使用单向绑定,当用户点击切换按钮后,绑定将失效,因此您的代码将无法运行。