Haritha
Haritha

Reputation: 1508

How to set selected item of a DataGrid programmatically in WPF with MVVM application?

I have bound the DataTable to the DataGrid control. How can I set the selected item programmatically ?

Example

In my view model I have a property of type DataTable to bind the DataGrid

 private DataTable sizeQuantityTable;

 public DataTable SizeQuantityTable
 {
        get
        {
            return sizeQuantityTable;
        }
        set
        {
            sizeQuantityTable = value;
            NotifyPropertyChanged("SizeQuantityTable");
        }
  }

My XAML

<DataGrid 
            ItemsSource="{Binding SizeQuantityTable}"
            AutoGenerateColumns="True" 
            Margin="0,0,0,120" />

The constructor of the view model (assigning dummy values)

this.SizeQuantityTable = new DataTable();

DataColumn sizeQuantityColumn = new DataColumn();
sizeQuantityColumn.ColumnName = "Size Quantity";
this.SizeQuantityTable.Columns.Add(sizeQuantityColumn);

DataColumn sColumn = new DataColumn();
sColumn.ColumnName = "S";
this.SizeQuantityTable.Columns.Add(sColumn);

DataColumn mColumn = new DataColumn();
mColumn.ColumnName = "M";
this.SizeQuantityTable.Columns.Add(mColumn);

DataRow row1 = this.SizeQuantityTable.NewRow();
row1[sizeQuantityColumn] = "Blue";
row1[sColumn] = "12";
row1[mColumn] = "15";
this.SizeQuantityTable.Rows.Add(row1);

DataRow row2 = this.SizeQuantityTable.NewRow();
row2[sizeQuantityColumn] = "Red";
row2[sColumn] = "18";
row2[mColumn] = "21";
this.SizeQuantityTable.Rows.Add(row2);

DataRow row3 = this.SizeQuantityTable.NewRow();
row3[sizeQuantityColumn] = "Green";
row3[sColumn] = "24";
row3[mColumn] = "27";
this.SizeQuantityTable.Rows.Add(row3);

OK. I have created three columns namely sizeQuantityColumn, sColumn and mColumn and added three rows namely row1, row2 and row2.

So, Let's say I wanna set the selected item as row2 (So in the view, the second row should be highlighted).

How can I do this?

EDIT

I hardcoded the SelectedIndex of the DataGrid to 1. (So the second row should be selected). In design time it shows as selected. But not in the run time. You can see it in the below snapshot.

So ultimaltely the problem is Not highlighting the row.

enter image description here

Upvotes: 8

Views: 38679

Answers (6)

Mahmoud
Mahmoud

Reputation: 58

xaml:

<DataGrid  SelectionUnit="FullRow" >
</DataGrid>

code

SelectRowByIndex(yourDataGridViewName, 0);

public static void SelectRowByIndex(DataGrid dataGrid, int rowIndex)
        {
            if (!dataGrid.SelectionUnit.Equals(DataGridSelectionUnit.FullRow))
                throw new ArgumentException("The SelectionUnit of the DataGrid must be set to FullRow.");

            if (rowIndex < 0 || rowIndex > (dataGrid.Items.Count - 1))
                throw new ArgumentException(string.Format("{0} is an invalid row index.", rowIndex));

            dataGrid.SelectedItems.Clear();
            /* set the SelectedItem property */
            object item = dataGrid.Items[rowIndex]; // = Product X
            dataGrid.SelectedItem = item;

            DataGridRow row = dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow;
            if (row == null)
            {
                /* bring the data item (Product object) into view
                 * in case it has been virtualized away */
                dataGrid.ScrollIntoView(item);
                row = dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow;
            }
            //TODO: Retrieve and focus a DataGridCell object
            if (row != null)
            {
                DataGridCell cell = GetCell(dataGrid, row, 0);
                if (cell != null)
                    cell.Focus();
            }
        }


 public static DataGridCell GetCell(DataGrid dataGrid, DataGridRow rowContainer, int column)
    {
        if (rowContainer != null)
        {
            DataGridCellsPresenter presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
            if (presenter == null)
            {
                /* if the row has been virtualized away, call its ApplyTemplate() method
                 * to build its visual tree in order for the DataGridCellsPresenter
                 * and the DataGridCells to be created */
                rowContainer.ApplyTemplate();
                presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
            }
            if (presenter != null)
            {
                DataGridCell cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
                if (cell == null)
                {
                    /* bring the column into view
                     * in case it has been virtualized away */
                    dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
                    cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
                }
                return cell;
            }
        }
        return null;
    }
    public static childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
    {
        foreach (childItem child in FindVisualChildren<childItem>(obj))
        {
            return child;
        }

        return null;
    }
    public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj)
   where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    } 

Upvotes: 0

DANGLES
DANGLES

Reputation: 26

For anyone using Observable Collections you may find this solution useful.

The SelectedModelIndex property simply returns the index of the SelectedModel from the ItemSource collection. I've found setting the SelectedIndex along with the SelectedItem highlights the row in the DataGrid.

    private ObservableCollection<Model> _itemSource
    public ObservableCollection<Model> ItemSource
    {
        get { return _itemSource; }
        set 
        { 
            _itemSource = value; 
            OnPropertyChanged("ItemSource"); 
        }
    }

    // Binding must be set to One-Way for read-only properties
    public int SelectedModelIndex
    {
        get 
        {
            if (ItemSource != null && ItemSource.Count > 0)
                return ItemSource.IndexOf(SelectedModel);
            else
                return -1;
        }            
    }

    private Model _selectedModel;
    public Model SelectedModel
    {
        get { return _selectedModel; }
        set 
        {
            _selectedModel = value;
            OnPropertyChanged("SelectedModel"); 
            OnPropertyChanged("SelectedModelIndex");               
        }                
    }

Upvotes: 0

Martin
Martin

Reputation: 5623

I just had the same problem. I saw that the item of a datagrid was selected correctly at design time, but not at runtime. (By the way, I create the instance of the view model in the xaml).

I could solve this problem by moving the code to programmatically set the selected item from the view models constructor to a different method in the view model and then calling this method in the loaded event of the window (or usercontrol).

Obviously the view is not completely done initializing itself when the view models constructor is called. This can be avoided by not coding in the view models constructor.

View (xaml):

<Window x:Class="MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Test" 
        xmlns:viewModel="clr-namespace:ViewModels" Loaded="Window_Loaded">
    <Window.DataContext>
        <viewModel:ExampleViewModel/>
    </Window.DataContext>

View (code behind):

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        ((ExampleViewModel)this.DataContext).LoadData();
    }

If you do not like setting up the Loaded event in the code behind, you can also do it in xaml (references to "Microsoft.Expression.Interactions" and "System.Windows.Interactivity" are needed):

    <Window x:Class="MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Test" 
        xmlns:viewModel="clr-namespace:ViewModels">
    <Window.DataContext>
        <viewModel:ExampleViewModel/>
    </Window.DataContext>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <ei:CallMethodAction TargetObject="{Binding}" MethodName="LoadData"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

In each case, you call the LoadData method in the ViewModel:

public class ExampleViewModel
{
    /// <summary>
    /// Constructor.
    /// </summary>
    public ExampleViewModel()
    {
        // Do NOT set selected item here
    }


    public void LoadData()
    {
        // Set selected item here
    }

Upvotes: 1

Erti-Chris Eelmaa
Erti-Chris Eelmaa

Reputation: 26268

You can always use SelectedItem property and bind it against row, as such:

SelectedItem="{Binding ActiveRow}"

and in ViewModel do:

ActiveRow = secondRow;

Upvotes: 3

fhnaseer
fhnaseer

Reputation: 7267

Add SelectedItem, SelectedValue in your DataGrid.

<DataGrid 
        ItemsSource="{Binding SizeQuantityTable}"
        AutoGenerateColumns="True" 
        SelectedValue ="{Binding SelectedValue}"
        Margin="0,0,0,120" />

And in your view model

private string _selectedValue;
public string SelectedValue
{
    get
    {
        return _selectedValue;
    }
    set
    {
        _selectedValue = value;
        NotifyPropertyChanged("SelectedValue");
    }
}

private DataTable sizeQuantityTable;
public DataTable SizeQuantityTable
{
    get
    {
        return sizeQuantityTable;
    }
    set
    {
        sizeQuantityTable = value;
        NotifyPropertyChanged("SizeQuantityTable");
    }
}

You can use SelectedItem as well, that is preferred.

Upvotes: 1

sa_ddam213
sa_ddam213

Reputation: 43596

There are a few way to select items in the DataGrid. It just depends which one works best for the situation

First and most basic is SelectedIndex this will just select the Row at that index in the DataGrid

 <DataGrid SelectedIndex="{Binding SelectedIndex}" />

private int _selectedIndex;
public int SelectedIndex
{
    get { return _selectedIndex; }
    set { _selectedIndex = value; NotifyPropertyChanged("SelectedIndex"); }
}

SelectedIndex = 2;

SelectedItem will select the row that matches the row you set

<DataGrid SelectedItem="{Binding SelectedRow}" />

private DataRow _selectedRow;
public DataRow SelectedRow
{
    get { return _selectedRow; }
    set { _selectedRow = value; NotifyPropertyChanged("SelectedRow");}
}

SelectedRow = items.First(x => x.whatever == something);

The most common one is SelectedValue with SelectedValuePath set, in this case you set the column you want to select with and then to can select the row by setting the corresponding value

<DataGrid SelectedValuePath="Size Quantity" SelectedValue="{Binding SelectionValue}" 

private string _selectedValue
public string SelectionValue 
{
    get { return _selectedValue; }
    set { _selectedValue = value; NotifyPropertyChanged("SelectionValue"); }
}

SelectionValue = "Blue";

Edit:

Here is my test and it is highlighting just fine

Code:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();

        this.SizeQuantityTable = new DataTable();
        DataColumn sizeQuantityColumn = new DataColumn();
        sizeQuantityColumn.ColumnName = "Size Quantity";
        ...................
        ........

    }

    private string _selectedValue;
    public string SelectionValue 
    {
        get { return _selectedValue; }
        set { _selectedValue = value; NotifyPropertyChanged("SelectionValue"); }
    }

    private int _selectedIndex;
    public int SelectedIndex
    {
        get { return _selectedIndex; }
        set { _selectedIndex = value; NotifyPropertyChanged("SelectedIndex"); }
    }

    private DataTable sizeQuantityTable;
    public DataTable SizeQuantityTable
    {
        get { return sizeQuantityTable; }
        set { sizeQuantityTable = value; NotifyPropertyChanged("SizeQuantityTable"); }
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        SelectedIndex = 2;
    }

    private void Button_Click_2(object sender, RoutedEventArgs e)
    {
        SelectionValue = "Blue";
    }

    private void NotifyPropertyChanged(string p)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(p));
        }
    }
}

Xaml:

<Window x:Class="WpfApplication21.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="202" Width="232" Name="UI">

    <Grid DataContext="{Binding ElementName=UI}">
        <DataGrid SelectedValuePath="Size Quantity"        
                  SelectedValue="{Binding SelectionValue}" 
                  SelectedIndex="{Binding SelectedIndex}"
                  ItemsSource="{Binding SizeQuantityTable}"
                  AutoGenerateColumns="True" 
                  Margin="0,0,0,41" />
        <StackPanel Orientation="Horizontal" Height="37" VerticalAlignment="Bottom" >
            <Button Content="SelectedIndex" Height="26"  Width="107" Click="Button_Click_1"/>
            <Button Content="SelectedValue" Height="26"  Width="107" Click="Button_Click_2"/>
        </StackPanel>
    </Grid>
</Window>

Result:

enter image description here

Upvotes: 10

Related Questions