1

Context

Using Entity Framework 6 I have an ObservableCollection of Airframe objects each of which has a subordinate collection of Identity objects. Through XAML and a view model I'm viewing this collection one airframe (master) at a time with each of it's Identity (detail) objects in a DataGrid. To handle the display / update of just one Airframe at a time I'm using a CollectionView so that I get current positioning within the collection and can wire up "Goto next" and "Goto previous" buttons and commands. A simplified code extract:

Code behind

private ADBContext databaseContext;
private UnitOfWork unitOfWork;
private ViewModels.ViewModel ViewModel;
public MainWindow()
{
    InitializeComponent();

    databaseContext = new ADBContext();
    unitOfWork = new UnitOfWork(databaseContext);

    ViewModel = new ViewModels.ViewModel(unitOfWork);

    this.DataContext = ViewModel;
}

View model

    public class ViewModel : INotifyPropertyChanged
    {         
        public CollectionView AirframeCollectionView { get; set; }

        public IUnitOfWork UnitOfWork;

        public ViewModel(IUnitOfWork unitOfWork)
        {
            UnitOfWork = unitOfWork;

            AirframeCollectionView = CollectionViewSource.GetDefaultView(new ObservableCollection<Airframe>(UnitOfWork.Airframes.GetAirframesForRegistration(SearchRegistration)));

            RaisePropertyChanged("AirframeCollectionView");
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

XAML - Master

<Grid.ColumnDefinitions>
    <ColumnDefinition Name="airframeLabels" MaxWidth="100"/>
    <ColumnDefinition Name="airframeDetails"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
    <RowDefinition Name="typeRow"/>
    <RowDefinition Name="constructionNoRow"/>
    <RowDefinition Name="remarksRow"/>
    <RowDefinition Name="rolledOutDateRow"/>
    <RowDefinition Name="firstFlightDateRow"/>
    <RowDefinition Name="statusRow"/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0">Type</Label>
<wpf:AutoCompleteTextBox Grid.Column="1" Grid.Row="0" 
                         Text="{Binding Path=AirframeCollectionView/TypeName}"
                         Provider="{Binding TypeNameStubSuggestions}"/>
<Label Grid.Column="0" Grid.Row="1">Construction no</Label>
<TextBox Grid.Column="1" Grid.Row="1" Margin="5" Name="constructionNo" 
         Text="{Binding AirframeCollectionView/ConstructionNumber}"/>
<Label Grid.Column="0" Grid.Row="2">Remarks</Label>
<TextBox Grid.Column="1" Grid.Row="2" Margin="5" Name="remarks"
         Text="{Binding AirframeCollectionView/Remarks}"/>
 <Label Grid.Column="0" Grid.Row="3">Rolled out</Label>
 <DatePickerTextBox Grid.Column="1" Margin="5" Grid.Row="3" Name="rolledOut" 
                    Text="{Binding AirframeCollectionView/RolloutDate, StringFormat=\{0:dd-MMM-yy\}}"/>
<Label Grid.Column="0" Grid.Row="4">First flight</Label>
<DatePickerTextBox Grid.Column="1" Margin="5" Grid.Row="4" Name="firstFlight" 
                   Text="{Binding AirframeCollectionView/FirstFlightDate, StringFormat=\{0:dd-MMM-yy\}}"/>
<Label Grid.Column="0" Grid.Row="5">Status</Label>
<ComboBox Grid.Column="1" Grid.Row="5" Margin="5" Name="status"
          ItemsSource="{Binding AirframeStatuses}"
          SelectedValue="{Binding AirframeCollectionView/StatusId, Mode=TwoWay}"
          SelectedValuePath="StatusId"
          DisplayMemberPath="StatusName"
          SelectedItem="{Binding AirframeCollectionView/StatusId}"/> 

XAML - Detail

<DataGrid Name="identitiesGrid"
          AutoGenerateColumns="False"
          ItemsSource="{Binding AirframeCollectionView/Identities, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          IsReadOnly="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Regn" Binding="{Binding Registration, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False"/>
</DataGrid>

Issue

This displays the data perfectly and I can edit the master data without a problem. When I click into any of the rows in the detail DataGrid however the first click selects the cell and the second click causes an exception "An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll with 'EditItem' is not allowed for this view`".

Question

How do I stop this exception?

ifinlay
  • 621
  • 1
  • 7
  • 24

1 Answers1

2

How do I stop this exception?

You need to make sure that the type of your "Identities" property supports editing. It should implement the IList interface. HashSet<T> does not for example but List<T> and ObservableCollection<T> do.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • In the database I have an Airframes table with a foreign key relationship to an Identities table. I access this structure through Entity Framework using the repository pattern and hence a UnitOfWork.Airframes.GetAirframesForRegistration method which returns an IEnumerable with EF giving each Airframe an Identities property. This is loaded into an ObservableCollection and thence to the CollectionView AirframeCollectionView. My assumption was that this arrangement would have the Identities properties supporting editing. Am I mistaken? – ifinlay Dec 11 '16 at 17:39
  • Yes, it is the Identities navigation property of the Airframe class that must (also) support editing, i.e. it should implement the IList interface. The auto-generated entity classes defines the navigation properties as ICollection<T> and these collections are unfortunately not editable in WPF DataGrids: http://stackoverflow.com/questions/6508479/edititem-is-not-allowed-for-this-view-wpf-net-4-ef-4-1-master-detail – mm8 Dec 11 '16 at 17:46
  • Ok, understood. That implies therefore that I need to place my identities into a separate structure, perhaps an ObservableCollection, for display / update. Will Entity Framework still know that the Airframe and it's Identities are linked when I issue SaveChanges() or must I do something to re-link them? – ifinlay Dec 11 '16 at 17:56
  • EF doesn't really care about whether the class exposes a navigation property as ICollection or IList. This is another question really but you should be able to modify the .tt template file to generate properties of a compatible type for you: http://stackoverflow.com/questions/20761622/entity-framework-does-not-generates-observablecollection. – mm8 Dec 11 '16 at 18:14
  • Great, I have it working now. Your last link especially useful. This does all seem a bit clunky for editing a master / detail collection in WPF which I'd guess is a very common requirement. Maybe it's something MS will look at in the future. Anyway, sincere thanks for your input on this. – ifinlay Dec 12 '16 at 21:52