CSLA for WPF and WPF DataGrid support.

CSLA for WPF and WPF DataGrid support.

Old forum URL: forums.lhotka.net/forums/t/10233.aspx


Devman posted on Monday, April 04, 2011

Hi,

I'm trying to use CSLA with the DataGrid control but have hit a snag when it comes to Undo and more specifically Model.CancelEdit().

After a call to Model.BeginEdit(), I Add a new row to the DataGrid, then Edit a DataGridTextColumn, followed by cancelling the action with Model.CancelEdit();

This causes an "Edit Level Mismatch in UndoChanges" exception.

Editing a DataGridTemplateColumn does not cause this behaviour

Is there a solution to this other than having to use DataGridTemplateColumns for each column?

Regards

ajj3085 replied on Monday, April 04, 2011

What version of Csla are you using?

Devman replied on Tuesday, April 05, 2011

Im using 4.1.0.

 

On further searching, this looks simliar to a issue brought up in the forum back in 2009 before the DataGrid was released and was part of the WPFToolkit.

http://forums.lhotka.net/forums/p/7033/33676.aspx#33676

 

 

 

RockfordLhotka replied on Tuesday, April 05, 2011

I've traded emails with at least one of the guys who own that control at Microsoft, so they are aware that the control doesn't act properly - or at least is unique in its behavior when compared to Windows Forms or Silverlight controls.

I suspect it would be helpful for you (and anyone else who cares) to report these issues on connect.microsoft.com as well, so there's community pressure to fix the control.

Devman replied on Thursday, April 07, 2011

Hi Rocky,

I've had a look on the connect website to see if this has been posted before and it hasn't.  I'd log the issue on there, but without wanting to sound foolish im not sure i could give enough detail on the problem as I don't fully understand what specifically is lacking with the DataGrid and what it "should" be doing.

Regards

David

RockfordLhotka replied on Thursday, April 07, 2011

Here is some specific detail (copied from an email):

 

My continual challenge with the WPF datagrid is that it isn’t 100% compliant with the way the Windows Forms or Silverlight datagrids interact with the various data binding interfaces and events.

 

Since my CSLA 4 base classes provide consistent support across all three platforms (and WP7, but there’s no datagrid there), it is problematic when the WPF datagrid doesn’t operate in a way that is consistent with the other smart client UI technologies.

 

Though it is also possible that WPF is requiring some extra interface or nuance I’m missing – that’s also possible.

 

Issues:

 

1.       The datagrid doesn’t use the AddNew method to add new items to a collection, nor does there appear to be a way to customize how the new object is created – the datagrid requires a public default constructor – this is non-workable in many business scenarios – the WinForms and SL datagrids either honor the AddNew mechanism or have a way to customize how new items are created

2.       The datagrid doesn’t properly invoke the IEditableObject interface if it is implemented by the items in a collection – most notably, the EndEdit method isn’t invoked when the user successfully leaves or deletes a row, so the item doesn’t know that the user is done editing the item

 

The first issue is a globally bad thing for pretty much all collections.

 

The second issue is a problem for CSLA, because I rely on proper use of IEditableObject to manage elements of state within objects. One common scenario that fails is a type of collection I have where changes the user makes in a datagrid are automatically committed as the user leaves each row. Since there’s no way to detect when a user has left a row in the WPF datagrid this collection type fails in WPF.

 

It works great in Silverlight and Windows Forms, where IEO is invoked correctly.

 

Devman replied on Thursday, April 07, 2011

Ok, Thanks Rocky for the details.

I'll submit a post to the connect website and link this thread to it.

Regards

David

MadGerbil replied on Tuesday, June 14, 2011

Two questions:

#1: It's been another two months.  I want to create a standard template for all projects going forward but this buggy datagrid is a real issue.  Has anything happened with it since the bugs have been reported?

#2: How are people working around these bugs?  I currently comment out "AddNewCore()" and I include MarkAsChild() in the constructor of the child objects.  Is this advisable?  Thoughts?

MadGerbil replied on Tuesday, June 14, 2011

My stars, when I use the WPF grid NewChild isn't even called.   Am I really limited to initializing my new child object inside the default constructor?  Tell me this isn't true.   Does the DataGrid completely bypass the DataPortal?

RockfordLhotka replied on Tuesday, June 14, 2011

The standard WPF datagrid does not implement standard data binding behaviors, no. This makes it really hard to use against any source objects (like CSLA objects or the DataTable) that expect standard binding behaviors. I've reported this to the team a couple times, but they don't seem inclined to fix the issues.

Devman replied on Wednesday, June 15, 2011

Never got round to posting on connect as Im working on something else at present, apologies for that.

Here's the post if anyone want to "vote" to increase its importance or add to content.

https://connect.microsoft.com/WPF/feedback/details/675473/wpf-datagrid-add-new-behaviour-and-ieditableobject-invocation

Devman replied on Friday, June 17, 2011

Hi 

The guys from connect have asked for a repro. This is fair enough, I've tried recreating a small sample, however this project does not reproduce the error, it just works which is frustrating!!

see my sky drive for sample.

http://cid-044ad885bcfe52a6.office.live.com/self.aspx/.Public/DataGridTest.rar

Referring back to my initial issue, I managed to get the error to disappear in my company's application if do either of the following;

1.I remove "UpdateSourceTrigger=PropertyChanged"  from my DataGridTextColumn binding expression.

2.Use a DataGridTemplateColumn with a TextBox as the template.

 

Has/Can anyone else repro this error? The bug will be closed off on connect if this can't be reproduced.....

 

Regards

David

Sam Bent replied on Tuesday, June 21, 2011

RockfordLhotka

The standard WPF datagrid does not implement standard data binding behaviors, no. This makes it really hard to use against any source objects (like CSLA objects or the DataTable) that expect standard binding behaviors. I've reported this to the team a couple times, but they don't seem inclined to fix the issues.

 

Which "standard data binding behaviors" are you talking about?   The WPF 4.0 DataGrid supports IBindingList.AddNew and IEditableObject.EndEdit, at least in all the samples I'm aware of.   It works with DataTable.  It works with INotifyCollectionChanged and INotifyPropertyChanged.   It works with IBindingList.   There are bugs, but not of the magnitude you're citing.   Please explain.

Also, I've looked around for your "reports to the team", but I can't find them.   Can you help me?   Do you have bug numbers, emails, titles, other identifying info?

- Sam (WPF team)

RockfordLhotka replied on Tuesday, June 21, 2011

Hi Sam, thanks for jumping in, I appreciate it.

I think there are two broad areas.

First, a collection can't implement IBindingList and also be an ObservableCollection. This is a broader issue than the datagrid control, but it is really ugly. To play well with much of WPF a collection should be an ObservableCollection, but to play with certain controls (apparently including the .NET 4 datagrid) the collection needs to be a BindingList. Nasty! (Most of my testing over time has been with ObservableCollection types, with the assumption that BindingList was a dying type. This has been part of my frustration.)

Second, if we restrict the conversation to BindingList subclasses, the behavior of the datagrid is somehow pretty different from the WinForms equivalent.

  1. Although AddNew appears to work after at least one item exists in the collection, it doesn't work if the collection starts with 0 items. I think this is because there's no blank row added while the first row is being added/edited, so it isn't possible to get the focus out of that first row to commit the changes. But if at least one pre-existing row is in the datagrid, then as a new row is added, it is possible to shift the focus up to a pre-existing row.
  2. Editing an existing item is somehow quite different - and I'm not sure how or why. It almost appears like the datagrid makes a clone of the item being edited, and calls the IEO methods on that clone, not on the actual object that is in the collection. This causes CSLA to get confused, because when EndEdit is called, that's what triggers the collection to save that item to the database - but the item isn't actually in the collection, so this doesn't work. The behavior I'm seeing here is completely unlike the WinForms datagrid, and it isn't clear to me how this is supposed to work (or even can work - do you call EndEdit and then replace the original item in the collection with the clone?)
  3. I don't see how removing works? Maybe I don't know the magic keystroke to remove a row from the datagrid - I click on the row marker to the far left, and press the DEL key, but that does nothing.

Specifically for point 2 though - I'm just plain confused about what is going on.

When EndEdit is called, my object handles that call by telling the parent collection to save the now-edited child object. This causes the collection to find the child object in the collection - but it isn't there. In fact, at this point I can look in the collection and see the unedited items - so clearly the datagrid is manipulating some sort of copy or clone of the actual item.

That's very uncool...

Sam Bent replied on Friday, June 24, 2011

1. You don't need to move focus.  Type Enter - that commits the row.  Or type Ctrl-Enter - that commits the row and leaves it selected.

2. DataGrid doesn't clone anything.  It calls IEO.EndEdit on the actual item it's editing.

3. DEL removes the selected row, provided it isn't in edit mode.  To leave edit mode, type Enter (to commit) or ESC (to cancel).  You may need to ESC twice - once to leave cell-edit mode, and once to leave row-edit mode.   The DEL should work.

I sympathize with your confusion.  I'm also confused - the things you're trying to do work just fine for me (using ADO.net DataTable), modulo the little suggestions.   I don't understand (2) at all - I don't see anything resembling what you do.   That's why a repro would help, in case there's some oddity about your particular BindingList that we don't handle.

The only quirk about BindingList (vs. ObservableCollection) I've seen mentioned so far is the AddNew issue.  Is there more?

The AddNew issue is a dropped ball on our part.   In 4.0 we added IEditableCollectionViewAddNewItem to ListCollectionView, specifically to support the operation of "add an existing item to the collection".   The app creates the new item any way it likes, then calls AddNewItem to add it to the collection.   This was a request from the DataGrid feature team, but then they neglected to add anything to DataGrid to take advantage of it.   I'll see whether it's possible to get that added for the next release.   We all agree it's necessary.

RockfordLhotka replied on Friday, June 24, 2011

Thanks for all that info - it is very helpful.

Based on what you are saying, I have continued to try and figure out what is happening with #2. As a result I have a question.

Here's what is happening:

  1. The user edits a row
  2. When the row edit is accepted, the datagrid calls EndEdit
  3. The EndEdit method calls a method on the parent list to save the item
  4. The save process is n-tier and thus serializes the object, returning a new instance of the object as a result
  5. That new instance (result of the save) is put into the list such that it replaces the original item
  6. A ListChanged event is raised to indicate that the item has been changed (replaced)

This works the first time the user edits a row, and fails on subsequent tries - at step 3, because the item isn't actually in the list - the datagrid is editing a copy of the item.

I wonder if the datagrid control is operating against a view over the list, and not the list itself? And perhaps that view over the list isn't being updated based on that ListChanged event? So the datagrid control ends up editing a stale copy of the object, and not the one that's actually in the list?

This stuff is, of course, incredibly hard to debug. But my observation matches what I'm describing here (or so I think).

I can provide a repro, no problem. It includes the CSLA source code, because all the relevant code is in the interplay between the list base class and editable object base class in the CSLA framework. And that's not a problem from a repro perspective either, other than that CSLA is non-trivial.

 

Sam Bent replied on Monday, June 27, 2011

Please send me a repro.

DataGrid talks to the collection through a collection view (presumably BindingListCollectionView), but the view should be reacting to ListChanged events and keeping up to date.   You describe a re-entrant scenario - the ListChanged is happening while DataGrid is committing the row - which could be causing problems.   I'm not sure how the various pieces handle re-entrancy, but a repro will help clarify.

Which class implements IBindingList here?   Is it a CSLA class, a Microsoft class, or the app's class?  

RockfordLhotka replied on Monday, June 27, 2011

IBindingList is implemented by Microsoft's BindingList<T>, and my CSLA classes subclass BindingList - with the business class subclassing a CSLA base class.

Yes, the scenario is reentrant, and I'll put together a repro. As you might expect, this behavior has existed for many years in the WinForms world, but there's no guarantee the WPF datagrid matches the WinForms one. The SL datagrid does work as expected - but I'm guessing there's little code-sharing between WPF and SL for the datagrid because they are so different in other ways.

Then again, in SL the collection is a subclass of ObservableCollection, because there is no BindingList, so that could be an important difference too.

Sam Bent replied on Tuesday, June 28, 2011

The reentrancy (adding/removing from the collection in response to IEO.EndEdit) comes from CSLA classes, right?    BindingList<T> doesn't do that on its own.

How do you add a new item in SL?   Like WPF, the SL DataGrid doesn't have any AddNew functionality that lets the app construct the new item.   Yet you say that SL DataGrid works for you.   What's different vs. WPF?

Also, I'd like to amend a previous remark.   The WPF DataGrid team didn't "drop the ball" on the AddNew functionality.   They tried to do it, but it didn't happen for a variety of non-technical reasons (schedule constraints, priorities, etc. etc.).   From your point of view, that's no help - the necessary feature is missing, and who cares what the reason is.   But I didn't mean to dis my friends down the hall :).

Devman replied on Tuesday, July 12, 2011

Rocky, do you have any more thoughts on this issue. I realise this thread has been time consuming for you and apologise for that since its not directly a CSLA issue, however the bug on connect is soon to be closed off as "Cannot Repro".

Regards

David

RockfordLhotka replied on Tuesday, July 12, 2011

I have a repro I need to package up and send to Sam. The challenge is that I'm on vacation, and so have been trying to sneak bits of CSLA work in between normal vacation activities :)

Devman replied on Friday, August 05, 2011

The bug on connect has now been closed off.

Will have to log the issue from scratch with a repro if we want the IEdiatbleObject issue looked at again.

Copyright (c) Marimer LLC