Thursday, September 29, 2011

Double-click on a cell in a DataGrid does not start editing the cell, while F2 works, and begins editing the cell!!!

Double-click on a cell in a DataGrid does not start editing the cell, while F2 works, and begins editing the cell!!!

I worked a lot to solve this problem.. In my case, the problem was that my implementation of IList was not working properly. Specifically, the index operator (this[]) , the IndexOf method and Contains method.. Fix them and then double-click will work again without problems.

Update: just more clarification. In my case, I tried to make a trick to deceive the DataGrid by passing different objects everytime the DataGrid calls the this[] operator on the collection (I return a new object having properties with equal values). But it turned out that DataGrid internally keeps a list of these objects and compare them by reference, ending up with unequal results.

Wednesday, September 28, 2011

A custom IEnumerable implementation does not work properly with DataGrid

I am developing a DataGrid that looks at a data store that does not have a public collection. I implemented an IEnumerable interface for this data-store BUT
the problem is: I am not able to edit cells; every time I try to edit a cell I get this exception saying, "'EditItem' is not allowed for this view.".

You may ask, why not to create a list for your data-store and pass it to the DataGrid. The answer is: Because my data-store might get very large.

Analysis: I did some digging and I found that DataGrid casts Items property (property of its grand base class: ItemsControl) it casts it to IEditableCollectionView. And here where it fails (we will talk about it later). But what is the type of Items Property ?
Items is a property of type ItemCollection (which is a CollectionView) that is created for the Enumerable class that you assigned to ItemsSource. The collection-view is created by calling CollectionViewSource.GetDefaultCollectionView on your Enumerable class.

The analysis' results: DataGrid fails to create an editable CollectionView for your Enumerable class. Why? because you did not implement the correct interface for your Enumerable class.

So, the question is what interfaces your collection (your Enumerable class) should implement, so that the DataGrid can create an editable collection-view for it?

OK , now , it started to make sense.. I dag deeper and deeper and I found this piece of comments inside ViewManager class in .Net framework that clarifies the problem: (ViewManager is the creator of the collection-view for your Enumerable class that eventually will be passed to and used by Items)
The comments say:
// Order of precendence in acquiring the View:
// 0) If collection is already a CollectionView, return it.
// 1) If the CollectionView for this collection has been cached, then
// return the cached instance.
// 2) If a CollectionView derived type has been passed in collectionViewType
// create an instance of that Type
// 3) If the collection is an ICollectionViewFactory use ICVF.CreateView()
// from the collection
// 4) If the collection is an IListSource call GetList() and perform 5),
// etc. on the returned list
// 5) If the collection is an IBindingList return a new BindingListCollectionView
// 6) If the collection is an IList return a new ListCollectionView
// 7) If the collection is an IEnumerable, return a new CollectionView
// (it uses the ListEnumerable wrapper)
// 8) return null
// An IListSource must share the view with its underlying list.

// if the view already exists, just return it
// Also, return null if it doesn't exist and we're called in "lazy" mode

So , all what we need to do now is to implement one of the following interfaces that would generate automatically one of the Collection Views that implements IEditableCollectionView. so that your grid will work correctly.

Finally, I implemented IList in my Enumerable class and it worked without problems :) great ..!!
Helpful Notes:
- The generic version of IList interface is not the one you should implement. You have to implement the non-gernic version of IList.
- You can try, implementing ICollectionViewFactory , but you have to know it is a little bit more complicated. Because you're going to end up implementing few more interfaces in the process.
- You can implement the IEditableObject interface for that items in your list to have more control over editing and validating each item in the list (each row in the DataGrid).

Problem solved.

Update: another complication I created .. read here about it

Friday, September 23, 2011

How to trace inside WPF source code !! FOUND IT..

How to trace inside WPF? Thank God I found the solution posted by Shawn Burke - MSFT
Thank you Shawn :)

And these links too:
http://referencesource.microsoft.com/serversetup.aspx
http://weblogs.asp.net/rajbk/archive/2010/04/21/setting-up-visual-studio-2010-to-step-into-microsoft-net-source-code.aspx

If non of the above worked (... just like what happened with me..), try this one. You can download .Net Framework source code :) .. isn't this neat. Here:
http://www.codeproject.com/Articles/93423/Step-Into-NET-Framework-4-0-Source-Code
Thanks Arik Poznanski :)

But no.. not even this worked .. I guess I still have DLLs that do not have source code of the same version.. But hey, look at the bright side.. I have the source code now.. And I can use the features and tricks provided by links the above. Especially Tracing into properties and filling the Call Stack frame with external assemblies' calls.. providing me with objects' names and methods' names. So I can look them up from the Source Code and figure out the problem that I am having.

Tabbing is misbehaving inside DataGrid with custom controls (GenerateElement)

Context: I created a DataGridBoundColumn class that creates my own control.
Problem: tabbing between cells (that were created using the GenerateElement), causes tabbing to go through DataGridCell (the container of your generated element) then your element. So it will require the user to press TAB twice to go to the next cell.
Solution: The elements that you generate using GenerateElement (not edit mode cells), need to update some of its properties: Focusable = false, IsHitTestVisible = false, and finally set its IsTabStop to false (use this line: noeditingElement.SetValue(KeyboardNavigation.IsTabStopProperty, false);)

Conclusion: Elements created using GenerateElement should be NOT focusable , NOT hit visible, or tab stop disabled.

Wednesday, September 21, 2011

Focus is lost when GenerateEditingElement is overridden

When deriving from DataGridBoundColumn to override control creation for your DataGrid, you are going to face a problem.. Where does my focus go when I double click the cell?

The cell control is created and activated but the focus just goes to the parent DataGridCell leaving the control that I just created in GenerateEditingElement unfocused. So I will have to click a third click to get the focus where I wanted it in the first place.

The solution is to override DataGridBoundColumn.PrepareCellForEdit and set the focus there. Voila.