10

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

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);
    }
}

还有我的自定义数据网格:

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);
        }
    }
}

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

  • 似乎 in 中唯一使用的方法MyCheckBoxColumnGenerateElement(). 不使用所有其他方法。我在它们中设置了断点,它们永远不会被击中......
  • 我使用 anObservableCollection作为数据源,而其余列在更改时会通知我,而这一列没有。

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

编辑 :

似乎如果我TextBlock从内部返回 aGenerateElement()它会调用其他方法(虽然通知问题没有得到解决)。但是为什么这不适用于 CheckBoxes?默认复选框列如何工作???

4

1 回答 1

7

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

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;
    }
}
//--------------------------------------------------------------------------------------------
于 2013-07-07T08:59:59.097 回答