Why does one of MY WPF DataGrids give the "'EditItem' is not allowed for this view" exception? - wpf

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...

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

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;

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.

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
}

even if this thread is old, it could help someone.
The issue is that LINQ returns a IEnumerable, as said VaelynPhi, but the cause is that editing a datagrid requires a source that implements IEditableCollectionView.
You can then use a list, an observableCollection or any collection implementing this interface.
I found this solution thanks to Shoe who gave an answer on this tread.

In my case this exception was thrown when I wanted to edit some cells. The problem was of wrong collection type bound to ItemSource => when I switched from IEnumerable<T> to ObservableCollection<T> everything works correctly.

Related

Rebinding Observable collection wpf

I created a WPF window to bind data in Datagrid based on the selection of date by the user.BY default it loads for a specific date which works fine. But When the date is changed, the grid is showing empty rows.but the observable collection I used is having data. The observable collection is of DataTable type.
Note: I used to set itemsource=null when there are no records since I am using the same grid for 2 different tables based on a radio button check.
I have set the public variable as Binding variable in the XAML, used INOtifyChanged interface.
My issues is when the same collection reloads, the Datagrid failed to bind and shows empty rows but generating columns. When the previous attempt set the itemsource as null, the current loading failed to load the column also.
Any generic scenario, I am facing,? Please help
My Code:
private ObservableCollection<DataTable> custInfoCol = new ObservableCollection<DataTable>();
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private DataTable _CustInfo;
public DataTable CustInfo
{
get { return _CustInfo; }
set
{
_CustInfo = value;
PropertyChanged(this, new PropertyChangedEventArgs("CustInfo"));
}
}
private void rbPrev_Checked(object sender, RoutedEventArgs e)
{
custInfoCol.Clear();
custInfoCol.Add(CustInfo = showcustomer(cid));
}
Sounds like you might need to change the UpdateSourceTrigger in your binding:
Height="{Binding Height, ElementName=Day, UpdateSourceTrigger=PropertyChanged}"
I've had problems with the default behavior of it before, so now I just type it out every time.
https://msdn.microsoft.com/en-us/library/system.windows.data.binding.updatesourcetrigger%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
Based on this piece of code you provided in the comment:
<DataGrid ItemsSource="{Binding custInfo,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
I think there is typo in the Xaml. You should use ... Binding CustInfo... with capital C.
Of course, if AutoGenerateColumns is true, then I guess this is not the problem, because you wrote
Datagrid failed to bind and shows empty rows but generating columns
Which the Binding works. Therefore, you need to check showcustomer(cid) and make sure it contains the required DataRows.
I fixed the issue which is very simple. I had created the datatable instance as global and i changed it to local just before loading the executereader() method.It resolved the issue.

wpf binding to static resource

So I have aproblem with binding in WPF. I am trying to bind a DataGrid ComboBoxColumn with a static resource but with no luck. I know where the problem is but I'm not sure how to fix it.
in XAML i have this:
<local:MyClasificators x:Key="clList"></local:MyClasificators>
and DataGridComboBoxColumn
<DataTemplate>
<ComboBox ItemsSource="{StaticResource clList}" DisplayMemberPath="Value" ></ComboBox>
</DataTemplate>
code for the source I'm binding:
public class MyClasificators:List<KeyValuePair<object, object>>
{
public void _MyClasificators(DataTable country)
{
foreach (DataRow row in country.Rows)
{
this.Add(new KeyValuePair<object, object>(row.ItemArray[0], row.ItemArray[1]));
}
}
And the code for passing the DataTable:
public void callMyClassificators(DataTable country)
{
MyClasificators clasif = new MyClasificators();
clasif._MyClasificators(country);
}
I know that most probably I just have to edit the Resource part, but I'm not sure how should I go about it?
<local:MyClasificators x:Key="clList"></local:MyClasificators>
translates to something like:
Resources.Add("clList", new MyClasificators());
That's it, there's no data in your object.
You could create the resource clList from code, for example in app.xaml.cs:
var countryTable = ... // Get or create table here
var clList = new MyClasificators();
var clList.callMyClassificators(countryTable);
Resources.Add("clList", clList);
From above code, it seems that an instance of MyClasificators is created in resource section. But MyClasificators (clList) does not have any items. It's an empty list. Place a break point in your code and check this.Resources["clList"] and check the count of items in it.
I see multiple problems.
in callMyClassificators, you create a new instance of MyClasificators. That instance is not the one you bind in Xaml. When you define a local resource, one instance is created there. That's the one your Combox is bound to, not the one you create in callMyClassificators. You should make sure xaml and code work on the same instance.
Let's say you fix No.1. When is "callMyClassificators" called? After the binding is done, there is no way for your MyClasificators to notify WPF that the list has changed. You could use a ObservableCollection> so that collection change will automatically be observed by WPF.

A simpel WPS using MVVM and EF

I'm on the edge of going nuts.
I have a working WinForms/database application, that I try to rebuild with WPF. My main problem is that I haven't worked with MVVM before and I can't seem to get the grip of the databinding from Entity Framework to the View.
My WinForms is build exclusively using code-behind, which I know is bad praxis, but it works.
I have read about 100 articles, tutorials and examples, downloaded a couples of demo/samples using MVVM. Including "WPF Application Framework (WAF)"
But I haven't found a simple solution/sample how to use EF as the Model or as a dataprovider for the Model. And to pass the information on to to the ViewModel, and lastly bind it from the View.
All the tutorials I have read only describe fetching data from a static list, I need the usual CRUD operations on the database.
I know questions like this are being asked all the time here, but I haven't being able to find an answer to pushing data from EF to the View(Model) and update back through EF. I hope that some of you can help a (must be retarded) person like me with some guiding.
The MVVM pattern hinges, a lot, on the INotifyPropertyChanged interface which EF implements so some of the work here is done for you.
I am not too sure on your particular requirement but start off by creating a view mode for your window. Lets assume that you have a entity called Person with an attribute of FirstName as a string and an Id of Int32 (of course), and that all the work for the data layer is taken care of. I will show you how to list the Person entity and allow edit of the person entity as well.
ViewModel
public class Window1ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Person> _people;
private Person _selectedPerson;
public ObservableCollection<Person> People {
get { return _people; }
set { _people = value;
this.RaisePropertyChanged("People");
}
}
public Person SelectedPerson {
get { return _selectedPerson; }
set { _selectedPerson = value;
this.RaisePropertyChanged("SelectedPerson");
}
public Window1ViewModel() {
// Instead of setting to empty collection populate with data from EF.
this.People = new ObservableCollection<Person>();
}
private void RaisePropertyChanged(string propertyName) {
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Now that you have your ViewModel taken care of it is time to create the UI and bind it.
Binding ListBox
<ListBox ItemsSource="{Binding Source={StaticResource viewModel}, Path=People}" SelectedItem="{Binding Source={StaticResource viewModel}, Path=SelectedPerson, Mode=TwoWay}"/>
Binding TextBox
<TextBox Text="{Binding Source={StaticResource viewModel}, Path=SelectedPerson.FirstName, Mode=TwoWay}"/>
Lastly you could create a button on your Window, bind it to an ICommand on your ViewModel to call the Save() method in the EF layer.
There is a lot you could write to meet your requirement i hope this goes some way to assist. Also recommend the following information. http://www.codeproject.com/Articles/81484/A-Practical-Quick-start-Tutorial-on-MVVM-in-WPF

MVVM WPF DataGrid SelectedItem via binding fires OnPropertyChanged twice

I've spent quite a few hours trying to solve the problem described below:
I've got a DataGrid defined in my MVVM WPF application, the stripped down XAML code looks like this:
<DataGrid AutoGenerateColumns="False" Name="dgdSomeDataGrid" SelectedItem="{Binding SelectedSomeItem, Mode=TwoWay}" ItemsSource="{Binding SomeItemCollection}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Id}" Header="ID" />
<DataGridTextColumn Header="Titel" Binding="{Binding Path=Title}" />
<DataGridTextColumn Header="Status" Binding="{Binding Path=State}" />
</DataGrid.Columns>
</DataGrid>
In my associated ViewModel I've got a corresponding property like:
public WorkItemForUi SelectedSomeItem
{
get
{
return SomeObject.SelectedSomeItem;
}
set
{
SomeObject.SelectedSomeItem = value;
OnPropertyChanged( "SelectedSomeItem" );
}
}
In my controller I've got the following:
private void MainWindowViewModelPropertyChanged( object sender, PropertyChangedEventArgs e )
{
if ( e.PropertyName == "SelectedSomeItem" )
{
UpdateSelectedSomeItem();
}
}
What I generally want to do is to retrieve the selected item from the DataGrid, get some more information about the item from an external data store (a TFS in this case) and show that extra information in a TextBox.
All of this already works as expected, but the problem is that the MainWindowViewModelPropertyChanged method is called twice, not once.
It may be the case that it is by design for the SelectedItem property being set to happen twice, but I'm not quite sure as a lot of information I found is a bit contradictory (and also sometimes not quite clear whether Windows Forms or WPF are meant).
I've seen some suggestions where a SelectionChanged event handler is defined for the DataGrid and an IsSelected property is used, but as far as I know this shouldn't be necessary due to my data binding.
Update
As part of the MainWindowController there is an Initialize method that references the MainWindowViewModelPropertyChanged handler.
public void Initialize( string tfsProjectCollection )
{
InitializeCommands();
InitializeViewModel();
AddWeakEventListener( m_MainWindowViewModel, MainWindowViewModelPropertyChanged );
}
Any ideas what might be the cause of my problem?
Does your SomeObject.SelectedSomeItem setter raise also OnPropertyChanged( "SelectedSomeItem" );? what is the type of SomeObject? why does SomeObject also need the SelectedSomeItem Property?
Please also post some code where you subscribe the MainWindowViewModelPropertyChanged.
I never had problem with the selecteditem behavior, but to be fair I did not need to subscribe to INotifyPropertyChanged to get this information. and i think you did not need that too. there are better way to communicate between viewmodels
EDIT: this works, but i dont know what SomeObject is in your code.
private WorkItemForUi _selected;
public WorkItemForUi SelectedSomeItem
{
get
{
return this._selectedSomeItem;
}
set
{
this._selectedSomeItem = value;
OnPropertyChanged( "SelectedSomeItem" );
}
}
Alright, after spending some more time on this we found out that the problem was in the ApplicationController class.
The constructor called the Initialize method in that class, and the Run method in the same class also called this method.
Within the Initialize method there was a call to the main window's view model `Initialize´ method in which an event listener was added:
[...]
AddWeakEventListener( m_MainWindowViewModel, MainWindowViewModelPropertyChanged );
[...]
Removing the call to the Initialize method from the constructor of the ApplicationController class solved the problem.

WPF User Control Binding Issue?

I have this:
public MyView: UserControl
{
public IList<Person> PersonList { get; set; }
public MyView()
{
//code
}
public void Display(MyData myData)
{
DataContext=myData;
}
//code
}
The XAML for this includes a ComboBox :
ItemsSource="{Binding RelativeSource={RelativeSource Self}, Path=PersonList}"
For some reason this does not work and the combo box does not get populated ( however, If I use the code-behind and I say comboBox.ItemsSource = PersonList then the combo box does got populated ).
Any ideas ?
Regards,
MadSeb
Your property is set to private, and are you sure that you are setting the DataContext.
* EDIT *
Based on the change you made above, you're setting your datacontext incorrectly. Your "PersonList" is anIList<> on your MyView class, but you're setting your data context to something else.
Try adding items to PersonList within MyView and setting this.DataContext = this; Also, as suggested, switch your IList<> to an ObservableCollection<>.
I would also highly suggest reading up on the Model View ViewModel (MVVM) approach. It will help out a lot. Josh Smith has a lot of good articles about the MVVM approach (and has written a good book about it too).
Here's a link to his blog. His book is linked there, as well.
I suspect it's because you're not firing any property-changed events. If you don't notify your UI when the property's value is first set, the binding won't update. Look into the INotifyPropertyChanged interface and implement it in your class.
Similarly, if your IList property isn't an ObservableCollection or doesn't implement INotifyCollectionChanged, then when you add items to the list the databound UI won't reflect this.
I believe your binding statement is the problem.
"{Binding RelativeSource={RelativeSource Self}, Path=PersonList}" is looking for a "PersonList" on the combobox itself.
Are you seeing any binding errors in the output window?
Ideally you'd want to bind to a property in your DataContext (MyData)

Resources