[Silverlight]简单实现DataGrid使用CheckBox选择行

以前写过几个方法实现这个功能,但最终还是选择了不继承DataGrid,所以再重新发布出来。

先显示最终结果,其中左边是错误的例子,右边才是正确的

在DataGrid中使用CheckBox选择行时典型的错误就是CheckBox没有Binding到任何属性上,这样的话当拖动滚动条时CheckBox.IsChecked就会乱掉,如Demo中左边那个DataGrid所示。最直观的解决方法是禁用DataGrid的滚动条,或者在绑定的数据上添加一个用于绑定CheckBox的bool属性。
其实只要在DataGrid.LoadingRow事件中将CheckBox的DataContext设定为另外一个Object,就不需要牺牲DataGrid的高效能,也不需要改变原有数据的结构。最终的实现方法是自定义一个包含DataGrid的DataGridTemplateColumn,而不需要重写DataGrid,这样的坏处就是每次调用需要多写一局代码,如果不想这样就继承DataGrid然后把这句代码写进去。在DataGrid.Columns中加入自定义的Column,然后在代码中关联DataGrid和这个Colum,就完成了:

 

 <sdk:DataGrid ItemsSource="{Binding}"
                      AutoGenerateColumns
="False"
                      Grid.Column
="1"
                      x:Name
="TestDataGrid">
            <sdk:DataGrid.Columns>
                <local:DataGridSelectColumn />
                <sdk:DataGridTextColumn Header="Name"
                                        Binding
="{Binding Name}"
                                        Width
="*" />
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>

 

   TestDataGrid.EnableSelect();

 

下面是这个DataGridSelectColumn的具体实现。创建一个UserControl然后把它改成DataGridTemplateColumn,这样好处是DataTemplate和Style中的FrameworkElement都可以轻松地获取。

 

<sdk:DataGridTemplateColumn xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
                            x:Class
="DataGridSelectSample.DataGridSelectColumn"
                            xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                            xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
                            xmlns:d
="http://schemas.microsoft.com/expression/blend/2008"
                            xmlns:mc
="http://schemas.openxmlformats.org/markup-compatibility/2006"
                            mc:Ignorable
="d" 
                            d:DesignHeight
="300"
                            d:DesignWidth
="400">
    <sdk:DataGridTemplateColumn.HeaderStyle>
        <Style TargetType="ContentControl">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <CheckBox  Content="Select All"
                                   VerticalContentAlignment
="Center"
                                   VerticalAlignment
="Center"
                                   Loaded
="OnHeaderCheckBoxLoaded"/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </sdk:DataGridTemplateColumn.HeaderStyle>
    <sdk:DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay}"
                      VerticalAlignment
="Center"
                      HorizontalAlignment
="Center" />
        </DataTemplate>
    </sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>

 

 

public partial class DataGridSelectColumn : DataGridTemplateColumn
    {

        private DataGrid _ownerDataGrid;
        private CheckBox _headerCheckBox;
        private Dictionary<object, MarkObject> _markObjects;

        public event EventHandler SelectedItemChanged;

        public DataGridSelectColumn()
        {
            InitializeComponent();
            IsReadOnly = true;
            _markObjects = new Dictionary<object, MarkObject>();
        }

        public DataGrid OwnerDataGrid
        {
            get { return _ownerDataGrid; }
            set
            {
                _ownerDataGrid = value;
                _ownerDataGrid.LoadingRow += OnLoadingRow;
            }
        }

        public void SelecteAll()
        {
            if (_headerCheckBox != null)
                _headerCheckBox.IsChecked = true;
            SetAllSelectedStates(true);
        }

        public void UnselectAll()
        {
            if (_headerCheckBox != null)
                _headerCheckBox.IsChecked = false;
            SetAllSelectedStates(false);
        }

        public List<T> GetSelectedItems<T>()
        {
            List<T> result = new List<T>();
            if (_ownerDataGrid.ItemsSource != null)
            {
                var enu = _ownerDataGrid.ItemsSource.GetEnumerator();
                while (enu.MoveNext())
                {
                    if (GetMarkObject(enu.Current).IsSelected)
                        result.Add((T)enu.Current);
                }
            }
            ClearItems();
            return result;
        }

        public void SetSelectedItems(IList items)
        {
            if (_ownerDataGrid.ItemsSource == null)
                return;

            var enu = _ownerDataGrid.ItemsSource.GetEnumerator();
            while (enu.MoveNext())
            {
                GetMarkObject(enu.Current).IsSelected = items.Contains(enu.Current);
            }
        }

        private void ClearItems()
        {
            var enu = _ownerDataGrid.ItemsSource.GetEnumerator();
            List<object> list = new List<object>();
            while (enu.MoveNext())
            {
                list.Add(enu.Current);
            }
            List<object> removableObjects = new List<object>();
            foreach (var item in _markObjects)
            {
                if (list.Contains(item.Key) == false)
                {
                    removableObjects.Add(item.Key);
                }
            }
            for (int i = 0; i < removableObjects.Count; i++)
            {
                _markObjects.Remove(removableObjects[i]);
            }
        }

        private void OnHeaderCheckBoxLoaded(object sender, RoutedEventArgs e)
        {
            _headerCheckBox = sender as CheckBox;
            _headerCheckBox.Loaded -= OnHeaderCheckBoxLoaded;
            _headerCheckBox.Checked += (s2, e2) => SetAllSelectedStates(true);
            _headerCheckBox.Unchecked += (s2, e2) => SetAllSelectedStates(false);
        }

        private void OnLoadingRow(object sender, DataGridRowEventArgs e)
        {
            object dataContext = e.Row.DataContext;
            FrameworkElement element = this.GetCellContent(e.Row);
            element.DataContext = GetMarkObject(dataContext);
        }

        private void SetAllSelectedStates(bool value)
        {
            if (_ownerDataGrid.ItemsSource == null)
                return;

            var enu = _ownerDataGrid.ItemsSource.GetEnumerator();
            while (enu.MoveNext())
            {
                GetMarkObject(enu.Current).IsSelected = value;
            }
            ClearItems();
        }

        private MarkObject GetMarkObject(Object obj)
        {
            if (_markObjects.ContainsKey(obj) == false)
            {
                MarkObject markObject;
                markObject = new MarkObject();
                _markObjects.Add(obj, markObject);
                markObject.PropertyChanged += (s, e) =>
                {
                    if (e.PropertyName == "IsSelected")
                    {
                        if (SelectedItemChanged != null)
                        {
                            SelectedItemChanged(this, EventArgs.Empty);
                        }
                    }
                };
            }

            return _markObjects[obj];
        }
    }

 

其中MarkObject是一个继承INotifyPropertyChanged的类,包含Selected属性,这样更改IsSelected时可以更新UI。

最后加上DataGridExtensions 是为了方便调用。

 


 

View Code
  public static class DataGridExtensions
    {
        internal static DataGridSelectColumn GetSelectColumn(this DataGrid dataGrid)
        {
            DataGridSelectColumn result = null;
            for (int i = 0; i < dataGrid.Columns.Count; i++)
            {
                result = dataGrid.Columns[i] as DataGridSelectColumn;
                if (result != null)
                    break;
            }
            return result;
        }

        public static void EnableSelect(this DataGrid dataGird)
        {
            var column = GetSelectColumn(dataGird);
            column.OwnerDataGrid = dataGird;
        }

        public static void SelectAll(this DataGrid dataGrid)
        {
            DataGridSelectColumn column = GetSelectColumn(dataGrid);
            if (column == null)
                throw new Exception("No Select Column");
            column.SelecteAll();
        }

        public static void UnselectAll(this DataGrid dataGrid)
        {
            DataGridSelectColumn column = GetSelectColumn(dataGrid);
            if (column == null)
                throw new Exception("No Select Column");
            column.UnselectAll();
        }

        public static List<T> GetSelectedItems<T>(this DataGrid dataGrid)
        {
            DataGridSelectColumn column = GetSelectColumn(dataGrid);
            if (column == null)
                throw new Exception("No Select Column");
            return column.GetSelectedItems<T>();
        }

        public static void SetSelectedItems(this DataGrid dataGrid, IList items)
        {
            DataGridSelectColumn column = GetSelectColumn(dataGrid);
            if (column == null)
                throw new Exception("No Select Column");
            column.SetSelectedItems(items);
        }
    }

 

附上完整源码,欢迎指正。

/Files/dino623/DataGridSelectSample.zip

posted @ 2012-01-22 23:53 dino.c 阅读(...) 评论(...) 编辑 收藏