简体   繁体   中英

How to select a row in datagrid from ViewModel in MVVM?

I'm trying to validate user input in a datagrid, the way i'm doing it is like:

I) allow user to add a row

II) allow user to fill some cells

III)when EndEdit() event happens, I would update my Model

IV) if Model could not be updated (for example when user didn't supply NotNull values), an error is raised.

V) HERE IS THE TRICKY PART! I want to stay on the faulty row of the datagrid, but the high lightened row will change despite the fact that I've set SelectedIndex and SelectedValue correctly.

here is my XAML:

<DataGrid x:Name="gd_Contacts" SelectedItem="{Binding SelectedContact,Converter={StaticResource NewPlaceConverter}}" 
              SelectedIndex="{Binding SelectedItemIndex}"
              Margin="0,5,1,0" ItemsSource="{Binding ContactCollection, Mode=TwoWay}" 
              CanUserAddRows="True" CanUserDeleteRows="False" AutoGenerateColumns="False" Grid.Column="1">

Here is my ViewModel:

private ObservableCollection<ActionEnabledContacts> FContactCollection;
//....
public ActionEnabledContacts SelectedContact //THE PROPERTY FOR SELECTEDITEM
    {
        get { return FSelectedContact; }
        set 
        { 
            //
            List <ActionEnabledContacts> InvalidList=ContactCollection.Where<ActionEnabledContacts>(p => p.cnt_Key == 0).ToList();
            if (InvalidList != null && InvalidList.Count > 0)
            {
                //now find index of that bastard
                List<ActionEnabledContacts> AllContacts = ContactCollection.ToList();
                int BastardIndex = 0;
                foreach (ActionEnabledContacts AContact in AllContacts)
                {
                    if (AContact.cnt_Key == 0)
                    {
                        FSelectedContact = InvalidList[0];
                        NotifyPropertyChanged();
                        SelectedItemIndex = BastardIndex;
                        //^^^^ THIS LINE DO UPDATE SELECTEDINDEX AND CALL NotifyPropertyChanged()
                    }
                    BastardIndex++;
                }
            }
            else
            {
                //
                FSelectedContact = value;
                NotifyPropertyChanged();
            }
        }
    }

You can't change the selection while the selection is changing... If that makes any sense :P

If the selection has just changed, you'll have to wait until you can change it programmatically once again. This would be usually done using Dispatcher.BeginInvoke to add your call to the Dispatcher queue, so it executes after the current selection is processed.

if (AContact.cnt_Key == 0)
{
    FSelectedContact = InvalidList[0];
    NotifyPropertyChanged();

    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
        SelectedItemIndex = BastardIndex));
}
BastardIndex++;

Doing this in your viewmodel is not exactly the best solution, though - MVVM-wise or in multiple UI threads scenarios. You could simply raise an event in that line (passing the index inside the EventArgs ), have your view subscribe to that event, and fire the Dispatcher call from the event handler (using this.Dispatcher instead of Application.Current.Dispatcher ).

Thanks to @almulo, I've implemented an event in the ViewModel and corresponding handler in View and it works like a charm, for anyone out there who might need help on this, these are the steps:

I) in ViewModel add an event like this:

public event EventHandler<EventArg<int>> DataGridRowSelectionChange;

*note that EventArg is just a simple class that can hold a value of type T as follows:

    public class EventArg<T> : EventArgs
{
    private T FEventData;

    public EventArg(T Param)
    {
        // TODO: Complete member initialization
        FEventData = Param;
    }

    public T EventData
    {
        get { return FEventData; }
        set { FEventData = value; }
    }
}

II) in ViewModel whenever you want to change the selected row :

if (DataGridRowSelectionChange != null)
     DataGridRowSelectionChange(this, new EventArg<int>(DesiredIndex));

III) in the View constructor add the event handler , like this:

((YourViewModelClass)DataContext).DataGridRowSelectionChange += CurrentViewModel_DataGridRowSelectionChange_EventHandler;

IV) and finally write the handler in the View like this:

void CurrentViewModel_DataGridRowSelectionChange_EventHandler(object sender, EventArg<int> e)
    {
       Dispatcher.BeginInvoke(new Action(() => GRIDNAME.SelectedIndex = e.EventData));
    }

and that's all :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM