DataGrid Выбрать столбец - PullRequest
       2

DataGrid Выбрать столбец

4 голосов
/ 01 марта 2012

Я пытаюсь программно выделить весь столбец в WPF DataGrid. Кажется, мой код работает, но он ДЕЙСТВИТЕЛЬНО медленный! Я предполагаю, что это потому, что он постоянно вызывает 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;
}

UPDATE

Я пробую решение, предложенное @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 в коде, оно обновляет графический интерфейс (вроде как его причудливо), но не наоборот. То есть если я выберу ячейку / строку в графическом интерфейсе, она не вызовет установщик свойства в коде. Как вы видите, привязка - это TwoWay, поэтому я не уверен, что проблема.

Еще одно ОБНОВЛЕНИЕ: Кажется, проблема определенно связана с виртуализацией. Если я отключаю виртуализацию (VirtualizingStackPanel.IsVirtualizing = "False"), она работает нормально.

1 Ответ

2 голосов
/ 01 марта 2012

Более эффективным подходом, вероятно, было бы использование свойств IsSelected для класса DataSource, чтобы каждый столбец имел соответствующее свойство 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 следующим образом (это делает select-all для Age, вы можете захотеть сделать более общую / элегантную реализацию;)):

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

Вы должны позаботиться о том, чтобы все ваши действия NotifyPropertyChanged были выстроены в линию, поскольку вы ожидаете, что сетка распознает, что свойства внутри ее связанной коллекции обновляются.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...