WPF,TreeView,绑定到集合中对象内部的属性
WPF, TreeView, Bind to properties inside object in Collection
这可能不难,但我越来越糊涂了。
我想在我的视图中显示一个 TreeView,但我无法正确进行数据绑定。
TemplateFamilyList 集合是根对象。它将包含一个 TemplateFamily 对象列表。这些 TemplateFamily 对象中的每一个都将包含一个 Template 对象和一个 TemplateParameter 对象列表,称为 TemplateParameterMembers。实际的模板 class 和实际的模板参数 class 在其他属性中都有一个名称 属性。
所以,在我的 ViewModel class 中,我有这个填充集合的方法:
public void LoadTemplateTree()
{
TemplateFamilyList = new ObservableCollection<TemplateFamily>();
List<Template> templateList = BuilderService.GetAllTemplates();
List<TemplateParameter> templatesParametersList;
foreach (Template template in templateList)
{
templatesParametersList = BuilderService.GetATemplatesParameters(template.TemplateID);
TemplateFamilyList.Add(new TemplateFamily(template, new ObservableCollection<TemplateParameter>(templatesParametersList)));
}
}
这是 TemplateFamily class:
public class TemplateFamily
{
public Template Template { get; set; }
public ObservableCollection<TemplateParameter> TemplateParameterMembers { get; set; }
public TemplateFamily(Template aTemplate, ObservableCollection<TemplateParameter> templatesParameters)
{
Template = aTemplate;
TemplateParameterMembers = templatesParameters;
}
}
我想要的是一个 TreeView,例如:
- NameOfTemplateA
NameOfTemplateB
- NameOfTemplateParameterB1
- NameOfTemplateParameterB2
等等
在我的 XAML 中,我有这个:(但它不起作用,它给出了一些只有箭头但没有名称的空白树视图)。
<TabItem Header="Template Files" Height="22" VerticalAlignment="Top">
<TreeView Height="Auto" Name="treeView1" Width="Auto" ItemsSource="{Binding TemplateFamilyList}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding TemplateParameterMembers}" >
<TextBlock Text="{Binding Path=Name}" Margin="2" ></TextBlock>
</HierarchicalDataTemplate >
</TreeView.ItemTemplate>
</TreeView>
</TabItem>
ViewModel 使用 ViewModelLocator 绑定到 View,在其他情况下它工作正常。我知道如果我绑定到 TemplateFamilyList,它将获得正确的对象,我只是不知道如何在该列表中显示每个 TemplateFamily 的名称(应该是 Name 属性 of the Template 属性 TemplateFamily 对象)然后对于树中的下一层,如果有 TemplateParameterMembers 列表,如何显示 TemplateParameterMembers 集合中每个项目的 TemplateParameter 属性 的名称 属性。
我想我可以尝试在 TemplateFamily class 中使用另一个 属性 名称,然后在构造函数中简单地复制模板的名称 属性,例如:
public TemplateFamily(Template aTemplate, ObservableCollection<TemplateParameter> templatesParameters)
{
Template = aTemplate;
Name = aTemplate.Name;
TemplateParameterMembers = templatesParameters;
}
但我不喜欢这种方法,此外,我没有专门的 "container" class 来收集模板参数,所以我仍然需要以某种方式向下钻取并在 TemplateParameterMembers 集合的每一项中获取 TemplateParameter 的名称 属性。
我看到了这个post
似乎在答案中,Gishu 使用了 DataType 属性,例如:
DataType="{x:Type local:Group}
本地名称空间在 OP post 中定义为:
xmlns:local="clr-namespace:TaskManager.Domain;assembly=TaskManager.Domain"
但是,在我的例子中,Template 和 TemplateParameter classes 是我模型的一部分,它们甚至与 ViewModel 和 View 不在同一个项目中。
就我的本地命名空间而言,我有这个:
xmlns:local="clr-namespace:S.VisualStudioExtension"
local:ViewModelLocator.AutoWireViewModel="True"
然后我是否应该为 Template 和 TemplateParameter class 添加一个命名空间,例如:
xmlns:domain="clr-namespace:S.ModelDomain"
并尝试在 DataType 属性中使用它?
我也怀疑我的 XAML 有问题,也许我应该有另一个 Hierarchical DataTemplate?
在这种情况下,最好和最通用(在我看来)的解决方案是为将在树中显示的每种类型的项目定义一个模板,并将它们放入树的资源字典中。每个模板应使用 DataType
属性 与相应的项目类型相关联。这样,将根据项目的类型自动选择合适的模板来呈现每个项目。假设您的 TemplateParameter
class 在命名空间 S.ModelDomain
中名为 S.ModelDomain
的项目中定义,并且 local
前缀在您的 XAML 中正确定义,这应该会给你预期的结果(我冒昧地删除了你的 XAML 不相关的属性):
<TreeView xmlns:models="clr-namespace:S.ModelDomain;assembly=S.ModelDomain"
ItemsSource="{Binding TemplateFamilyList}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:TemplateFamily}"
ItemsSource="{Binding TemplateParameterMembers}" >
<TextBlock Text="{Binding Template.Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type models:TemplateParameter}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
尽管在 XAML 的根元素(通常是 Window
或 UserControl
上定义名称空间映射是常见的做法。在此处查看更多信息:XAML Namespaces and Namespace Mapping for WPF XAML.
请注意,模板没有指定 x:Key
参数,尽管已放入资源字典中。在自动模板选择过程中需要考虑模板。这种模板称为隐式模板。这是一个有用的 link:Data Templating Overview.
这可能不难,但我越来越糊涂了。
我想在我的视图中显示一个 TreeView,但我无法正确进行数据绑定。
TemplateFamilyList 集合是根对象。它将包含一个 TemplateFamily 对象列表。这些 TemplateFamily 对象中的每一个都将包含一个 Template 对象和一个 TemplateParameter 对象列表,称为 TemplateParameterMembers。实际的模板 class 和实际的模板参数 class 在其他属性中都有一个名称 属性。
所以,在我的 ViewModel class 中,我有这个填充集合的方法:
public void LoadTemplateTree()
{
TemplateFamilyList = new ObservableCollection<TemplateFamily>();
List<Template> templateList = BuilderService.GetAllTemplates();
List<TemplateParameter> templatesParametersList;
foreach (Template template in templateList)
{
templatesParametersList = BuilderService.GetATemplatesParameters(template.TemplateID);
TemplateFamilyList.Add(new TemplateFamily(template, new ObservableCollection<TemplateParameter>(templatesParametersList)));
}
}
这是 TemplateFamily class:
public class TemplateFamily
{
public Template Template { get; set; }
public ObservableCollection<TemplateParameter> TemplateParameterMembers { get; set; }
public TemplateFamily(Template aTemplate, ObservableCollection<TemplateParameter> templatesParameters)
{
Template = aTemplate;
TemplateParameterMembers = templatesParameters;
}
}
我想要的是一个 TreeView,例如:
- NameOfTemplateA
NameOfTemplateB
- NameOfTemplateParameterB1
- NameOfTemplateParameterB2
等等
在我的 XAML 中,我有这个:(但它不起作用,它给出了一些只有箭头但没有名称的空白树视图)。
<TabItem Header="Template Files" Height="22" VerticalAlignment="Top">
<TreeView Height="Auto" Name="treeView1" Width="Auto" ItemsSource="{Binding TemplateFamilyList}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding TemplateParameterMembers}" >
<TextBlock Text="{Binding Path=Name}" Margin="2" ></TextBlock>
</HierarchicalDataTemplate >
</TreeView.ItemTemplate>
</TreeView>
</TabItem>
ViewModel 使用 ViewModelLocator 绑定到 View,在其他情况下它工作正常。我知道如果我绑定到 TemplateFamilyList,它将获得正确的对象,我只是不知道如何在该列表中显示每个 TemplateFamily 的名称(应该是 Name 属性 of the Template 属性 TemplateFamily 对象)然后对于树中的下一层,如果有 TemplateParameterMembers 列表,如何显示 TemplateParameterMembers 集合中每个项目的 TemplateParameter 属性 的名称 属性。
我想我可以尝试在 TemplateFamily class 中使用另一个 属性 名称,然后在构造函数中简单地复制模板的名称 属性,例如:
public TemplateFamily(Template aTemplate, ObservableCollection<TemplateParameter> templatesParameters)
{
Template = aTemplate;
Name = aTemplate.Name;
TemplateParameterMembers = templatesParameters;
}
但我不喜欢这种方法,此外,我没有专门的 "container" class 来收集模板参数,所以我仍然需要以某种方式向下钻取并在 TemplateParameterMembers 集合的每一项中获取 TemplateParameter 的名称 属性。
我看到了这个post 似乎在答案中,Gishu 使用了 DataType 属性,例如:
DataType="{x:Type local:Group}
本地名称空间在 OP post 中定义为:
xmlns:local="clr-namespace:TaskManager.Domain;assembly=TaskManager.Domain"
但是,在我的例子中,Template 和 TemplateParameter classes 是我模型的一部分,它们甚至与 ViewModel 和 View 不在同一个项目中。 就我的本地命名空间而言,我有这个:
xmlns:local="clr-namespace:S.VisualStudioExtension"
local:ViewModelLocator.AutoWireViewModel="True"
然后我是否应该为 Template 和 TemplateParameter class 添加一个命名空间,例如:
xmlns:domain="clr-namespace:S.ModelDomain"
并尝试在 DataType 属性中使用它?
我也怀疑我的 XAML 有问题,也许我应该有另一个 Hierarchical DataTemplate?
在这种情况下,最好和最通用(在我看来)的解决方案是为将在树中显示的每种类型的项目定义一个模板,并将它们放入树的资源字典中。每个模板应使用 DataType
属性 与相应的项目类型相关联。这样,将根据项目的类型自动选择合适的模板来呈现每个项目。假设您的 TemplateParameter
class 在命名空间 S.ModelDomain
中名为 S.ModelDomain
的项目中定义,并且 local
前缀在您的 XAML 中正确定义,这应该会给你预期的结果(我冒昧地删除了你的 XAML 不相关的属性):
<TreeView xmlns:models="clr-namespace:S.ModelDomain;assembly=S.ModelDomain"
ItemsSource="{Binding TemplateFamilyList}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:TemplateFamily}"
ItemsSource="{Binding TemplateParameterMembers}" >
<TextBlock Text="{Binding Template.Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type models:TemplateParameter}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
尽管在 XAML 的根元素(通常是 Window
或 UserControl
上定义名称空间映射是常见的做法。在此处查看更多信息:XAML Namespaces and Namespace Mapping for WPF XAML.
请注意,模板没有指定 x:Key
参数,尽管已放入资源字典中。在自动模板选择过程中需要考虑模板。这种模板称为隐式模板。这是一个有用的 link:Data Templating Overview.