tags:

views:

38

answers:

2

I have an ObservableCollection of ChildViewModels with somewhat complex behaviour.

When I go to edit a row - the DataGrid goes into 'edit-mode' - this effectively disables UI-notifications outside the current cell until the row is committed - is this intended behaviour and more importantly can it be changed?

Example:

public class ViewModel
{
    public ViewModel()
    {
        Childs = new ObservableCollection<ChildViewModel> {new ChildViewModel()};
    }
    public ObservableCollection<ChildViewModel> Childs { get; private set; }
}
public class ChildViewModel : INotifyPropertyChanged
{
    private string _firstProperty;
    public string FirstProperty
    {
        get { return _firstProperty; }
        set
        {
            _firstProperty = value;
            _secondProperty = value;
            OnPropetyChanged("FirstProperty");
            OnPropetyChanged("SecondProperty");
        }
    }

    private string _secondProperty;
    public string SecondProperty
    {
        get { return _secondProperty; }
        set
        {
            _secondProperty = value;
            OnPropetyChanged("SecondProperty");
        }
    }

    private void OnPropetyChanged(string property)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

And in View:

<Window.Resources>
    <local:ViewModel x:Key="Data"/>
</Window.Resources>
<DataGrid DataContext="{Binding Source={StaticResource Data}}" ItemsSource="{Binding Childs}"/>

Notice how the second notification when editing first column is hidden until you leave the row.

EDIT: Implementing IEditableObject does nothing:

public class ChildViewModel : INotifyPropertyChanged,IEditableObject
{
    ...
    private ChildViewModel _localCopy;

    public void BeginEdit()
    {
        _localCopy = new ChildViewModel {FirstProperty = FirstProperty, SecondProperty = SecondProperty};
    }

    public void EndEdit()
    {
        _localCopy = null;
    }

    public void CancelEdit()
    {
        SecondProperty = _localCopy.SecondProperty;
        FirstProperty = _localCopy.FirstProperty;
    }
}
A: 

ok, so, here is the problem. Observable Collection does NOT notify of objects that it contains changing. It only notifies on add/remove/etc. operations that update the collection is-self.

I had this problem and had to manually add my columns to the datagrid, then set the Binding item on the Column object. so that it would bind to my contents.

Also, I made the objects that are in my ICollectionView derive from IEditableObject so when they are "updated" the grid will refresh itself.

this sucks, but its what i had to do to get it to work.

Optionally, you could make your own ObservableCollection that attaches/detaches property changed handlers when an item is addeed and remove.

Muad'Dib
Yes - but the childs inside the collection notifies as they implement INotifyPropertyChanged. Oh - was too quick - will try.
Goblin
Okay - having added IEditable interface did nothing - notice that the object in question is already in the collection - it is when I edit a property that notifications to other properties fail to fire before the row is committed.
Goblin
I used the IEditableObject bc my collection was an ICollectionView. If you dont change your collection, you still have to attach your property changed events to the items in the collection. also, i dont know that the auto gen'd columns add a binding or not.
Muad'Dib
The autogenerated columns are bound, and the properties they are bound to are notified properly - if you try my example you'll see that the columns do update - just first when I leave the row - ie. I edit the first column to a new value and then promptly leave the row - then they both update as they should, but if I <Tab> to the second column - no update happens on the second column.
Goblin
+2  A: 

This behavior is implemented in DataGrid using BindingGroup. The DataGrid sets ItemsControl.ItemBindingGroup in order to apply a BindingGroup to every row. It initializes this in MeasureOverride, so you can override MeasureOverride and clear them out:

public class NoBindingGroupGrid
    : DataGrid
{
    protected override Size MeasureOverride(Size availableSize)
    {
        var desiredSize = base.MeasureOverride(availableSize);
        ClearBindingGroup();
        return desiredSize;
    }

    private void ClearBindingGroup()
    {
        // Clear ItemBindingGroup so it isn't applied to new rows
        ItemBindingGroup = null;
        // Clear BindingGroup on already created rows
        foreach (var item in Items)
        {
            var row = ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
            row.BindingGroup = null;
        }
    }
}
Quartermeister
Amazing! Thank you so very much! This had me tearing out hair by the handfuls :) I'll award the bounty as soon as I can.
Goblin