25

How do I use the Datagrid.SelectedItem to select a row programmatically?

Do I first have to create a IEnumerable of DataGridRow objects and pass the matching row to this SelectedItem property or how do I do it?

EDIT:

I need to match the cell content of the first columns cell with a TextBox.Text first, before selecting the row.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Tony The Lion
  • 61,704
  • 67
  • 242
  • 415

8 Answers8

42

My code iterates through cells of the datagrid's first column and checks if cell content equals to the textbox.text value and selects the row.

for (int i = 0; i < dataGrid.Items.Count; i++)
{
    DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(i);
    TextBlock cellContent = dataGrid.Columns[0].GetCellContent(row) as TextBlock;
    if (cellContent != null && cellContent.Text.Equals(textBox1.Text))
    {
        object item = dataGrid.Items[i];
        dataGrid.SelectedItem = item;
        dataGrid.ScrollIntoView(item);
        row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        break;
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
serge_gubenko
  • 20,186
  • 2
  • 61
  • 64
  • 2
    There might be cases when you find adding `dataGrid.UpdateLayout()` just before `dataGrid.ScrollIntoView()` quite useful - as noted in [MSDN](http://msdn.microsoft.com/en-us/library/windows/apps/hh968031.aspx): *When the contents of the ItemsSource collection changes, particularly if many items are added to or removed from the collection, you might need to call UpdateLayout prior to calling ScrollIntoView for the specified item to scroll into the viewport.* – jwaliszko Feb 03 '14 at 11:16
  • 1
    I tried this and it crashed on me, but context is good. What I found was that if the visual grid area is only say... 5 rows, but you have 20 rows of data, the ContainerFromIndex(i) fails returning a row and throws error if not checked for null. – DRapp Feb 18 '16 at 15:02
  • is it possible that this only works for rows that are visible to the user ? Although `DataGrid.Items.Count` is far over 1000 in my case, `ContainerFromIndex(i)` starts returning `null` after the first 7 elements. Or am I missing something? – Boern Jun 08 '18 at 13:28
27

You don't need to iterate through the DataGrid rows, you can achieve your goal with a more simple solution. In order to match your row you can iterate through you collection that was bound to your DataGrid.ItemsSource property then assign this item to you DataGrid.SelectedItem property programmatically, alternatively you can add it to your DataGrid.SelectedItems collection if you want to allow the user to select more than one row. See the code below:

<Window x:Class="ProgGridSelection.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" Loaded="OnWindowLoaded">
<StackPanel>
    <DataGrid Name="empDataGrid" ItemsSource="{Binding}" Height="200"/>
    <TextBox Name="empNameTextBox"/>
    <Button Content="Click" Click="OnSelectionButtonClick" />
</StackPanel>

public partial class MainWindow : Window
{
    public class Employee
    {
        public string Code { get; set; }
        public string Name { get; set; }
    }

    private ObservableCollection<Employee> _empCollection;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void OnWindowLoaded(object sender, RoutedEventArgs e)
    {
        // Generate test data
        _empCollection =
            new ObservableCollection<Employee>
                {
                    new Employee {Code = "E001", Name = "Mohammed A. Fadil"},
                    new Employee {Code = "E013", Name = "Ahmed Yousif"},
                    new Employee {Code = "E431", Name = "Jasmin Kamal"},
                };

        /* Set the Window.DataContext, alternatively you can set your
         * DataGrid DataContext property to the employees collection.
         * on the other hand, you you have to bind your DataGrid
         * DataContext property to the DataContext (see the XAML code)
         */
        DataContext = _empCollection;
    }

    private void OnSelectionButtonClick(object sender, RoutedEventArgs e)
    {
        /* select the employee that his name matches the
         * name on the TextBox
         */
        var emp = (from i in _empCollection
                   where i.Name == empNameTextBox.Text.Trim()
                   select i).FirstOrDefault();

        /* Now, to set the selected item on the DataGrid you just need
         * assign the matched employee to your DataGrid SeletedItem
         * property, alternatively you can add it to your DataGrid
         * SelectedItems collection if you want to allow the user
         * to select more than one row, e.g.:
         *    empDataGrid.SelectedItems.Add(emp);
         */
        if (emp != null)
            empDataGrid.SelectedItem = emp;
    }
}
Mohammed A. Fadil
  • 9,205
  • 7
  • 50
  • 67
9

I've searched solution to similar problem and maybe my way will help You and anybody who face with it.

I used SelectedValuePath="id" in XAML DataGrid definition, and programaticaly only thing I have to do is set DataGrid.SelectedValue to desired value.

I know this solution has pros and cons, but in specific case is fast and easy.

Best regards

Marcin

Steve Greatrex
  • 15,789
  • 5
  • 59
  • 73
Marcin Sasin
  • 91
  • 1
  • 1
  • hello , I know I am replying late.. Marcin is it possible to share the code having this issue. this will help me a lot.. I am facing a similar issue – Indhi Feb 13 '13 at 14:47
  • This is the simplest solution! – Abbas Mar 28 '16 at 12:35
8

It's a little trickier to do what you're trying to do than I'd prefer, but that's because you don't really directly bind a DataGrid to a DataTable.

When you bind DataGrid.ItemsSource to a DataTable, you're really binding it to the default DataView, not to the table itself. This is why, for instance, you don't have to do anything to make a DataGrid sort rows when you click on a column header - that functionality's baked into DataView, and DataGrid knows how to access it (through the IBindingList interface).

The DataView implements IEnumerable<DataRowView> (more or less), and the DataGrid fills its items by iterating over this. This means that when you've bound DataGrid.ItemsSource to a DataTable, its SelectedItem property will be a DataRowView, not a DataRow.

If you know all this, it's pretty straightforward to build a wrapper class that lets you expose properties that you can bind to. There are three key properties:

  • Table, the DataTable,
  • Row, a two-way bindable property of type DataRowView, and
  • SearchText, a string property that, when it's set, will find the first matching DataRowView in the table's default view, set the Row property, and raise PropertyChanged.

It looks like this:

public class DataTableWrapper : INotifyPropertyChanged
{
    private DataRowView _Row;

    private string _SearchText;

    public DataTableWrapper()
    {
        // using a parameterless constructor lets you create it directly in XAML
        DataTable t = new DataTable();
        t.Columns.Add("id", typeof (int));
        t.Columns.Add("text", typeof (string));

        // let's acquire some sample data
        t.Rows.Add(new object[] { 1, "Tower"});
        t.Rows.Add(new object[] { 2, "Luxor" });
        t.Rows.Add(new object[] { 3, "American" });
        t.Rows.Add(new object[] { 4, "Festival" });
        t.Rows.Add(new object[] { 5, "Worldwide" });
        t.Rows.Add(new object[] { 6, "Continental" });
        t.Rows.Add(new object[] { 7, "Imperial" });

        Table = t;

    }

    // you should have this defined as a code snippet if you work with WPF
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler h = PropertyChanged;
        if (h != null)
        {
            h(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    // SelectedItem gets bound to this two-way
    public DataRowView Row
    {
        get { return _Row; }
        set
        {
            if (_Row != value)
            {
                _Row = value;
                OnPropertyChanged("Row");
            }
        }
    }

    // the search TextBox is bound two-way to this
    public string SearchText
    {
        get { return _SearchText; }
        set
        {
            if (_SearchText != value)
            {
                _SearchText = value;
                Row = Table.DefaultView.OfType<DataRowView>()
                    .Where(x => x.Row.Field<string>("text").Contains(_SearchText))
                    .FirstOrDefault();
            }
        }
    }

    public DataTable Table { get; private set; }
}

And here's XAML that uses it:

<Window x:Class="DataGridSelectionDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
        xmlns:DataGridSelectionDemo="clr-namespace:DataGridSelectionDemo" 
        Title="DataGrid selection demo" 
        Height="350" 
        Width="525">
    <Window.DataContext>
        <DataGridSelectionDemo:DataTableWrapper />
    </Window.DataContext>
    <DockPanel>
        <Grid DockPanel.Dock="Top">
        <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Label>Text</Label>
            <TextBox Grid.Column="1" 
                     Text="{Binding SearchText, Mode=TwoWay}" />
        </Grid>
        <dg:DataGrid DockPanel.Dock="Top"
                     ItemsSource="{Binding Table}"
                     SelectedItem="{Binding Row, Mode=TwoWay}" />
    </DockPanel>
</Window>
Robert Rossney
  • 94,622
  • 24
  • 146
  • 218
  • 2
    For all that the others seem to have gotten more upvotes, this seems to be the one that discusses how to bind a data set to an XML control in the way that best makes available the capabilities of the tool. – Jeff Dege Dec 19 '11 at 22:29
2

// In General to Access all rows //

foreach (var item in dataGrid1.Items)
{
    string str = ((DataRowView)dataGrid1.Items[1]).Row["ColumnName"].ToString();
}

//To Access Selected Rows //

private void dataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    try
    {
        string str = ((DataRowView)dataGrid1.SelectedItem).Row["ColumnName"].ToString();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
andrewsi
  • 10,807
  • 132
  • 35
  • 51
Shan
  • 53
  • 1
  • 3
1

I came across this fairly recent (compared to the age of the question) TechNet article that includes some of the best techniques I could find on the topic:

WPF: Programmatically Selecting and Focusing a Row or Cell in a DataGrid

It includes details that should cover most requirements. It is important to remember that if you specify custom templates for the DataGridRow for some rows that these won't have DataGridCells inside and then the normal selection mechanisms of the grid doesn't work.

You'll need to be more specific on what datasource you've given the grid to answer the first part of your question, as the others have stated.

Andre Luus
  • 3,692
  • 3
  • 33
  • 46
  • FindVisualChild trick, described long ago in the TechNet article, and kindly mentioned by Andre Luus works perfectly. Thank you, Andre for the link. I remember using the search of Visual Child much earlier than 2013, but time passes, you forget the problem working with not WPF technologies. I thought now, after so many years, WPF developers must have improved the library and such an easy tasks, as focusing DataGrid row is solved with one or two lines of code. But no, I happened to compress the solution "only" in 36 lines. Fantastic! Compare with JS approach. – Alexander Chernosvitov May 13 '19 at 08:16
1

I have changed the code of serge_gubenko and it works better

for (int i = 0; i < dataGrid.Items.Count; i++)
{
    string txt = searchTxt.Text;
    dataGrid.ScrollIntoView(dataGrid.Items[i]);
    DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(i);
    TextBlock cellContent = dataGrid.Columns[1].GetCellContent(row) as TextBlock;
    if (cellContent != null && cellContent.Text.ToLower().Equals(txt.ToLower()))
    {
        object item = dataGrid.Items[i];
        dataGrid.SelectedItem = item;
        dataGrid.ScrollIntoView(item);
        row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        break;
    }
}
ulentini
  • 2,413
  • 1
  • 14
  • 26
0

If anyone stumblng here has problems with internal grid selection happening after OnSelectionChanged - after unsuccessfully trying out all the selection setters for a dozen hours the only thing that worked for me was reloading and repopulating DataGrid along with selected item. Not elegant at all, but at this point I'm not sure if a better solution exists in my situation.

datagrid.ItemsSource = null
datagrid.ItemsSource = items;
datagrid.SelectedItem = selectedItem;
Mindaugas-kun
  • 61
  • 1
  • 4