WPF 和 Prism - 绑定到 TemplateSelector 驱动的 ListBoxItem 的 IsSelected 事件未在 ViewModel 绑定代码中触发

WPF and Prism - Binding to TemplateSelector driven ListBoxItem's IsSelected event is not firing in ViewModel bound code

我正在使用 Prism 和 WPF。这是我的视图模型:

using DialogueTool.Service.Abstracts;
using DialogueTool.Service.Models.Concretes;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace DialogueTool.UI.ViewModels
{
    public class DialogueEntryControlViewModel : BindableBase, INavigationAware
    {
        private IServices services;
        private DialogueWrapperModel dialogueWrapperModel;

        public DialogueEntryControlViewModel(IServices _services)
        {
            services = _services;
        }

        DialogueWrapperModel DialogueWrapperModel { get; set; }

        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            dialogueWrapperModel = navigationContext.Parameters.GetValue<DialogueWrapperModel>("DialogueWrapperModel");
            DialogueEntries = new ObservableCollection<DialogueEntryModel>(dialogueWrapperModel.DialogueEntries);
        }

        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            //throw new NotImplementedException();
            return true;
        }

        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
            //throw new NotImplementedException();
        }

        #region Properties

        private ObservableCollection<DialogueEntryModel> _breadCrumbs = new ObservableCollection<DialogueEntryModel>();
        public ObservableCollection<DialogueEntryModel> BreadCrumbs
        {
            get { return _breadCrumbs; }
            set { SetProperty(ref _breadCrumbs, value); }
        }

        /// <summary>
        /// 
        /// </summary>
        public ObservableCollection<DialogueEntryModel> _dialogueEntries = new ObservableCollection<DialogueEntryModel>();
        public ObservableCollection<DialogueEntryModel> DialogueEntries
        {
            get { return _dialogueEntries; }
            set{ SetProperty(ref _dialogueEntries, value); }
        }

        private DialogueEntryModel _selectedDialogueEntry;
        public DialogueEntryModel SelectedDialogueEntry
        {
            get { return _selectedDialogueEntry; }
            set { SetProperty(ref _selectedDialogueEntry, value); }
        }

        private DialogueEntryModel _selectedDialogueEntryChoice;
        public DialogueEntryModel SelectedDialogueEntryChoice
        {
            get { return _selectedDialogueEntryChoice; }
            set { 
                SetProperty(ref _selectedDialogueEntryChoice, value);

                BreadCrumbs.Add(value);
            }
        }
        #endregion

        #region Commands
        #endregion
    }
}

这是我的 xaml:

<UserControl x:Class="DialogueTool.UI.Views.Controls.DialogueEntryControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:templateSelector="clr-namespace:DialogueTool.UI.DataTemplateSelectors">

    <UserControl.Resources>

        <DataTemplate x:Key="QuestionDataTemplateTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                    <ColumnDefinition Width="auto"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>

                <TextBlock Grid.Column="0"
                           Grid.Row="0"
                           TextWrapping="Wrap"
                           VerticalAlignment="Center"
                           Text="{Binding Text, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBlock>

                <TextBlock Grid.Column="1"
                           Grid.Row="0"
                           FontSize="25"
                           FontWeight="Bold"
                           VerticalAlignment="Center">&#x21E9;</TextBlock>

                <TreeView Grid.ColumnSpan="2"
                          Grid.Column="0"
                          Grid.Row="1"
                          Visibility="{Binding Path=DialogueEntries, Converter={StaticResource EmptyListToVisibilityConverter} }">

                    <TreeViewItem Header="Choices"
                                  IsExpanded="{Binding Path=DialogueEntries, Converter={StaticResource EmptyListToBoolConverter} }">
                        <StackPanel Width="160">

                            <!--<Label>Search</Label>
                                    <TextBox Text="{Binding ChapterSearchText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />-->

                            <!---->
                            <Label>Choices</Label>
                            <ListBox ItemsSource="{Binding DialogueEntries}"
                                     SelectedItem="{Binding Path=SelectedDialogueEntryChoice, Mode=TwoWay}">

                                <ListBox.ItemTemplate>
                                    <DataTemplate>
                                        <Grid Width="146">
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="*"></ColumnDefinition>
                                                <ColumnDefinition Width="auto"></ColumnDefinition>
                                            </Grid.ColumnDefinitions>
                                            <Grid.RowDefinitions>
                                                <RowDefinition></RowDefinition>
                                            </Grid.RowDefinitions>

                                            <TextBlock Grid.Column="0"
                                                       Grid.Row="0"
                                                       MaxWidth="150"
                                                       TextWrapping="Wrap"
                                                       VerticalAlignment="Center"
                                                       Text="{Binding Text, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBlock>

                                            <TextBlock Grid.Column="1"
                                                       Grid.Row="0"
                                                       FontSize="25"
                                                       FontWeight="Bold"
                                                       VerticalAlignment="Center">&#x21E8;</TextBlock>
                                        </Grid>
                                    </DataTemplate>
                                </ListBox.ItemTemplate>
                            </ListBox>
                        </StackPanel>
                    </TreeViewItem>
                </TreeView>
            </Grid>
        </DataTemplate>

        <DataTemplate x:Key="TextDataTemplateTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                    <ColumnDefinition Width="auto"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>

                <TextBlock Grid.Column="0"
                           Grid.Row="0"
                           TextWrapping="Wrap"
                           VerticalAlignment="Center"
                           Text="{Binding Text, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBlock>

                <TextBlock Grid.Column="1"
                           Grid.Row="0"
                           FontSize="25"
                           FontWeight="Bold"
                           VerticalAlignment="Center">&#x21E9;</TextBlock>

            </Grid>
        </DataTemplate>

        <templateSelector:DialogueEntryDataTemplateSelector x:Key="DialogueEntryDataTemplateSelector"
                                                            x:Name="DialogueEntryDataTemplateSelector"
                                                            TextTemplate="{StaticResource TextDataTemplateTemplate}"
                                                            QuestionTemplate="{StaticResource QuestionDataTemplateTemplate}" />
    </UserControl.Resources>

    <StackPanel>
        <StackPanel FlowDirection="LeftToRight"
                    Orientation="Horizontal">
            <ItemsControl ItemsSource="{Binding BreadCrumbs}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Excerpt}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
        <!---->
        <Label>Dialogue Entries</Label>
        <ListBox ItemsSource="{Binding DialogueEntries}"
                 SelectedItem="{Binding SelectedDialogueEntry, Mode=TwoWay}"
                 ItemTemplateSelector="{StaticResource DialogueEntryDataTemplateSelector}">
        </ListBox>

    </StackPanel>
</UserControl>

以下绑定到 SelectedDialogueEntryChoice 不会触发我的 ViewModel 的 SelectedDialogueEntryChoice 属性:

<ListBox ItemsSource="{Binding DialogueEntries}"
         SelectedItem="{Binding Path=SelectedDialogueEntryChoice, Mode=TwoWay}">

我读到可能是 Tree 正在吸收点击事件,但我还没有想出如何确保所选项目事件有效

为了确保我不会遗漏任何可能重要的内容,这是我正在使用的 DataTemplateSelector class:

using DialogueTool.Domain.Enums;
using DialogueTool.Service.Models.Concretes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace DialogueTool.UI.DataTemplateSelectors
{
    public class DialogueEntryDataTemplateSelector : DataTemplateSelector
    {
        public DataTemplate QuestionTemplate { get; set; }
        public DataTemplate TextTemplate { get; set; }
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            var dialogueEntryModel = item as DialogueEntryModel;

            switch (dialogueEntryModel.Type)
            {
                case DialogueEntryType.Question:
                    return QuestionTemplate;
                case DialogueEntryType.Text:
                    return TextTemplate;
                default:
                    return TextTemplate;
            }
        }
    }
}

回复评论中的问题:

问:"does not trigger the property"是什么意思?
A:下面的set方法在选中项改变时不会触发

private DialogueEntryModel _selectedDialogueEntryChoice;
public DialogueEntryModel SelectedDialogueEntryChoice
{
    get { return _selectedDialogueEntryChoice; }
    set { 
        SetProperty(ref _selectedDialogueEntryChoice, value);

        BreadCrumbs.Add(value);
    }
}

例如,SelectedDialogueEntry set 方法确实被触发了

我正在尝试的事情:

我试图绑定到我的 Xaml 的 UserControl 元素,我希望它可以访问 DataContext 并因此访问我的 ViewModel...但无济于事:

<ListBox ItemsSource="{Binding DialogueEntries}"
         SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectedDialogueEntryChoice}">

我发现的事情:

如果我从模板选择器中取出代码,SelectedDialogueEntryChoice 就会起作用。所以我假设这与失去对 ViewModel

的访问有关

诀窍是直接绑定到 DataContext。我还需要找到 UserControlAncestorTypeDataContext 绑定到:

<ListBox ItemsSource="{Binding DialogueEntries}"
         SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectedDialogueEntryChoice, Mode=TwoWay}">