Wpf 在生成的字段中设置ContentPresenter ContentTemplate的值更改

Wpf 在生成的字段中设置ContentPresenter ContentTemplate的值更改,wpf,Wpf,我正在尝试构建一个树状视图,其中: 1.TreeViewItems由我的模型中的列表生成。 2.每个TreeViewItem都包含一个ComboBox和一个动态元素,我希望根据ComboBox中选择的值更改其模板 这是我当前的xaml代码 <Window x:Class="MyTestWPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:

我正在尝试构建一个树状视图,其中: 1.TreeViewItems由我的模型中的列表生成。 2.每个TreeViewItem都包含一个ComboBox和一个动态元素,我希望根据ComboBox中选择的值更改其模板

这是我当前的
xaml
代码

<Window x:Class="MyTestWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyTestWPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:NodeTypeToTemplateConverter x:Key="NodeTypeToTemplateConverter"/>
        <DataTemplate x:Key="Template1">
            <TextBlock Text="Template 1" />
        </DataTemplate>
        <DataTemplate x:Key="Template2">
            <TextBlock Text="Template 2" />
        </DataTemplate>
        <Style x:Key="MyNodeTemplate" TargetType="ContentPresenter">
            <Setter Property="ContentTemplate" Value="{StaticResource Template1}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=NodeType}">
                    <DataTrigger.Value>
                        <local:NodeTypesEnum>Type1</local:NodeTypesEnum>
                    </DataTrigger.Value>
                    <Setter Property="ContentTemplate" Value="{Binding Converter={StaticResource NodeTypeToTemplateConverter}}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
        <HierarchicalDataTemplate DataType="{x:Type local:MyTreeNode}"
                              ItemsSource="{Binding Nodes}">
            <StackPanel Orientation="Horizontal">
                <ComboBox ItemsSource="{Binding Path=GetAvailableNodeType}"
                      SelectedItem="{Binding Path=NodeType}" />
                <ContentPresenter Style="{StaticResource MyNodeTemplate}" Content="{Binding}" />
            </StackPanel>
        </HierarchicalDataTemplate>
    </Window.Resources>
    <TreeView x:Name="MyTree" ItemsSource="{Binding MyTreeModel}" />
</Window>
树节点类型:

namespace MyTestWPF
{
    public class MyTreeNode
    {
        public string Name { get; set; }
        public NodeTypesEnum NodeType { get; set; }
        public MyTreeNode[] Nodes { get; set; }
        public NodeTypesEnum[] GetAvailableNodeType()
        {
            return new NodeTypesEnum[] { NodeTypesEnum.Type1, NodeTypesEnum.Type2 };
        }
    }

    public enum NodeTypesEnum
    {
        Type1 = 0,
        Type2 = 1
    }
}
转换器(
NodeTypeToTemplateConverter
)接收整个ViewModel,并根据模型中的值返回相关模板的名称

using System;
using System.Globalization;
using System.Windows.Data;

namespace MyTestWPF
{
    public class NodeTypeToTemplateConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if((value as MyTreeNode).NodeType == NodeTypesEnum.Type1)
            {
                return "Template1";
            } else
            {
                return "Template2";
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
问题是上述代码导致堆栈溢出异常。TreeView中的第一项无休止地调用
NodeTypeToTemplateConverter
的Convert方法

我认为这与
DataTrigger.Value
有关。将该值设置为不同于默认值的值
节点类型
允许页面加载而不会溢出,但当任何
组合框
设置为
节点类型1
时,堆栈溢出

我试图简单地删除
DataTrigger.Value
元素,但这会导致根本无法调用转换器


如何根据相邻组合框选择的值动态构建模板名称?

您可能希望使用DataTemplateSelector而不是转换器

public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate Template1 { get; set; }
    public DataTemplate Template2 { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        //Logic to select template based on 'item' value.
        if (item == <template1Value>) return Template1;  //TODO: replace <template1Value>
        else if (item == <template2Value>) return Template2;  //TODO: replace <template2Value>
        else return new DataTemplate();
    }
}

<local:ComboBoxItemTemplateSelector x:Key="ComboBoxItemTemplateSelector">
    <local:ComboBoxItemTemplateSelector.Template1>
        <DataTemplate>
            <TextBlock Text="" />
        </DataTemplate>
    </local:ComboBoxItemTemplateSelector.Template1>
    <local:ComboBoxItemTemplateSelector.Template2>
        <DataTemplate>
            <TextBlock Text="" />
        </DataTemplate>
    </local:ComboBoxItemTemplateSelector.Template2>
</local:ComboBoxItemTemplateSelector>

<ContentPresenter Content="{Binding NodeType}" ContentTemplateSelector="{StaticResource ComboBoxItemTemplateSelector}"/>
公共类ComboBoxItemTemplateSelector:DataTemplateSelector
{
公共数据模板模板1{get;set;}
公共数据模板模板2{get;set;}
公共覆盖数据模板SelectTemplate(对象项,DependencyObject容器)
{
//基于“项”值选择模板的逻辑。
if(item==)返回Template1;//TODO:替换
else if(item==)返回Template2;//TODO:替换
否则返回新的DataTemplate();
}
}
我还没有完全测试这段代码,所以如果您有任何问题,请告诉我

编辑:

模板选择器仅在内容更改时执行,因此如果使用{Binding},则无法执行。解决方法是将DataTemplate内容绑定到父级的DataContext

<DataTemplate>
    <TextBlock Text="" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContentPresenter}}"/>
</DataTemplate>


如果此解决方法不可接受,还有其他方法可以解决此问题。

请在提出问题时提供答案,以便获得更好的帮助。@mm8抱歉。我已经在重现问题的地方完整地添加了代码的其余部分。感谢您的评论。因此,如果我设置了
Content=“{Binding NodeType}”
但留下了另一个问题,那么这会对所选组合框项目中的更改起作用并作出反应。在我的示例中,我设置了
Content=“{Binding}”
,因为加载的模板需要完整的MyTreeNode DataContext对象。按照您的示例,只传递NodeType属性。我想我应该在编辑答案之前询问,您是需要在模板选择器中使用整个对象,还是在数据模板中使用DataTemplate?在数据模板中使用
<DataTemplate>
    <TextBlock Text="" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContentPresenter}}"/>
</DataTemplate>