WPF中DataTemplateSelector仅触发一次,更新InputMode时不生效问题
问题分析
你的核心问题是DataTemplateSelector仅在ContentControl首次绑定执行时触发一次,后续KeyViewModel.InputMode变更时,Selector不会重新评估并切换模板。根本原因是:
- ContentControl的
Content绑定的是MainViewModel实例,只要这个实例本身不变,WPF不会因为其内部嵌套属性(StreamDeck.SelectedKey.InputMode)的变化而重新调用模板选择器。 - 模板选择器的触发逻辑依赖于
Content属性的实例变更,而非其内部属性的变更。
解决方案
方案1:绑定到直接触发变更的属性(推荐)
将ContentControl的Content直接绑定到StreamDeck.SelectedKey.InputMode,这样当InputMode实例切换时,Content属性会更新,自动触发模板选择器:
1. 修改ContentControl绑定
<ContentControl Content="{Binding StreamDeck.SelectedKey.InputMode}" ContentTemplateSelector="{StaticResource FromModeTemplateSelector}" />
2. 简化模板选择器逻辑
public class FromModeTemplateSelector : DataTemplateSelector { public DataTemplate OpeningModeTemplate { get; set; } = new(); public DataTemplate ShortcutModeTemplate { get; set; } = new(); public override DataTemplate SelectTemplate(object item, DependencyObject container) { return item switch { OpeningMode => OpeningModeTemplate, ShortcutMode => ShortcutModeTemplate, _ => base.SelectTemplate(item, container) }; } }
3. 调整模板内的绑定路径
此时模板的DataContext是InputMode实例,需要通过RelativeSource回溯到Window的DataContext(即MainViewModel):
<!-- 示例:修改ComboBox的绑定 --> <ComboBox DockPanel.Dock="Top" ItemsSource="{Binding DataContext.StreamDeck.SelectedKey.ActionChoices, RelativeSource={RelativeSource AncestorType=Window}}" SelectedIndex="{Binding DataContext.StreamDeck.SelectedKey.SelectedActionIndex, RelativeSource={RelativeSource AncestorType=Window}}" />
方案2:手动触发模板重新选择
如果必须保持Content绑定到MainViewModel,可以通过监听InputMode的变更,强制ContentControl重新评估模板:
1. 在MainViewModel中添加触发属性
public InputModeBase TriggerTemplateRefresh => StreamDeck?.SelectedKey?.InputMode; public MainViewModel() { // 订阅SelectedKey的变更事件 StreamDeck.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(StreamDeckViewModel.SelectedKey)) { // 订阅新SelectedKey的InputMode变更事件 StreamDeck.SelectedKey.PropertyChanged += SelectedKey_InputModeChanged; OnPropertyChanged(nameof(TriggerTemplateRefresh)); } }; } private void SelectedKey_InputModeChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(KeyViewModel.InputMode)) { OnPropertyChanged(nameof(TriggerTemplateRefresh)); } }
2. 绑定触发属性并监听变更
<ContentControl x:Name="ModeContentControl" Content="{Binding}" ContentTemplateSelector="{StaticResource FromModeTemplateSelector}" Tag="{Binding TriggerTemplateRefresh}" />
在MainWindow.xaml.cs中添加监听逻辑:
private void ModeContentControl_TagChanged(object sender, DependencyPropertyChangedEventArgs e) { var control = sender as ContentControl; control.ContentTemplate = control.ContentTemplateSelector.SelectTemplate(control.Content, control); }
额外注意事项
- 确保
StreamDeckViewModel的SelectedKey属性在变更时触发PropertyChanged事件,否则切换不同Key时模板选择器也不会响应。 - 使用CommunityToolkit.Mvvm的
[ObservableProperty]特性可以简化属性实现,减少手动编写SetProperty的代码,降低出错概率。
内容的提问来源于stack exchange,提问作者Philipp Feldmann FV Feldmann
相关产品推荐
相关产品推荐

