[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中选择一个选项,但是从一个组合中选择将自动从其他组合中选择相应的项目。
现在您可能会问,到底是谁设计了这个愚蠢的用户界面?不幸的是,我们既不知道也不控制这个决定。我要求客户端改为使用ONE Combo(而不是很多),并使用DataGrid作为其下拉列表,以便我们可以在每个网格行中显示一个data item,并且用户可以选择其中之一。清晰而简单。但是管理层不同意,在这里我们试图模仿synchronized comboboxes。大声笑。
所以我们当前正在做的是将传入的JSON即时转换为DataTable
。此DataTable为每个JSON字段获取一列,并与它们的项一样多。可以说是一种枢轴。然后,我们创建ComboBoes并将每个绑定到此DataTable的单个字段。该字段名称当然是动态的,并且是在运行时确定的,这意味着我必须在运行时修改DataTemplate
,这会带来此问题。
希望它不会太无聊! :)
想通了,比我想象的要容易。现在,我不是在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;
}