【问题标题】:Custom Control for DataGridTemplateColumnDataGridTemplateColumn 的自定义控件
【发布时间】:2014-08-29 19:51:02
【问题描述】:

我目前正在尝试使用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 &lt;Controls:DataGridDateColumn Header="Policy Date" SelectedDate="{Binding Path=PolicyDate}" SortMemberPath="PolicyDate" /&gt;

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

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')

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

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

任何建议将不胜感激。

【问题讨论】:

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


    【解决方案1】:

    在解决这个问题很长时间后,我最终创建了一个自定义控件,该控件继承自 PresentationFramework 程序集中的 DataGridBoundColumn。这比尝试正确绑定模板属性要好得多。我相信它们没有约束力,因为列模板不是可视树的一部分。根据我在框架代码中看到的内容,看起来绑定被传递到生成的单元格。因此,传播绑定的唯一真正方法是使用某种代理对象来绑定数据,并将该绑定映射到依赖属性。非常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;
        }       
    

    【讨论】:

    • 我遇到了您在此处发布的完全相同的问题。您愿意分享您在解决方案中使用的一些代码吗?
    • 基本上我试图将标题过滤器添加到数据网格列。所以我只是覆盖了基本类型 DataGridTextColumn、DataGridDCheckBoxColumn 和 DataGridComboBoxColumn。我还构建了自己的 DataGridDateColumn 因为没有内置的 datepicker 列。如果您查看上面帖子中提供的链接,您可以看到 MS 如何实现它的列,您可以简单地覆盖它并挂钩到公开的事件。需要注意的关键方法是 GenerateEditingElementGenerateElementPrepareCellForEdit 事件处理程序。
    【解决方案2】:

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

    【讨论】:

    • 我想知道为什么我必须使用类型转换器?我在 SelectedDate 中指定了我正在寻找的路径,但 WPF 将该行的整个对象绑定到它。为什么要这样做?它应该只是将 PolicyDate 从 TestData 对象提供给自定义控件不应该吗?这里是 TestData 对象。 public class TestData { public string PolicyNumber { get; set; } public bool IsValid { get; set; } public string Name { get; set; } public DateTime PolicyDate { get ; 设置; } }
    • 这里错误显示你已经为你的数据模板使用了文本块。它有属性 text 和 text 是字符串类型的对象,你必须将你的类型 System.DateTime 转换为 System.String 或 System.string 类型.请试试这个
    • 如果我必须为绑定到列的每种类型都有一个转换器,那么这完全违背了自定义控件的目的。我最终放弃了这个冒险,只是创建了一个继承自 WPF 框架中的 DataGridBoundColumn 的自定义控件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-26
    • 1970-01-01
    相关资源
    最近更新 更多