如何在WPF DataGrid中以编程方式选择行或单元格?

12

在 WinForm DataGridView 中,初始化时会自动选择第一行,当我试图关闭该功能时,这让我很困扰。转向 WPF DataGrid,微软似乎已经决定关闭了这个功能,我认为这是一件好事。然而,我现在很难启用此功能。对于某些DataGrid,我希望在通过数据绑定填充网格后自动选择第一行。网络上有一些建议,但我无法使其正常工作。希望在这里能够更好地解决问题。

4个回答

11

设置 IsSynchronizedWithCurrentItem = "true"

编辑:

回应您的评论,我假设您的 DataGrid 的 SelectionUnit 设置为 "Cell",是吗?好的,我不确定这是否是最佳解决方案,但您可以在 DataGrid 上处理 Loaded 事件,并在代码中手动设置所选单元格。因此,您将拥有类似以下内容的东西:

<DataGrid x:Name="dg" AutoGenerateColumns="False" IsSynchronizedWithCurrentItem="True"
            SelectedCellsChanged="dg_SelectedCellsChanged" SelectionUnit="Cell"
            Loaded="dg_Loaded">
    ...
</DataGrid>

事件处理器:

private void dg_Loaded(object sender, RoutedEventArgs e)
{
    if ((dg.Items.Count > 0) &&
        (dg.Columns.Count > 0))
    {
        //Select the first column of the first item.
        dg.CurrentCell = new DataGridCellInfo(dg.Items[0], dg.Columns[0]);
        dg.SelectedCells.Add(dg.CurrentCell);
    }
}
注意,这仅在DataGrid.SelectionUnit设置为"Cell"时才起作用。否则,我认为它会抛出异常。
编辑2:
XAML:
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Button Click="Button_Click">Reset</Button>
        <DataGrid x:Name="dg" AutoGenerateColumns="False" IsSynchronizedWithCurrentItem="True"
                SelectionUnit="Cell"
                DataContextChanged="dg_DataContextChanged"
                ItemsSource="{Binding Items}"
                Loaded="dg_Loaded">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding}"/>
            </DataGrid.Columns>
        </DataGrid>
    </StackPanel>
</Window>

代码后端:

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.LoadItems();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.LoadItems();
        }

        private void LoadItems()
        {
            this.DataContext = new { Items = new List<string> { "Item1", "Item2", "Item3" } };
            this.SelectFirstItem();
        }

        private void dg_Loaded(object sender, RoutedEventArgs e)
        {
            SelectFirstItem();
        }

        void SelectFirstItem()
        {
            if ((dg.Items.Count > 0) &&
                (dg.Columns.Count > 0))
            {
                //Select the first column of the first item.
                dg.CurrentCell = new DataGridCellInfo(dg.Items[0], dg.Columns[0]);
                dg.SelectedCells.Add(dg.CurrentCell);
            }
        }

        private void dg_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.SelectFirstItem();
        }
    }
}

非常感谢这个提示。我刚刚尝试了一下。将其设置为true将使DataGrid的第一行“突出显示”,但不是选定状态。我需要触发SelectedCellsChanged事件。那么,我该如何使这个事件也被触发呢? - newman
我不认为WPF中有DataBindingComplete事件。我不确定。那么尝试一下"dataGrid.ItemContainerGenerator.ItemsChanged"事件怎么样? - ASanch
@karmicpuppet:刚刚尝试了使用DataGrid.ItemContainerGenerator.ItemsChanged事件,行为似乎与DataContextChanged相同。 - newman
@karmicpuppet:我不明白MVVM在这种情况下如何帮助我。我只想在DataContext更改时自动选择第一行。我真的很讨厌为了这个看似非常简单的任务而写大量的代码。 - newman
好的,如果你真的想在DataContext更改时自动选择第一行,那么我相信我上面的代码可以实现。请参见上面的EDIT2并尝试运行代码。它是有效的。 - ASanch
显示剩余3条评论

3

您可以在DataGrid.Loaded事件中始终执行此操作。只需获取第一行,并使该行触发选择事件。

void MyGridLoaded(...) {
DataGridRow r = yourGrid.ItemContainergenerator.ContainerFromIndex(0) as DataGridRow;
  if(r != null) {
     r.IsSelected = false;
     r.IsSelected = true;
  }

} 

我不确定这是否是一个bug,因为在控件加载之前,可能无法保证从您的对象中触发选择事件。我不知道。

2
你可以尝试这个。
        this.dataGrid.SelectionMode = DataGridSelectionMode.Single;

        // Selects the 4th row.
        this.dataGrid.SelectedIndex = 3;

这个是用于行选择的。 - Prince Ashitaka
@Avatar:我刚刚尝试了你的建议。它似乎具有与IsSynchronizedWithCurrentItem相同的效果。因此,它只是突出显示行,但我没有得到SelectedCellsChanged事件。不幸的是,没有“SelectedRowChanged”事件或类似的事件。与WinForm DataGridView相比,WPF DataGrid的事件非常少。 - newman
@miliu 在选择单元格改变时应该会触发SelectedCellsChanged事件,不知道问题出在哪里。我这里有一个示例,请检查一下。可能会给你一些想法。http://www.filefactory.com/file/b3bhf1d/n/DataGridCheckBoxSelection.zip - Prince Ashitaka
@Avatar:我无法从FileFactory下载您的文件。它要求我注册以摆脱弹出广告。我已经注册了,但它仍然弹出。我必须等待30秒。我等了,但是当我点击时,整个循环重新开始,并最终导致我的IE崩溃。它已经崩溃了三次。我讨厌那些有大量弹出窗口的网页。他们真的能从中赚钱吗? - newman
不要做那些事情。我以为它们是免费的。好的,我已经在这里上传了文件 https://sites.google.com/site/html5tutorials/DataGridCheckBoxSelection.zip 我想这样更安全。 - Prince Ashitaka
@Avatar:感谢您提供的示例,它帮助我部分解决了我的问题。是的,SelectedCellsChanged事件确实被触发了。我只是没有注意到它,因为在我的事件处理程序中,我正在检查未设置的CurrentCell。然而,我还看到另一个问题。如果我在DataContextChanged事件中放置DataGrid.SelectedIndex = 0,它第一次就能工作。但在后续的时间(当数据上下文被更改时),选择高亮不会显示出来。我需要这个视觉提示来指示当前选定的行。我认为这更多是DataContextChanged事件的问题,请参见上文。 - newman

1

我很高兴地报告,我通过ItemContainerGenerator.StatusChanged事件找到了解决这个问题的方法。

dataGrid.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);

void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
        {
            if (dataGrid.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                dataGrid.SelectedIndex = 0;
            }
        }

当使用状态 ContainersGenerated 触发此事件时,dataGrid 将完全初始化。对我而言,这更像是 WinForm 中 DataGridView 的 DataBindingComplete 事件。如果是这样,那么 "DataContextChanged" 事件应该真正被称为 "DataContextChanging" 事件。

这是受到我意外发现的一个帖子 here 的启发,我当时正在寻找其他线索。


其实我刚刚意识到 ItemContainerGenerator_StatusChanged 事件可能会被触发太多次了。我转而使用 DataContextChanged 事件,并通过 Dispatcher.BeginInvoke 使其正常工作。 - newman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接