WPF и Prism - привязка к управляемому TemplateSelector событию IsSelected ListBoxItem не запускается в связанном коде ViewModel - PullRequest
0 голосов
/ 18 марта 2020

Я использую Prism и WPF. Вот мой ViewModel:

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 не вызывает свойство SelectedDialogueEntryChoice моего ViewModel:

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

Я читал, что, возможно, дерево поглощает событие нажатия, но я еще не выяснил, как убедиться, что событие выбранного элемента работает

Просто чтобы убедиться, что я ничего не пропустил это может быть важно, вот класс DataTemplateSelector, который я использую:

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

Ответы на вопросы в комментариях:

Q: Что означает «не вызывает свойство»?
A: Следующий метод установки не запускается при изменении выбранного элемента.

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

        BreadCrumbs.Add(value);
    }
}

Например, SelectedDialogueEntry метод set срабатывает

Вещи, которые я пытаюсь:

Я пытался привязать элемент UserControl моего Xaml, который, я надеялся, имеет доступ в DataContext и, следовательно, мой ViewModel ... Но безрезультатно:

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

Вещи, которые я узнал:

Если я возьму код из селектора шаблонов, SelectedDialogueEntryChoice работает. Поэтому я предполагаю, что это связано с потерей доступа к ViewModel

1 Ответ

0 голосов
/ 19 марта 2020

Хитрость заключалась в том, чтобы связываться напрямую с DataContext. Мне также нужно было найти AncestorType из UserControl, с которым DataContext связан:

<ListBox ItemsSource="{Binding DialogueEntries}"
         SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectedDialogueEntryChoice, Mode=TwoWay}">
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...