DataGrid Selection Column

I am trying to programmatically select an entire column in a DataGrid WPF. My code seems to work, but it is REALLY slow! I guess this is because I constantly have to call ScrollIntoView. Can someone help me with a solution to speed it up or an alternative to select the entire column?

public static void SelectColumn(DataGrid grid, int column) { for (int i = 0; i < grid.Items.Count; i++) { // Select each cell in this column var cell = DataGridHelper.GetCell(grid, i, column); if (cell != null) { cell.IsSelected = true; } } DataGridHelper.GetCell(grid, 0, column).Focus(); } public static DataGridCell GetCell(DataGrid grid, int row, int column) { DataGridRow rowContainer = GetRow(grid, row); if (rowContainer != null) { DataGridCellsPresenter presenter = TreeHelper.GetVisualChild<DataGridCellsPresenter>(rowContainer); if (presenter == null) { // may be virtualized, bring into view and try again grid.ScrollIntoView(rowContainer, grid.Columns[column]); presenter = TreeHelper.GetVisualChild<DataGridCellsPresenter>(rowContainer); } if (presenter != null) { // try to get the cell but it may possibly be virtualized DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column); if (cell == null) { // may be virtualized, bring into view and try again grid.ScrollIntoView(rowContainer, grid.Columns[column]); cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column); } return cell; } } return null; } public static DataGridRow GetRow(DataGrid grid, int index) { DataGridRow row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index); if (row == null) { // may be virtualized, bring into view and try again grid.ScrollIntoView(grid.Items[index]); row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index); } return row; } 

UPDATE

I am trying to find the solution suggested by @ianschol. Here is what I have (I bind the code in b / c. I don't know how many columns I need before executing):

 for (int i = 0; i < this.CurrentData.Data[0].Length; i++) { TheGrid.Columns.Add( new DataGridTextColumn { Header = (this.CurrentData.Rank > 1) ? string.Format(this.culture, headerFormatString, i + 1) : string.Empty, Binding = new Binding(string.Format("[{0}].DataValue", i)) { ValidatesOnDataErrors = true, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }, Width = DataGridLength.Auto, ElementStyle = new Style { TargetType = typeof(TextBlock), Triggers = { this.errorTrigger } }, EditingElementStyle = new Style { TargetType = typeof(TextBox), Triggers = { this.errorTrigger } }, CellStyle = new Style { TargetType = typeof(DataGridCell), Setters = { new Setter { Property = DataGridCell.IsSelectedProperty, Value = new Binding(string.Format("[{0}].IsSelected", i)) { Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }, } }, } }); } 

and my IsSelected property:

 private bool isSelected = false; public bool IsSelected { get { return this.isSelected; } set { this.isSelected = value; OnPropertyChanged("IsSelected"); } } 

And the new SelectColumn code:

 public static void SelectColumn(DataGrid grid, int column) { for (int i = 0; i < grid.Items.Count; i++) { // Select each cell in this column ((DataItem[])(grid.Items[i]))[column].IsSelected = true; } } 

The problem is that if I update the IsSelected property in the code, it updates the GUI (kinda, its quirky), but not vice versa. That is, if I select a cell / row in the GUI, it does not call the customization tool in the code. As you can see, binding is TwoWay, so I'm not sure about this problem.

Other UPDATE: The problem is certainly related to virtualization. If I turn off virtualization (VirtualizingStackPanel.IsVirtualizing = "False"), it works fine.

+4
source share
1 answer

A more efficient approach would probably be to have IsSelected properties in the DataSource class, so that each column has a corresponding IsSelected property.

 public class MyData : INotifyPropertyChanged { private string name; public string Name { get { return name; } set { name = value; Notify("Name"); } } private bool nameSelected = false; public bool NameSelected { get { return nameSelected; } set { nameSelected = value; Notify("NameSelected"); } } //... etc ... } 

You can then modify the CellStyle for each column to associate the IsSelected property with the cell with the corresponding IsSelected property in the class.

  <DataGrid ItemsSource="{Binding Users}" AutoGenerateColumns="False" HorizontalAlignment="Left" Name="scratchGrid" CanUserAddRows="False" VerticalScrollBarVisibility="Auto" SelectionUnit="Cell"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Name}" Header="User Name" Width="200"> <DataGridTextColumn.CellStyle> <Style TargetType="{x:Type DataGridCell}"> <Setter Property="IsSelected" Value="{Binding NameSelected}" /> </Style> </DataGridTextColumn.CellStyle> </DataGridTextColumn> <DataGridTextColumn Binding="{Binding Age}" Header="User Age" Width="80"> <DataGridTextColumn.CellStyle> <Style TargetType="{x:Type DataGridCell}"> <Setter Property="IsSelected" Value="{Binding AgeSelected}" /> </Style> </DataGridTextColumn.CellStyle> </DataGridTextColumn> </DataGrid.Columns> </DataGrid> 

Finally, implement your select-all code like this (this does select-all in Age, you can make a more universal / elegant implementation;)):

  foreach (MyData user in Users) { user.AgeSelected = true; } 

You need to make sure that all NotifyPropertyChanged behavior is aligned, as you expect the grid to know that the properties within the linked collection are being updated.

+2
source

Source: https://habr.com/ru/post/1399244/


All Articles