【问题标题】:DataGrid Select ColumnDataGrid 选择列
【发布时间】:2012-03-01 19:16:00
【问题描述】:

我正在尝试以编程方式选择 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;
}

更新

我正在尝试@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(有点奇怪),但反之则不然。 IE。如果我在 GUI 中选择一个单元格/行,它不会在代码中调用属性设置器。如您所见,绑定是双向的,所以我不确定这个问题。

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

【问题讨论】:

    标签: c# wpf datagrid


    【解决方案1】:

    一种更有效的方法可能是在 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 ...
    }
    

    接下来,您可以更改每个 Column 的 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>
    

    最后,像这样实现你的全选代码(这在 Age 上全选,你可能想要做一个更通用/优雅的实现;):

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

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

    【讨论】:

    • 你有点误解了这个问题,默认的选择单位是一行,如果要选择一列,每个属性都需要一个 IsSelected 属性,并且每一列都有不同的单元格样式绑定到它。
    • 这就是我打算建议的,H.B.,虽然我正在审查我写的内容并且我的语言有点模糊。将举例修改。
    • 它似乎可以从代码到 GUI,但反之则不行。 IE。如果我在代码中设置 isselected 它在 gui 中可见(有点,它的古怪)。但是,如果我选择一个单元格或一行,它确实会传播到代码中的属性(不会调用 setter)。我将绑定设置为 TwoWay,所以我不确定这个问题。我将在问题中发布我的新代码,以便您可以看到我在做什么。
    • 您是否尝试使用BindingExpression 来检查绑定?听起来绑定存在问题,因为它没有将更改传播到源(不能将其归咎于 UI 刷新)。你所说的“古怪”是指 GUI 并不总是正确更新吗?
    • 正确,gui 并不总是正确更新。我想知道它是否与虚拟化有关,因为如果它离开第一个“屏幕”(即如果我必须向下滚动),它是否选择它会变得很糟糕
    猜你喜欢
    • 1970-01-01
    • 2011-07-06
    • 2011-01-06
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    • 1970-01-01
    • 2012-06-27
    • 2011-01-01
    相关资源
    最近更新 更多