在DataTemplateSelector中修改基于XAML的DataTemplate

问题描述 投票:0回答:1

[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中已经定义的一种。

注2FrameworkElementFactory方法在返回之前的最后一步中将构造的模板对象分配给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,这会带来此问题。

希望它不会太无聊! :)

wpf xaml datatemplate datatemplateselector
1个回答
0
投票

想通了,比我想象的要容易。现在,我不是在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;
}
© www.soinside.com 2019 - 2024. All rights reserved.