在这个假设的例子中,假设我有一个对象FooSet,它有五个属性Foo1,Foo2,Foo3 Foo4和Foo5,所有这些属性都是Foo类型,它本身有几个属性。最后,我有一个名为FooTemplate的DataTemplate,它知道如何以图形方式显示Foo类型的对象。
现在使用内置的DataGrid时,ItemsSource是FooSet对象的集合。我想要做的是设置五个模板化列,这些列都使用FooTemplate数据模板。但是,DataGrid的模板列类型不允许我设置该列的数据源(例如Foo1,Foo2等),因此我最终复制模板,每列一次,只需将Foo1.SomeProp更改为Foo2.SomeProp在模板的绑定中,这当然是荒谬的。但我为我的生活找不到怎么说'B栏使用Foo2作为它的数据源。'
这是一些Pseudo-XAML来展示我想要的......
<Resources>
<DataTemplate TargetType="Foo">
<StackPanel>
<local:FooPropAControl Value="{Binding FooPropA}" />
<local:FooPropBControl Value="{Binding FooPropB}" />
<local:FooPropCControl Value="{Binding FooPropC}" />
</StackPanel>
</DataTemplate>
</Resources>
<DataGrid ItemsSource="{Binding MyItems}" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTemplateColumn DataSource="{Binding Foo1}" />
<DataGridTemplateColumn DataSource="{Binding Foo2}" />
<DataGridTemplateColumn DataSource="{Binding Foo3}" />
<DataGridTemplateColumn DataSource="{Binding Foo4}" />
<DataGridTemplateColumn DataSource="{Binding Foo5}" />
</DataGrid.Columns>
</DataGrid>
即使我必须在列中明确指定模板,这仍然没问题。它将该列的数据源设置为FooSet的属性,因此我可以使用一个DataTemplate。所有其他列都允许您设置一些绑定。我甚至尝试将DataGridTemplateColumn子类化以添加DataSource但是没有太过分(我的猜测是因为本身没有列,而是指示如何生成行中的单元格,但这只是猜测。)
现在我知道第三方Xceed网格可以让你准确指定,但我希望有一个原生的解决方案。
那么,howzyadoodat?或者你可以吗?
中号
答案 0 :(得分:2)
好问题,我会使用ContentControl
来处理它,代码仍然有点膨胀,但它比复制整个模板更好,例如:
<DataGrid ItemsSource="{Binding EmpSets}">
<DataGrid.Resources>
<DataTemplate DataType="{x:Type obj:Employee}">
<TextBlock>
<Run Text="{Binding Name}"/>
<Run Name="RunChan" Text=" - "/>
<Run Text="{Binding Occupation}"/>
</TextBlock>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Emp1">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding Emp1}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Emp2">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding Emp2}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- ... -->
</DataGrid.Columns>
</DataGrid>
这里我在资源中使用了一个隐式DataTemplate,但你也可以通过定义&amp; amp;显式地将它作为每个ContentControl的ContentTemplate
应用。引用一个密钥,但无论如何你都知道。
准系统子类化方法:
public class DataGridTemplateMemberColumn : DataGridTemplateColumn
{
public static readonly DependencyProperty MemberPathProperty =
DependencyProperty.Register("MemberPath", typeof(string), typeof(DataGridTemplateMemberColumn), new UIPropertyMetadata(null));
public string MemberPath
{
get { return (string)GetValue(MemberPathProperty); }
set { SetValue(MemberPathProperty, value); }
}
protected override System.Windows.FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
return GenerateContent(CellEditingTemplate, dataItem);
}
protected override System.Windows.FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
return GenerateContent(CellTemplate, dataItem);
}
private FrameworkElement GenerateContent(DataTemplate template, object dataItem)
{
var contentControl = new ContentControl();
contentControl.ContentTemplate = template;
if (MemberPath != null)
{
Binding binding = new Binding(MemberPath);
binding.Source = dataItem;
contentControl.SetBinding(ContentControl.ContentProperty, binding);
}
else
{
contentControl.Content = dataItem;
}
return contentControl;
}
}
<DataGrid.Columns>
<cex:DataGridTemplateMemberColumn MemberPath="Emp1" />
<cex:DataGridTemplateMemberColumn MemberPath="Emp2" />
<cex:DataGridTemplateMemberColumn MemberPath="Emp3" />
<cex:DataGridTemplateMemberColumn MemberPath="Emp4" />
<cex:DataGridTemplateMemberColumn MemberPath="Emp5" />
</DataGrid.Columns>
答案 1 :(得分:1)
您可以在每列中使用ContentControl来提供所需的绑定:
<DataGrid ItemsSource="{Binding MyItems}" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding Foo1}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...
</DataGrid.Columns>
</DataGrid>
ContentControl是一种无外观控件,使用模板呈现其内容(默认为其DataContext)。因此,应该使用隐式DataTemplate。
答案 2 :(得分:1)
这是一个非常类似于@H.B的清理版本。建议。然而,礼仪说如果可以的话就投票给别人,即使这是我正在使用的那个,我仍然认为他被接受了。
public class DataGridTemplateMemberColumn : DataGridTemplateColumn
{
public static readonly DependencyProperty MemberPathProperty = DependencyProperty.Register(
"MemberPath",
typeof(PropertyPath),
typeof(DataGridTemplateMemberColumn),
new UIPropertyMetadata(null)
);
public PropertyPath MemberPath
{
get { return (PropertyPath)GetValue(MemberPathProperty); }
set { SetValue(MemberPathProperty, value); }
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
return LoadTemplateContent(CellEditingTemplate ?? CellTemplate, CellEditingTemplateSelector ?? CellTemplateSelector);
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
return LoadTemplateContent(CellTemplate, CellTemplateSelector);
}
private FrameworkElement LoadTemplateContent(DataTemplate template, DataTemplateSelector selector)
{
ContentPresenter target = new ContentPresenter();
target.ContentTemplate = template;
target.ContentTemplateSelector = selector;
BindingOperations.SetBinding(
target,
ContentPresenter.ContentProperty,
new Binding(){Path = MemberPath}
);
return target;
}
}
......这就是你如何使用它......
<DataGrid AutoGenerateColumns="False">
<DataGrid.Columns>
<foo:DataGridTemplateMemberColumn Header="Input" MemberPath="Input" />
<foo:DataGridTemplateMemberColumn Header="Output" MemberPath="Output" />
</DataGrid.Columns>
</DataGrid>