Wpf DataGrid SelectedItem loses binding after cell

2019-05-30 06:53发布

问题:

I have a DataGrid bound to a collection of items (Rules). If I edit one of these items manually in the DataGrid, it seems like the SelectedItem binding on the grid stops working (RuleListViewModelPropertyChanged in the controller is never called again). But only if I actually change the value of the item in the cell, otherwise SelectedItem keeps working like it should.

I have stripped the code of some irrelevant things, so this is basically what I have. First, I have the following DataGrid:

<DataGrid x:Name="RuleTable" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding Rules}" SelectedItem="{Binding SelectedRule, Mode=TwoWay}" 
               BorderThickness="0" >
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding TargetValue, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, 
                                  ValidatesOnExceptions=True, NotifyOnValidationError=True}" 
                                Header="{x:Static p:Resources.TargetValue}" Width="*" ElementStyle="{StaticResource TextCellElementStyle}"
                                EditingElementStyle="{StaticResource TextCellEditingStyle}"/>
        </DataGrid.Columns>
    </DataGrid>

With a ViewModel that looks like this:

public class RuleListViewModel : ViewModel<IRuleListView>
{
    private IEnumerable<Rule> rules;
    private Rule selectedRule;

    public RuleListViewModel(IRuleListView view)
        : base(view)
    {
    }

    public RuleListViewModel() : base(null) {}

    public IEnumerable<Rule> Rules
    {
        get
        {
            return rules;
        }
        set
        {
            if (rules != value)
            {
                rules = value;
                RaisePropertyChanged("Rules");
            }
        }
    }

    public Rule SelectedRule
    {
        get
        {
            return selectedRule;
        }
        set
        {
            if (selectedRule != value)
            {
                selectedRule = value;
                RaisePropertyChanged("SelectedRule");
            }
        }
    }
}

And finally a Controller that looks like this:

public class RuleController : Controller
{
    private readonly IShellService shellService;
    private readonly RuleListViewModel ruleListViewModel;

    private readonly DelegateCommand addRuleCommand;
    private readonly DelegateCommand deleteRuleCommand;

    [ImportingConstructor]
    public RuleController(IShellService shellService, IEntityService entityService, RuleListViewModel ruleListViewModel)
    {
        this.shellService = shellService;
        this.ruleListViewModel = ruleListViewModel;
    }

    public void Initialize()
    {
        AddWeakEventListener(ruleListViewModel, RuleListViewModelPropertyChanged);
        shellService.RuleListView = ruleListViewModel.View;

        List<Rule> rules = GetRules();
        ruleListViewModel.Rules = new ObservableCollection<Rule>(rules);
    }

    private void RuleListViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "SelectedRule")
        {
            // This never gets called after edit
        }
    }
}

I have really no idea what the problem is, but since I actually have to change the value to experience this behavior (only clicking in the cell and not editing anything works fine) I'm guessing it has something to do with the SelectedItem binding breaking when I change the value of the item bound to it?!

回答1:

For anyone who comes across this thread and you are using ObservableCollection - check to see if you override GetHashCode() (I was doing equality testing via IEquatable). The bindings to SelectedItem and SelectedIndex break as soon as you make a change to a cell.

From MSDN (https://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=vs.110).aspx):

"You can ensure that the hash code of a mutable object does not change while the object is contained in a collection that relies on its hash code."

And of course I was getting a hash on the editable (i.e. mutable) field...



回答2:

I think it's because datagrid uses Bindingroups, So when you entered in a cell every UI event is by-passed until you validate and leave the row.

read this post it might helps :WPF DataGrid source updating on cell changed



回答3:

So it turns out that it was because I forgot to let my data model inherit from System.Waf.Applications.DataModel (Maybe I should have mentioned that I'm using WPF Application Framework).

I guess it had to do with PropertyChanged events not being handled.