WPF DataGrid - changing SelectedItem in code not reflected in UI?

Oct 20, 2009 at 11:39 PM
We are binding a set of  objects to a WPF DataGrid.  In the SelectionChanged event, we need to do some validation.  If validation does not pass, we need to cancel the event.  Since there is no option to cancel this event, and simply changing the selected item from within the event puts us into an infinite loop, I've had to do the following:
 
Remove the handler
Change the SelectedIndex to the previously selected index
Add the handler again
 
This works correctly, but the problem is that the UI does not reflect the change to the selected item.  The SelectedItem property of the DataGrid is changed, but the UI still highlights the newly-selected row.
 
Anyone have any ideas?
 
Here's the code:

private void dg_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

if (Validate()//Check to see if description is left blank;

{

MessageBox.Show("Blank Description");

dg.SelectionChanged -= new SelectionChangedEventHandler(dg_SelectionChanged);

dg.SelectedIndex = this.previousIndex;

dg.SelectionChanged += new SelectionChangedEventHandler(dg_SelectionChanged);

e.Handled = true;

}

this.previousIndex = dg.SelectedIndex;

}

Oct 21, 2009 at 4:23 PM
Try setting the row selected property in the LoadingRow event.

Bill

On 10/20/2009 5:39 PM, dez182 wrote:
>
>
> From: dez182
>
> We are binding a set of objects to a WPF DataGrid. In the
> SelectionChanged event, we need to do some validation. If validation
> does not pass, we need to cancel the event. Since there is no option to
> cancel this event, and simply changing the selected item from within the
> event puts us into an infinite loop, I've had to do the following:
>
> Remove the handler
> Change the SelectedIndex to the previously selected index
> Add the handler again
>
> This works correctly, but the problem is that the UI does not reflect
> the change to the selected item. The SelectedItem property of the
> DataGrid is changed, but the UI still highlights the newly-selected row.
>
> Anyone have any ideas?
>
> Here's the code:
>
> private void dg_SelectionChanged(object sender, SelectionChangedEventArgs e)
>
> {
>
> if (Validate()) //Check to see if description is left blank;
>
> {
>
> MessageBox.Show("Blank Description");
>
> dg.SelectionChanged -= new
> SelectionChangedEventHandler(dg_SelectionChanged);
>
> dg.SelectedIndex = this.previousIndex;
>
> dg.SelectionChanged += new
> SelectionChangedEventHandler(dg_SelectionChanged);
>
> e.Handled = true;
>
> }
>
> this.previousIndex = dg.SelectedIndex;
>
> }
>
> Read the full discussion online
> <http://wpf.codeplex.com/Thread/View.aspx?ThreadId=72596&ANCHOR#Post247902>.
>
> To add a post to this discussion, reply to this email
> ([email removed]
> <mailto:[email removed]?subject=[wpf:72596]>)
>
> To start a new discussion for this project, email
> [email removed] <mailto:[email removed]>
>
> You are receiving this email because you subscribed to this discussion
> on CodePlex. You can unsubscribe or change your settings
> <https://wpf.codeplex.com/subscriptions/thread/project/edit> on
> codePlex.com.
>
> Please note: Images and attachments will be removed from emails. Any
> posts to this discussion will also be available online at codeplex.com
>
Oct 21, 2009 at 4:29 PM

The LoadingRow event is not called after the SelectionChanged event.

Oct 21, 2009 at 10:20 PM
Right, sorry. Then my first thought is to pick some other time to do the
validation, if possible. And if not, allow the SelectedRow to remain
selected but present the user with some way to correct the data.

Thanks,

Bill

On 10/21/2009 10:30 AM, dez182 wrote:
>
>
> From: dez182
>
> The LoadingRow event is not called after the SelectionChanged event.
>
> Read the full discussion online
> <http://wpf.codeplex.com/Thread/View.aspx?ThreadId=72596&ANCHOR#Post248164>.
>
> To add a post to this discussion, reply to this email
> ([email removed]
> <mailto:[email removed]?subject=[wpf:72596]>)
>
> To start a new discussion for this project, email
> [email removed] <mailto:[email removed]>
>
> You are receiving this email because you subscribed to this discussion
> on CodePlex. You can unsubscribe or change your settings
> <https://wpf.codeplex.com/subscriptions/thread/project/edit> on
> codePlex.com.
>
> Please note: Images and attachments will be removed from emails. Any
> posts to this discussion will also be available online at codeplex.com
>
Jan 21, 2010 at 4:28 PM

Did you ever find a resolution for this issue? I am trying to accomplish the same thing and have tried everything I can think of to this point.

Jan 21, 2010 at 4:30 PM

Nope.

Jan 21, 2010 at 5:08 PM

Thanks for the quick response anyway.

Jul 15, 2010 at 4:55 PM
Edited Jul 15, 2010 at 4:58 PM

For anyone still struggling, here's how I did it (after several frustrating hours):

1) Create a static property to hold the currently selected row of the datagrid.

static int currentlySelectedImportRow { get; set; }
2) At the end of the SelectionChanged method for the datagrid, populate the property to hold the new index.
currentlySelectedRow = ThisDataGrid.SelectedIndex;

3) Based on code from http://social.msdn.microsoft.com/forums/en-US/wpf/thread/63974f4f-d9ee-45af-8499-42f29cbc22ae, create static methods to get a cell within a datagrid.

        public static DataGridCell GetCell(DataGrid dg, int row, int column)
        {
            DataGridRow rowContainer = GetRow(dg, row);

            if (rowContainer != null)
            {
                DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);

                // try to get the cell but it may possibly be virtualized
                DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
                if (cell == null)
                {
                    // now try to bring into view and retreive the cell
                    dg.ScrollIntoView(rowContainer, dg.Columns[column]);
                    cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
                }
                return cell;
            }
            return null;
        }

        static T GetVisualChild<T>(Visual parent) where T : Visual
        {
            T child = default(T);
            int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < numVisuals; i++)
            {
                Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
                child = v as T;
                if (child == null)
                {
                    child = GetVisualChild<T>(v);
                }
                if (child != null)
                {
                    break;
                }
            }
            return child;
        } 

        public static DataGridRow GetRow(DataGrid dg, int index)
        {
            DataGridRow row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
            if (row == null)
            {
                // may be virtualized, bring into view and try again
                dg.ScrollIntoView(dg.Items[index]);
                row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
            }
            return row;
        }
4) Create a static method which will set the index for the datagrid according to the value of the property, and focus on the first cell of the appropriate row.
        static object RestoreSelectedRow(object sender)
        {
            DataGrid thisDG = (DataGrid)sender;
            thisDG.SelectedIndex = currentlySelectedRow;

            DataGridCell cell = GetCell(impFiles, currentlySelectedRow, 0);
            cell.Focus();

            return null;
        }
5) In the SelectionChanged event for the DataGrid, use BeginInvoke to launch a new thread which will set the index back to the row which failed validation.

ThisDataGrid.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(RestoreSelectedRow), ThisDataGrid);
6) At the end of the SelectionChanged event, if the datagrid row fails validation, exit without changing the value of the currentlySelectedRow property.