[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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM