Why does one of MY WPF DataGrids give the “'Ed

2019-05-26 19:50发布

问题:

I have read all the Q&A I could find here and on the MS forums about this exception, and tried most of the suggestions that I understood, and a few others. It seems that this exception can come up for a wide range of causes.

As with others, I have a WPF DataGrid bound to a collection, which throws this exception when one tries to edit one of the cells. They are set to be write-able, the collection is an ObservableCollection, I've implemented get and set handlers which send notification messages.

The suggestions I haven't tried are the ones involving implementing IList's non-generic interface, because I have no idea what I would do to do that. Also, I have many DataGrids bound to various lists and collections in my app which work, and this one used to work when it was bound to a LINQ collection.

Please help me figure out what I need to do here.

The Data Grid is:

<DataGrid Name="dgIngredients" Margin="567,32,0,44" Width="360" ItemsSource="{Binding}" IsReadOnly="False"
           AutoGenerateColumns="False" HorizontalAlignment="Left" CanUserAddRows="False" CanUserDeleteRows="False">
    <DataGrid.Columns>
        <DataGridTextColumn Width="63" Header="Percent" Binding="{Binding Preference}" IsReadOnly="False" />
        <DataGridTextColumn SortDirection="Descending" Width="301" Header="Ingredient" Binding="{Binding Ingredient}" IsReadOnly="True" CanUserSort="True" CanUserReorder="False" />
    </DataGrid.Columns>
</DataGrid>

The column being edited is the non-read-only one, Preference.

The collection is:

private ObservableCollection<RAM_Ingredient> MemberIngredientPrefs = new ObservableCollection<RAM_Ingredient>();

The binding is:

dgIngredients.DataContext = MemberIngredientPrefs.OrderBy("Ingredient",true);

RAM_Ingredient is:

public class RAM_Ingredient : INotifyPropertyChanged 

etc.

Where RAM_Ingredient.Preference is:

private int _Preference;
public int Preference
{
    get
    {
        return _Preference;
    }
    set
    {
        // This is needed to send notification of changes (and to not throw an exception on grid edit!):
        if ((_Preference != value))
        {
            SendPropertyChanging();
            _Preference = value;
            SendPropertyChanged("Preference");
        }
    }
}

The exception is:

System.InvalidOperationException was unhandled
  Message='EditItem' is not allowed for this view.
  Source=PresentationFramework
  StackTrace:
       at System.Windows.Controls.ItemCollection.System.ComponentModel.IEditableCollectionView.EditItem(Object item)
       at System.Windows.Controls.DataGrid.EditRowItem(Object rowItem)
       at System.Windows.Controls.DataGrid.OnExecutedBeginEdit(ExecutedRoutedEventArgs e)

etc...

回答1:

I have also this problem, And found that the point here is that we can not edit a IEnumerable in a DataGrid, only a list can be edited.

therefore we didn't need to create a new class, its works also on a LINQ query with anonymous return type. it's need only to be a list.

here is a sample of my code:

 dtgPrdcts.ItemsSource= ProductLists.Select(Function(x) New With {.ListTitle = x.ListTitle, .ProductID = x.ProductID, .License = "", .ForRemove = True}).ToList


回答2:

I still don't know what specifically caused the problem, but I managed to work around it, and I'm not sure how much of what I did was overkill, but it works.

I created a new class just for the purpose of holding the data in the DataGrid rows. I make a List of objects of this class and fill it in and bind it to the DataGrid as I was doing before. I also added the usual stuff and nonsense for getting Change Notification to work (probably overkill) and I had to re-define a comparison function in a different way to get it to sort because of that whole comedy situation.

i.e.

List<UsablePref> MemberIngredientPrefs = new List<UsablePref>();

...

            foreach (RAM_Ingredient ingredient in App.Ingredients)
        {
            ingredient.GetPreferences(EditorMember);
            UsablePref pref = new UsablePref();
            pref.Ingredient = ingredient.Ingredient;
            pref.IngredientID = ingredient.IngredientID;
            pref.Preference = ingredient.Preference;
            MemberIngredientPrefs.Add(pref);
        }

        // Sort alphabetically by ingredient name, 
        MemberIngredientPrefs.Sort(UsablePref.CompareByName);
        // and bind the ingredient prefs DataGrid to its corresponding List
        dgIngredients.DataContext = MemberIngredientPrefs;


回答3:

I had this same problem trying to create a list of rows from a join; since the LINQ query returns an IEnumerable, I had the DataGrid bound to that IEnumerable; this worked fine for readonly and oddly worked with ComboBoxes and some other custom controls I used, but plain text editing threw the InvalidOperationException. The solution was an ObservableCollection in place of the IEnumerable; basically from:

BoundView = (/*LINQ QUERY*/); // is IEnumerable<CustomJoinObject>

to

BoundView = new ObservableCollection<CustomJoinObject>(/*LINQ QUERY*/);

In both cases BoundView is the DataContext for the DataGrid.

I'm assuming this happens because IEnumerable doesn't have the machinery to support a datagrid, whereas ObservableCollection does.



回答4:

The model class needs to implement the interface INotifyPropertyChanged coming from the namespace System.ComponentModel.

Class example:

 public class Exemple :  INotifyPropertyChanged
 {
   #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #endregion INotifyPropertyChanged Members

 }