在DataTemplate
中返回DataTemplateSelector
之前是否有办法对其进行修改?
我的DataTemplate
是在XAML中定义的。我需要为此模板设置绑定中的一个元素,但是其绑定路径仅在运行时确定。模板如下所示:
<DataTemplate DataType="vm:FormField">
<StackPanel>
<ComboBox ItemsSource="{Binding ValueList.DefaultView}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Mode=OneWay}" /> <!--This is the problem child-->
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
TextBlock.Text
需要将其绑定路径设置为将由基础数据项提供的属性。我的DataTemplateSelector
使用以下代码为其分配新路径:
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
//MultiValueTemplate is the above template
var Content = MultiValueTemplate.LoadContent() as StackPanel;
var ComboItemText = (Content.Children[0] as ComboBox).ItemTemplate.LoadContent() as TextBlock;
//Underlying item contains the field name that I want this to bind to.
ComboItemText.SetBinding(TextBlock.TextProperty, (item as MyItemVM).FieldName);
return MultiValueTemplate;
}
这不起作用。代码运行,但是输出未设置TextProperty
绑定。我需要更改/添加什么?
注意:我已经使用FrameworkElementFactory
方法解决了这个问题,但是我不得不在代码中重新定义整个DataTemplate
(即使是简单的模板,这也是很痛苦的事情就像上面的那个)。我想使用XAML中已经定义的代码。
注释2 :FrameworkElementFactory
方法在返回之前的最后一步中将构造的模板对象分配给DataTemplate.VisualTree
。我认为这是我所缺少的那部分,但是由于VisualTree
要求的对象是FrameworkElementFactory
类型的对象,因此没有办法做到这一点,而在使用基于XAML的模板时我们没有这个类型。
我们基本上是从服务器端获取如下所示的JSON结构:
`[
"Person":
{
"Name": "Peter",
"Score": 53000
},
"Person":
{
"Name": "dotNET",
"Score": 24000
}
,...
]
服务器将确定JSON中将包括哪些字段。需要我们的应用程序解析此JSON,然后显示与字段一样多的ComboBox。然后,每个组合框都将在其中列出一个字段。因此,在上面的示例中,名称将具有一个组合,而分数将具有一个组合。用户可以从第一个或第二个ComboBox中选择一个选项,但是从一个组合中选择将自动从其他组合中选择相应的项目。
现在您可能会问,到底是谁设计了这个愚蠢的UI?不幸的是,我们既不知道也不控制这个决定。我要求客户端改为使用 ONE (而不是很多)与DataGrid作为下拉菜单,以便我们可以在每个网格行中显示一个数据项,用户可以选择这些项目之一。清晰而简单。但是管理层不同意,在这里我们试图模仿同步组合框。大声笑。
因此,我们目前正在做的是即时将传入的JSON转换为DataTable
。此DataTable为每个JSON字段获取一列,并与它们的项一样多。可以说是一种枢轴。然后,我们创建ComboBoes并将每个绑定到此DataTable的单个字段。该字段名称当然是动态的,并且是在运行时确定的,这意味着我必须在运行时修改DataTemplate
,这引发了这个问题。
希望它不会太无聊! :)
答案 0 :(得分:1)
看起来您可以将SelectedValuePath
和DisplayMemberPath
绑定到FieldName并完成此操作:
<ComboBox SelectedValuePath="{Binding RelativeSource={RelativeSource AncestorType=ComboBox}, Path=DataContext.FieldName}"
DisplayMemberPath="{Binding RelativeSource={RelativeSource AncestorType=ComboBox}, Path=DataContext.FieldName}"/>
答案 1 :(得分:0)
注意 :对于以后的读者,正如@ASh在其回答中提到的,DisplayMemberPath
是DependencyProperty
,可以是用于绑定到动态字段名称。此答案中的解决方案将针对该特定问题进行过度设计。我仍将其保留在此处,因为它在某些绑定可能不够的其他情况下很有用。
弄明白了,比我想的要容易。现在,我不是在DataTemplateSelector
中修改模板,而是在运行时使用Behavior
来修改绑定路径。这是行为:
public class DynamicBindingPathBehavior : Behavior<TextBlock>
{
public string BindingPath
{
get { return (string)GetValue(BindingPathProperty); }
set { SetValue(BindingPathProperty, value); }
}
public static readonly DependencyProperty BindingPathProperty =
DependencyProperty.Register("BindingPath", typeof(string), typeof(DynamicBindingPathBehavior),
new FrameworkPropertyMetadata(null, (sender, e) =>
{
var Behavior = (sender as DynamicBindingPathBehavior);
Behavior.AssociatedObject.SetBinding(TextBlock.TextProperty, new Binding(Behavior.BindingPath));
}));
}
这是我必须在XAML模板中进行的修改:
<DataTemplate DataType="vm:FormField">
<StackPanel>
<ComboBox ItemsSource="{Binding ValueList.DefaultView}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Mode=OneWay}">
<e:Interaction.Behaviors>
<local:DynamicBindingPathBehavior BindingPath="{Binding RelativeSource={RelativeSource AncestorType=ComboBox}, Path=DataContext.FieldName, Mode=OneWay}" />
</e:Interaction.Behaviors>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
从现在开始,一切都很好。
另一种方法是在DataTemplateSelector
中以编程方式创建模板。如果您想走这条路,这里是在SelectTemplate
函数中的用法概述:
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var DT = new DataTemplate();
FrameworkElementFactory stackpanelElement = new FrameworkElementFactory(typeof(StackPanel), "stackpanel");
FrameworkElementFactory comboboxElement = new FrameworkElementFactory(typeof(ComboBox), "combobox");
comboboxElement.SetBinding(ComboBox.ItemsSourceProperty, new Binding() { Path = new PropertyPath("ValueList.DefaultView") });
comboboxElement.SetBinding(ComboBox.SelectedItemProperty, new Binding() { Path = new PropertyPath("Value") });
var ItemTemplate = new DataTemplate();
FrameworkElementFactory textblockElement2 = new FrameworkElementFactory(typeof(TextBlock), "textblock2");
textblockElement2.SetBinding(TextBlock.TextProperty, new Binding() { Path = new PropertyPath(YOUR_BINDING_PROPERTY_PATH) });
ItemTemplate.VisualTree = textblockElement2;
comboboxElement.SetValue(ComboBox.ItemTemplateProperty, ItemTemplate);
stackpanelElement.AppendChild(comboboxElement);
DT.VisualTree = stackpanelElement;
return MultiValueTemplate;
}