WPF DataGrid - 创建一个新的自定义列 [英] WPF DataGrid - Creating a new custom Column

查看:36
本文介绍了WPF DataGrid - 创建一个新的自定义列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建我自己的复选框列(替换默认的),以便稍后移动到更复杂的数据列,我有以下代码:

I am trying to create my own checkbox column (replacing the default one), in order to move to more complex data columns later-on, and I have the following code:

public class MyCheckBoxColumn : DataGridBoundColumn
{
    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        var cb = new CheckBox();
        var bb = this.Binding as Binding;
        var b = new Binding { Path = bb.Path, Source = cell.DataContext };
        cb.SetBinding(ToggleButton.IsCheckedProperty, b);
        return cb;
    }

    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
        var cb = new CheckBox();
        var bb = this.Binding as Binding;
        var b = new Binding { Path = bb.Path, Source = ToggleButton.IsCheckedProperty };
        cb.SetBinding(ToggleButton.IsCheckedProperty, b);
        return cb;
    }

    protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
    {
        var cb = editingElement as CheckBox;
        return cb.IsChecked;
    }

    protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
    {
        var cb = editingElement as CheckBox;
        if (cb != null) cb.IsChecked = (bool)uneditedValue;
    }

    protected override bool CommitCellEdit(FrameworkElement editingElement)
    {
        var cb = editingElement as CheckBox;
        BindingExpression binding = editingElement.GetBindingExpression(ToggleButton.IsCheckedProperty);
        if (binding != null) binding.UpdateSource();
        return true;// base.CommitCellEdit(editingElement);
    }
}

还有我的自定义 DataGrid:

And my custom DataGrid:

public class MyDataGrid : DataGrid
{
    protected override void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e)
    {
        try
        {
            var type = e.PropertyType;
            if (type == typeof(bool))
            {
                var col = new MyCheckBoxColumn();
                col.Binding = new Binding(e.PropertyName) {Mode = BindingMode.TwoWay};
                e.Column = col;
            }
            else
            {
                base.OnAutoGeneratingColumn(e);
            }
            var propDescr = e.PropertyDescriptor as System.ComponentModel.PropertyDescriptor;
            e.Column.Header = propDescr.Description;
        }
        catch (Exception ex)
        {
            Utils.ReportException(ex);
        }
    }
}

现在,除了两件事外,一切都很好:

Now, everything seems nice except for two things:

  • 似乎MyCheckBoxColumn 中唯一使用的方法是GenerateElement().不使用所有其他方法.我在它们中放置了断点,它们永远不会被击中......
  • 我使用 ObservableCollection 作为数据源,虽然其余列在更改时通知我,但这个没有.
  • It seems that the only used method in in MyCheckBoxColumn is the GenerateElement(). All the other methods are not used. I have put breakpoints in them and they never get hit...
  • I use an ObservableCollection as a data source and, while the rest of the columns notify me when they get changed, this one doesn't.

奇怪的是,当您选中/取消选中复选框时,bool 值会发生变化,但没有通知并且没有通过 CommitCellEdit().有谁知道这里出了什么问题?

The odd thing is that the bool value gets changed when you check/uncheck the checkbox, but without notification and without passing through CommitCellEdit(). Does anyone know what is going wrong here?

似乎如果我从 GenerateElement() 内部返回一个 TextBlock 它会使其他方法被调用(尽管通知问题没有得到修复).但是为什么这不适用于 CheckBoxes?默认复选框列是如何工作的???

It seems that if I return a TextBlock from inside GenerateElement() it makes the other methods to be called (the notification problem doesn't get fixed though). But why doesn't this work with with CheckBoxes? How does the default check box column work???

推荐答案

好的.这是自定义 CheckBox 列的完整代码.似乎,为了在 DataGrid 中将复选框之类的控件作为显示(而非编辑)元素,您必须使其命中测试不可见.或者您可以简单地使用 TextBlock 来显示一些类似于复选标记的字符:

OK. Here is the complete code for a custom CheckBox column. It seems that, in order to have a control like a checkbox as a display (not editing) element in a DataGrid you have to make it hit-test-invisible. Or you can simply use a TextBlock to display some character that resembles a checkmark:

public class MyCheckBoxColumn : DataGridBoundColumn
{
    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
    {
        var cb = new CheckBox() { IsHitTestVisible = false, HorizontalAlignment = HorizontalAlignment.Center, HorizontalContentAlignment = HorizontalAlignment.Center };
        var bb = this.Binding as Binding;
        var b = new Binding { Path = bb.Path, Source = dataItem, Mode = BindingMode.TwoWay };
        cb.SetBinding(ToggleButton.IsCheckedProperty, b);
        return cb;
//          var cb = new TextBlock() { TextAlignment = TextAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center };
//          var bb = this.Binding as Binding;
//          var b = new Binding { Path = bb.Path, Source = dataItem, Mode = BindingMode.TwoWay, Converter = new MyBoolToMarkConverter() };
//          cb.SetBinding(TextBlock.TextProperty, b);
//          return cb;
    }

    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
        var cb = new CheckBox() { HorizontalAlignment = HorizontalAlignment.Center, HorizontalContentAlignment = HorizontalAlignment.Center };
        var bb = this.Binding as Binding;
        var b = new Binding { Path = bb.Path, Source = dataItem, Mode = BindingMode.TwoWay };
        cb.SetBinding(ToggleButton.IsCheckedProperty, b);
        return cb;
    }

    protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
    {
        var cb = editingElement as CheckBox;
        if (cb != null) return cb.IsChecked;
        return false;
    }

    protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
    {
        var cb = editingElement as CheckBox;
        if (cb != null) cb.IsChecked = (bool)uneditedValue;
    }

    protected override bool CommitCellEdit(FrameworkElement editingElement)
    {
        // The following 2 lines seem to help when sometimes the commit doesn't happen (for unknown to me reasons).
        //var cb = editingElement as CheckBox;
        //cb.IsChecked = cb.IsChecked;
        BindingExpression binding = editingElement.GetBindingExpression(ToggleButton.IsCheckedProperty);
        if (binding != null) binding.UpdateSource();
        return true;// base.CommitCellEdit(editingElement);
    }
}
//--------------------------------------------------------------------------------------------
public class MyBoolToMarkConverter : IValueConverter
{
    const string cTick = "■";

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value.GetType() != typeof(bool)) return "";
        bool val = (bool)value;
        return val ? cTick : "";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value.GetType() != typeof(string)) return false;
        string val = (string)value;
        return val == cTick;
    }
}
//--------------------------------------------------------------------------------------------

这篇关于WPF DataGrid - 创建一个新的自定义列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆