DataGrid Select Column

本文关键字:Column Select DataGrid | 更新日期: 2023-09-27 18:26:12

我正在尝试用程序选择WPF数据网格中的整列。我的代码似乎可以工作,但它真的很慢!我猜这是因为它不断地调用ScrollIntoView。有人能帮我提供一个加快速度的解决方案,或者选择整个专栏的替代方案吗?

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

更新

我正在试用ianschol提出的解决方案。以下是我所拥有的(我绑定在b/c后面的代码中,直到运行时我才知道我需要多少列):

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

和我的IsSelected属性:

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

新的SelectColumn代码:

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

问题是,如果我在代码中更新IsSelected属性,它会更新GUI(有点奇怪),但不会反过来。也就是说,如果我在GUI中选择了一个单元格/行,它就不会调用代码中的属性setter。正如你所看到的,绑定是双向的,所以我不确定这个问题。

另一个更新:问题显然与虚拟化有关。如果我关闭虚拟化(VirtualizingStackPanel.IsVirtualization="False"),它可以正常工作。

DataGrid Select Column

更有效的方法可能是在DataSource的类上具有IsSelected属性,这样每列都有相应的"IsSelected"属性。

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 ...
}

接下来,您可以更改每个列的CellStyle,以将单元格的IsSelected属性绑定到类上的相关IsSelected特性。

    <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>

最后,像这样实现select all代码(这确实在Age上进行了select all,你可能想做一个更通用/更优雅的实现;):

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

您必须注意确保所有NotifyPropertyChanged行为都是对齐的,因为您希望网格识别出其绑定集合中的属性正在更新。