DataGridTemplateColumn的自定义控件

时间:2014-07-08 22:41:11

标签: c# wpf custom-controls dependency-properties

我目前正在尝试从DataGridTemplateColumn创建一个自定义控件,该控件将在我们的许多应用程序中重用。我遇到了一些问题,在自定义控件上获取依赖属性以进行绑定并正确引发属性更改通知。

我目前拥有从DataGridTemplateColumn继承的控件xaml如下所示:

<DataGridTemplateColumn x:Class="Controls.DataGridDateColumn"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Text="{Binding SelectedDate}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <Grid FocusManager.FocusedElement="{Binding ElementName=DatePicker}">
                <DatePicker Name="DatePicker" HorizontalAlignment="Left" VerticalAlignment="Center" SelectedDate="{Binding SelectedDate}"/>
            </Grid>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>

背后的代码看起来像这样

public partial class DataGridDateColumn : DataGridTemplateColumn
{

    public static readonly DependencyProperty SelectedDateProperty = 
        DependencyProperty.Register("SelectedDate", 
        typeof(DateTime?), 
        typeof(DataGridDateColumn),
        new FrameworkPropertyMetadata(null, OnSelectedDateChanged));

    private static void OnSelectedDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        DataGridDateColumn col = (DataGridDateColumn)d;
        col.SelectedDate = (DateTime?)e.NewValue;
    }

    public DateTime? SelectedDate {
        get { 
            return (DateTime?)GetValue(SelectedDateProperty); 
        }
        set { 
            SetValue(SelectedDateProperty, value);                
        }
    }      

    public DataGridDateColumn()
    {
        InitializeComponent();            
    }

}

当我在主页面上的数据网格中有控件并尝试绑定到SelectedDate时,如<Controls:DataGridDateColumn Header="Policy Date" SelectedDate="{Binding Path=PolicyDate}" SortMemberPath="PolicyDate" />

我在输出窗口中收到一个绑定错误,指出它找不到我所指的依赖属性

System.Windows.Data Error: 40 : BindingExpression path error: 'SelectedDate' property not found on 'object' ''TestData' (HashCode=32071430)'. BindingExpression:Path=SelectedDate; DataItem='TestData' (HashCode=32071430); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

我最初的想法是,因为这是一个项目控件,我需要以不同的方式注册依赖项属性,但我找不到任何其他信息。

我尝试创建自定义列的原因是因为我们计划将特定行为与几种不同类型的列相关联,以便在所有应用中使用户体验更加同质。所以我希望能够处理自定义控件内部的行为,这样我们就不必在使用它的每个数据网格上不断地将不同的事件连接到模板。

任何建议都将不胜感激。

2 个答案:

答案 0 :(得分:8)

在与此问题斗争了很长一段时间后,我最终创建了一个自定义控件,该控件继承自DataGridBoundColumn程序集中的PresentationFramework。这比尝试正确绑定模板属性要好得多。我相信它们没有绑定,因为列模板不是可视树的一部分。基于我在框架代码中看到的情况,看起来发生的是绑定被传递到生成的单元格。因此传播绑定的唯一真正方法是使用某种代理对象来获取数据并将其映射到依赖属性。非常hacky。

我建议未来的用户查看Microsoft的参考源上的DataGridTextColumn代码。并为自己的用途构建类似的东西。

我最终继承了许多自定义控件的DataGridBound列。需要注意的关键方法是GenerateEditingElementGenerateElementPrepareCellForEdit。它们都是事件处理程序,允许您操作单元格的表示以及附加绑定和事件处理程序。

这里的示例是我的自定义DataGridDateColumn中的一大块代码,因为没有内置的代码,我想要一个具有特定行为的可重用版本:

    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
        DatePicker dp = new DatePicker();
        dp.Name = "datePicker";
        CustomControlHelper.ApplyBinding(dp, DatePicker.SelectedDateProperty, this.Binding);
        dp.PreviewKeyDown += DatePicker_OnPreviewKeyDown;
        return (FrameworkElement)dp;
    }

    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        TextBlock tb = new TextBlock();
        CustomControlHelper.ApplyBinding(tb, TextBlock.TextProperty, this.Binding);            
        cell.TextInput += OnCellTextInput;            
        return (FrameworkElement)tb;
    }

    protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
    {
        DatePicker picker = editingElement as DatePicker;
        DateTime newValue = DateTime.Today;

        if (picker != null)
        {
            DateTime? dt = picker.SelectedDate;
            if (dt.HasValue)
            {
                newValue = dt.Value;
            }

        }            

        picker.Focus();
        return newValue;
    }       

答案 1 :(得分:0)

这里的消息显示,这看起来像一个类型转换问题,你可以做的是你可以创建一个valueconverter并可以在需要时手动应用,它会将绑定值转换为你想要的特定类型,并将服务器你的目的。