Могу ли я установить источник данных для DataGridTemplateColumn во встроенном WPF DataGrid? - PullRequest
2 голосов
/ 02 мая 2011

В этом гипотетическом примере представьте, что у меня есть объект FooSet, имеющий пять свойств Foo1, Foo2, Foo3 Foo4 и Foo5, каждый из которых имеет тип Foo, который сам имеет несколько свойств. Наконец, у меня есть DataTemplate с именем FooTemplate, который знает, как графически отображать объекты типа Foo.

Теперь при использовании встроенной DataGrid ItemsSource представляет собой набор объектов FooSet. Что я хочу сделать, это настроить пять шаблонных столбцов, которые все используют шаблон данных FooTemplate. Тем не менее, тип столбца шаблона DataGrid не позволяет мне установить источник данных для этого столбца (например, Foo1, Foo2 и т. Д.), Поэтому я заканчиваю дублирование шаблона, один раз для каждого столбца, просто меняя 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. Все остальные столбцы позволяют вам установить некоторую привязку, которая делает это. Я даже пытался создать подкласс DataGridTemplateColumn, чтобы добавить DataSource, но не зашел слишком далеко (мое предположение состоит в том, что столбца как такового нет, а, скорее, он определяет, как генерируются ячейки в строках, но это только предположение.)

Теперь я знаю, что сторонняя сетка Xceed позволяет вам указать именно это, но я надеюсь на собственное решение.

Итак, как устроится? Или ты можешь?

M

Ответы [ 3 ]

2 голосов
/ 02 мая 2011

Хороший вопрос, я бы подошел к нему, используя 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, определяя и ссылаясь на ключ, но вы все равно это знаете.


Подклассификация Barebone:

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 голос
/ 02 мая 2011

Это очищенная версия чего-то очень похожего на то, что @ 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>
1 голос
/ 02 мая 2011

Вы можете использовать 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...