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">⇩</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">⇨</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">⇩</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
。我还需要找到 UserControl
的 AncestorType
,DataContext
绑定到:
<ListBox ItemsSource="{Binding DialogueEntries}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectedDialogueEntryChoice, Mode=TwoWay}">
我正在使用 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">⇩</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">⇨</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">⇩</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
。我还需要找到 UserControl
的 AncestorType
,DataContext
绑定到:
<ListBox ItemsSource="{Binding DialogueEntries}"
SelectedItem="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectedDialogueEntryChoice, Mode=TwoWay}">