2

在这个假设的例子中,假设我有一个对象 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 但并没有走得太远(我的猜测是因为本身没有列,而是决定了如何生成行中的单元格,但这只是一个猜测。)

现在我知道第 3 方 Xceed 网格可以让您准确指定,但我希望有一个本地解决方案。

那么,怎么样?或者你可以吗?

4

3 回答 3

2

好问题,我会使用 a 来处理它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,但您也可以ContentTemplate通过定义和引用一个键将其显式应用为每个 ContentControl 的,但无论如何您都知道。


准系统子类化方法:

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>
于 2011-05-02T06:47:54.903 回答
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。

于 2011-05-02T06:52:53.013 回答
1

这是与@HB 建议的极其相似的东西的清理版本。但是,SO 礼节说尽可能投票给其他人,所以即使这是我正在使用的那个,我仍然投票给他接受。

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>
于 2011-05-02T08:18:29.410 回答