2

この架空の例では、Foo1、Foo2、Foo3、Foo4、および Foo5 の 5 つのプロパティを持つオブジェクト FooSet があるとします。これらはすべて、それ自体がいくつかのプロパティを持つ Foo 型です。最後に、FooTemplate という DataTemplate を用意しました。これは、Foo 型のオブジェクトをグラフィカルに表示する方法を知っています。

組み込みの DataGrid を使用する場合、ItemsSource は FooSet オブジェクトのコレクションです。私がやりたいことは、すべて FooTemplate データ テンプレートを使用する 5 つのテンプレート化された列を設定することです。ただし、DataGrid のテンプレート列の種類では、その列 (Foo1、Foo2 など) のデータ ソースを設定できないため、テンプレートを複製し、列ごとに 1 回、Foo1.SomeProp を Foo2.SomeProp に変更するだけです。もちろんばかげているテンプレートのバインディングで。しかし、私は一生、「列 B は Foo2 をデータ ソースとして使用する」と言う方法を見つけることができません。

ここに私が望むものを示すための疑似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 を 1 つだけ使用できます。他のすべての列では、それを行うバインディングを設定できます。DataGridTemplateColumn をサブクラス化して DataSource を追加しようとしましたが、あまりうまくいきませんでした (私の推測では、列自体はなく、行内のセルがどのように生成されるかを指示するためですが、それは単なる推測です)。

サードパーティの Xceed グリッドで正確に指定できることはわかっていますが、ネイティブ ソリューションを期待しています。

それで、どうですか?それともできますか?

M

4

3 に答える 3

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>

ここでは、リソースで 1 つの暗黙的な 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 に答える