为在代码中创建的DataGridTemplateColumn添加数据绑定

时间:2012-12-22 07:17:04

标签: wpf datatemplate datagridtemplatecolumn

问题:

有没有办法在XAML中定义DataTemplate并在代码中实例化它(而不是通过FindResource检索单例)并在发送到{{1}之前修改其VisualTree是必需的,例如DataTemplate

背景:

我在DataGridTemplateColumn.CellTemplate中通过添加data[][]列来显示二维数组DataGrid,并且在XAML中定义了DataGridTemplateColumn,知道如何呈现数组中的每个元素。但是,每个单元格的默认DataTemplate是行,即DataContext。所以我需要通过将根视觉元素的data[x]设置为绑定DataTemplate来“DataContext为每个列”参数化“"[y]",其中y是列索引。目前,DataTemplate定义为DataGrid.Resources并由FindResource()检索,每次都返回相同的实例。除了调用LoadContent()之外,我还会在UIElement本身上加载VisualTree而不是DataTemplate。我正在寻找一种方法来实例化代码中的DataTemplate,进行所需的修改并设置为DataGridTemplateColumn.CellTemplate

3 个答案:

答案 0 :(得分:7)

受Sisyphe的回答启发,我发现了这种更便携的解决方案:

public class DataGridBoundTemplateColumn : DataGridTemplateColumn
{
    public string BindingPath { get; set; }

    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
        var element = base.GenerateEditingElement(cell, dataItem);
        element.SetBinding(ContentPresenter.ContentProperty, new Binding(this.BindingPath));
        return element;
    }

    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        var element = base.GenerateElement(cell, dataItem);
        element.SetBinding(ContentPresenter.ContentProperty, new Binding(this.BindingPath));
        return element;
    }
}

用法:

var cellTemplate = (DataTemplate)this.dataGrid.FindResource("cellTemplate");
foreach (var c in data.Columns)
{
    var col = new DataGridBoundTemplateColumn
    {
        Header = c.HeaderText,
        CellTemplate = cellTemplate,
        BindingPath = string.Format("[{0}]", c.Index)
    };

    this.dataGrid.Columns.Add(col);
}

希望这可以帮助那些与我的问题具有相同要求的人。

答案 1 :(得分:2)

您应该在WPF中看到DataTemplate作为工厂。因此,我认为您并不真正需要DataTemplate的新实例,您只是希望根据您的上下文对其进行不同的应用。

如果我理解你的问题,问题是你的DataContext单元格的DataGrid不正确:它是行视图模型,而你希望它是Cell ViewModel(这很有意义) )。然而,这是DataGrid的基本行为,可能与每个行中的单元格由DataGridCellsPresenter(基本上是ItemsControl)保持这一事实有关,其ItemsSource依赖属性具有没有设置(因此解释了不好DataContext)。

我遇到了这个问题并找到了解决这个问题的两种方法(但我只设法做了一个工作)。

第一个是子类DataGridCellsPresenter并覆盖OnItemChanged方法手动设置ItemsSource。

protected override void OnItemChanged(object oldItem, object newItem)
{
    var rowViewModel = newItem as ViewModel;
    if (rowViewModel != null)
    {
        ItemsSource = rowViewModel.Items;
    }
    else
    {
        ItemsSource = null;
    }
}

其中rowViewModel.Items应该指向类似data [x]的东西。但是,我使用此修复程序遇到了一些麻烦,无法使其正常工作。

第二个解决方案是子类DataGridCell并在更改ColumnProperty时更新dataContext。您还必须继承DataGridCellsPresenter以使其创建正确的单元格控件

public class MyDataGridCell : DataGridCell
{
    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        if (e.Property == ColumnProperty)
        {
            var viewModel = DataContext as YourViewModelType;
            if (viewModel != null)
            {
                var column = (e.NewValue as DataGridTemplateColumn);
                if (column != null)
                {
                    var cellViewModel = viewModel[column.DisplayIndex];
                    DataContext = cellViewModel;
                }
            }
        }
        base.OnPropertyChanged(e);
    }
}

public class MyDataGridCellsPresenterControl : DataGridCellsPresenter
{
    protected override System.Windows.DependencyObject GetContainerForItemOverride()
    {
        return new MyDataGridCell();
    }
}

最后,您还必须覆盖DataGridRow默认ControlTemplate,以使其使用自定义DataGridCellsPresenter代替原始DataGridCellsPresenter

<ControlTemplate x:Key="DataGridRowControlTemplate" TargetType="{x:Type DataGridRow}">
    <Border x:Name="DGR_Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <SelectiveScrollingGrid>
            <SelectiveScrollingGrid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </SelectiveScrollingGrid.ColumnDefinitions>
            <SelectiveScrollingGrid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </SelectiveScrollingGrid.RowDefinitions>
            <local:MyDataGridCellsPresenter Grid.Column="1" ItemsPanel="{TemplateBinding ItemsPanel}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            <DataGridDetailsPresenter Grid.Column="1" Grid.Row="1" Visibility="{TemplateBinding DetailsVisibility}">
                <SelectiveScrollingGrid.SelectiveScrollingOrientation>
                    <Binding Path="AreRowDetailsFrozen" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}">
                        <Binding.ConverterParameter>
                            <SelectiveScrollingOrientation>Vertical</SelectiveScrollingOrientation>
                        </Binding.ConverterParameter>
                    </Binding>
                </SelectiveScrollingGrid.SelectiveScrollingOrientation>
            </DataGridDetailsPresenter>
            <DataGridRowHeader Grid.RowSpan="2" SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical">
                <DataGridRowHeader.Visibility>
                    <Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}">
                        <Binding.ConverterParameter>
                            <DataGridHeadersVisibility>Row</DataGridHeadersVisibility>
                        </Binding.ConverterParameter>
                    </Binding>
                </DataGridRowHeader.Visibility>
            </DataGridRowHeader>
        </SelectiveScrollingGrid>
    </Border>
</ControlTemplate>

答案 2 :(得分:1)

(templateKey as DataTemplate).LoadContent()

说明 当您致电LoadContent时,UIElement中的DataTemplate个对象已创建,您可以将其添加到另一个UIElement的可视树中。