I have a DataGrid.
It's ItemsSource is bound to the ModelView's CompositeCollection through the ViewModel.
The CompositeCollection consists of 2 ObservableCollections.
The display on the grid is fine. I am able to see the collection.
However, when I try to edit one of the rows, I get a crash (NotSupportedException) of:
"'EditItem' is not allowed for this view"
How do I make the rows editable? I need to be able to edit the ModelViews representing each row in the Grid.
Here is the CompositeCollection Property code that I use to bind to the itemssource:
this isn't the exact code since I am not allowed to post the exact code but it is the same logic on how I make the collection
public CompositeCollection ModelViewsCollection
{
get
{
CollectionContainer modelViewContainer;
CompositeCollection modelViewCollection = new CompositeCollection();
modelViewContainer= new CollectionContainer();
modelViewContainer.Collection= this.ModelViewCollection;
modelViewCollection .Add(modelViewContainer);
modelViewContainer= new CollectionContainer();
modelViewContainer.Collection= this.ModelViewCollection2;
modelViewCollection .Add(modelViewContainer);
return modelViewCollection;
}
}
CompositeCollection does not implement IEditableCollectionView which is used by the datagrid to edit.
I have had the same issues, and ended up doing my own fake composite collection on the view model, similiar to what you have, if all you are putting in your collection is two observable collections, its not to hard to track the changes listening to collection changed on both of them. and make your viewmodels collection consist of both of them
You could even do the dirty hack that i did, of rebuilding the ObservableCollection that the grid binds to every time one of the collections change (not elegant i know, but ill go back and optimise when i get time.. i.e. never) With a linq query this stuff is really easy.
Otherwise maybe you could derive from CompositeCollection and try and add the IEditableCollectionView, if you get that working be sure to let me know.
here is the same question on the datagrid forum
Related
i've searched for several hours, and didn't find an answer for my problem.
i'm trying to place comboboxes in a datagrid filled with DYNAMIC data. (the number and the content of these comboboxes always change. i don't know in advance how many columns there are, where i need to use comboboxes. so every single combobox gets a unique dataprovider, which comes from an external source, WHEN the program runs.)
-i found MANY threads discussing this problem, but solving via crappy mxml files, filling the comboboxes inside the sourcecode by hand. i want to point out, that isn't good for me.
-i found a better solution, in which they used some sort of custom itemrenderer to get the data from the internet. (kind of a country chooser thing) but sadly that wasn't good enough, because the number and name of the countries in the world are static more or less, they don't change. so their renderer class didn't depend on any parameters from the main algorithm.
but in my program i calculate the data in my own actionscript objects, then fill an arraylist with that. so at the beginning i load the desired data from the net, and when i get the response of the urlrequest, AFTER that i start to populate the datagrid/combobox.
i can fill any datagrid or combobox without trouble, but to put that combobox inside a datagrid cell seems to be impossible.
could anyone please help? it drives me crazy. i managed to do this in several languages before, c#, java, even php+html, but in flex it looks way too complicated then it should be.
EDIT:
i'm aware, that this amount of network activity could mean some load on the server. i didn't design the philosophy behind it, i just need to wrote a client which meets the expectations. my program looks something like this:
(i'm willing to rewrite any part of it, just to make those nasty comboboxes work)
=========
main.mxml file
this is the main program, i handle some login related stuff here, and set basic design properties for the datagrids. (for example: maxwidth, maxheight, layout constraints etc.)
nothing interesting, except the command when i instantiate the actionscript class, which i wrote to fill the datagrid.
"..<fx:Script>
<![CDATA[
private var myGrid1:MyGridType;
..
somefunction {
myGrid1 = new MyGridType(theDatagridDefinedBefore, "argumentNeededToFillDataGridsWithUniqueData");
}
]]>
</fx:Script>.."
=========
MyGridType.as file
in the constructor i call a urlrequest with the help of the second argument, then add an eventlistener to it. when the data arrives, the eventlistener fires the filler function: i read the results into an arraycollection, then make it the dataprovider for the the datagrid in the first argument.
so far so good.
here comes the trouble with the comboboxes. for a specific number columns, i instantiate my combobox class. let's call that class "MyComboBoxType".
"..
blablabla = new MyComboBoxType(theDatagridDefinedBefore, param1, param2, param3);"
=========
MyComboBoxType.as file
i do nearly exactly the same, what i did in the MyGridType class. call for help from the net with param1-2-3. when i receive the data, fill an arraycollection. maybe set that arraycollection to be the dataprovider for a combobox. AAAAAAAND now i want that arraycollection or combobox to be on the datagrid "theDatagridDefinedBefore".
I know it's not exactly what you're trying to accomplish, but I had a somewhat similar issue in the past. Take a look at How to get the value of a ComboBox within a DataGrid to see if it helps.
If it were me, I would populate the entire ArrayCollection set before binding them to the datagrid if at all possible.
You should build up your custom [Bindable] data structure - say MyGridData class - for the rows in the grid (if you haven't done it yet);
the dataProvider of your grid should
be an Array / ArrayCollection /..
of MyGridData objects.
this step clearly works already, but
for the integrity: override the
getItemEditor function, or specify
it explicitly using mxml, to return
the combobox when needed.
as for the dataProvider of the
combobox, you should specify the
data.comboArray from inside the
renderer class, where data is the
MyGridData instance used by the row
you are processing. (overriding the
set data(value: Object):void
function, you can pre-process it.)
this way, you are working with the
reference of your original instances,
and by the binding you can detect /
show any changes to them directly.
Forgive me, I'm new to Flash Builder 4 and Actionscript 3 (actually, to programming as a whole beyond some very simplistic stuff). I have watched / read a bunch of tutorials, and started a project but now seem to have hit a wall. The answer is most likely simple, but seems to be alluding me.
How do I (or What approach should I take) to control visual elements, for instance, BorderContainer's, that I created dynamically?
As is, I have an Application containing a BorderContainer and a DataGrid. At runtime, 3 new BorderContainers (which are dragable, and resizeable) are created based on XML data that contains X & Y co-ordinates, and Height and Width values, and then added to the pre-existing BorderContainer. How would I go about getting the properties of these children BorderContainers to be displayed and remain up-to-date in the DataGrid (such as when they are moved/resized)?
My intentions in the future would be to have a custom component which displays a summary of these items in a separate area (think photoshop "layers" control, but much more simplistic), but wanted to get a better understanding of what's going on first.
Any input, documentation, examples, etc. is all appreciated. Again, I apologize for what may be an incredibly easy solution, or if any of my language is unclear, I'm new to this ^_^;
I would create an ArrayCollection of the BorderContainers with their various properties set (also make sure you call addElement on the parent BorderContainer). Make sure your ArrayCollection is declared as Bindable, then set it as the dataProvider for your DataGrid. Then specify the columns for your DataGrid based on whatever properties you want to display (height, width, etc). Now whenever the properties of the BorderContainers change, the DataGrid will automatically update.
Assuming a pure AS3 project, the best approach is to build a dictionary of your objects.
Let's also assume you've created identifiers for the components, or can easily create them at runtime.
var containers:Dictionary = new Dictionary();
private function _init():void
{
//some loop to create objects
containers[newObject.name] = newObject;
}
Later you can quickly access it by just grabbing the hashed index from the containers dictionary.
Now, assuming a Flex project, we have a few more approaches we can take:
DisplayObjectContainer implements getChildByName()
Group implements getElementAt, and numElements to iterate over, check names, and return value expected.
Personally, I still prefer the dictionary approach...
As for keeping things up to date, you can look into Binding (typically a Flex-only solution) or more appropriately investigate the events dispatched:
Event.RESIZE
Event.MOVE
etc.
In the handlers, just update your UI!
HTH, otherwise post more info and we'll see what we can figure out.
I'm using XtraGrid of DevExpress 2.9.5 to display a blotter of dynamic set of lines. The blotter is integrated into another application, this is why it has to be based on UserControl class and also implement a couple of custom interfaces.
public partial class BlotterForm : UserControl, ISMMdiEmbeddable, ISMAssociatedMFCWindow
{
private BindingList<BlotterTrade> fDeals;
....
}
As the data is binded to control using BindedList, any change should be reflected in the form automatically. And if I try to add new line to fDeals like follows:
public void AddDeal()
{
fDeals.Add(new BlotterTrade(1,2,3));
}
... i can see the line, but it's content is rubbish.
I tried to do the same in a small test application. It works ok with only difference that the blotter in test application is based on DevExpress.XtraEditors.XtraForm. To me it looks now that the form of original blotter doesn't overload some method or miss some event. But I cannot find out what exactly is missed.
Can somebody tell me what I do wrong or don't do?
Thanks.
A couple of things:
BindingList doesn't always work too well with DevExpress, and it's suggested to use XPCollection instead.
Do you have any more info about how you setup your columns in the xtragrid? If you use incorrect field names in the column, then they won't show what you're looking for.
If the params you're using (1, 2, 3) are ids stored as fkeys to other objects (not sure if you're using xpo or not) then they won't show up correctly either (there'll likely be a '+' in the cell instead of any values).
[aside] be sure that blottertrade implements INotifyPropertyChanged for better interaction with the grid.
Thanks to everybody for the answers and comments. I think I sorted out the problem. It was actually related to interaction between native C++ and C# layers in my application. The object that was supposed to be displayed in XtraGrid was created in C++ layer, the grid was displayed asynchronously with object construction/deconstruction, that's why at the moment when the grid was ready to display it, the object itself didn't exist. Hence the rubbish. It's good the grid itself was not crashing or firing exceptions.
I want to verify that a generated class (single entity or collection) from an O/RM tool is data binding compatible.
I read that supported data binding types in WCF are: one time, one way, two way, one way from source in WCF. But how about "old school" .NET 1.1 data binding ?
It looks kind of difficult to check in code what kind of data binding support there is. You also have difference in runtime and design time data binding support. When reading some webpages I read different kind of implementations: implement IList, IComponent, INotifyPropertyChanged, IBindingList.... pffffff I don't know exactly where to look for...
You can databind to virtually any class. Let's imagine you create a very simple class, with a few properties, say for instance, Person with a Name and Age. I am talking about a plain simple class with absolutely nothing fancy about it.
If you create an instance of Person, you can do several things with it, and I will assume you are working with Windows Forms, but this mostly applies to other frameworks:
- You can bind its properties to properties of controls.
- You can bind it to datagrids, lists, etc. In the former case you can set mappings of which properties bind to which columns. In the latter, which property is displayed in the list, which property is the value selected by the user.
- Even better, you can bind it to a bindingSource.
Binding a single instance to a grid or a list isn't that useful, so what usually is done is that you create a list of instances and bind those to the grid. Even more correct is to bind the list to a bindingsource and the grid to the bindingsource also.
You can see a good article here about how to do all this.
Now, about all the interfaces you mention, they all only add more value to the databinding experience. Let's talk about a few of them.
INotifyPropertyChanged. Person is not less "databindable" than any other object if it does not implement this interface. What instances of Person are not able to do, however, is notify the controls their properties are bound to that the latter have changed. Try this: Bind the Name property of a Person instance to the Text property of a TextBox. Create a button that when clicked changes the value of that instance's Name. You will see the TextBox does not update after clicking the button. If, on the other hand, you implement INotifyPropertyChanged and have the setter of the Name property raise the PropertyChangedEvent that is defined by the interface, after repeating the experience, you will see that the textbox is updated automatically.
IEnumerable. If instead of a single Person, you want to databind not to a set of people, you can create a list of people and databind to that list. Let's take for instance, List lst = new List(); How do the databinding controls like datagrid, bindingSource, etc., know you want to bind to a set of Person(s) and not to the properties of lst itself? It is because List implements IEnumerable. So, whenever you bind these controls to an instance of anything that implements IEnumerable, the controls know that you intend to bind not to the properties of the list, but to the instances the list refers to. How do they know the type of objects the list contains? To be more generic and support any type of IEnumerable implementation, they just check the type of the first element in the list and assume all others are equal.
IBindingList: Even if Person implements IPropertyChanged, if you group instances of Person into a List bind that list to a control and, by code, change the value of a property of one of the instances, you will see nothing happen in the screen. This happens because Person is notifying, not the binding source, but the list. But the list wasn't made for databinding, so it is not listening, nor propagating the event to the control. Lists that implement IBindingList, like BindingList, offer a better databinding support precisely by listening to the PropertyChangedEvent events of their contents and propagating them up to the databound control.
I am affraid I have given you no way of determining if an object is databoundable, because virtually all them are, but I hope I've given you a way of determining different levels of databinding support (INotifyPropertyChanged and IBindingList). I am assuming you know how to check for these via reflection.
Any instance of a class with properties is data bindable. (in fact instances of any class with fields or properties at all are data bindable)
Using reflection in .NET makes it very easy to discover/use data in an object. (at a small performance cost)
In order to answer this question you'd need to provide the specific usage scenarios you'll be encountering.
Rui gives some good explanation of different common data binding patterns, but each of them is for solving specific problems.
The right answer is always dependent on the context.
:)
I have a problem with Gridview sorting that is similar to others but I'm binding to a collection object as opposed to a data table.
The existing business rules and data access layers of an application follow the pattern of having an object and, if you need a collection of objects of that type, to have another class inheriting CollectionBase and implementing IBindingList.
For desktop applications, it was easy to databind a gridview to one of these objects and there weren't any problems with turning on column sorting. Everything was 'in state' in the desktop app's presentation layer.
Now that code is being moved to a new web application (ASP.NET 2.0, VB codebehind pages).
I've played around with what I had to do to only have certain columns of the collection show up in the gridview and the gridview looked pretty good. When I turned on 'allow sorting', that's when the problems showed up.
I'm getting the error about not having a .Sorting method, etc. In researching this, I found all sorts of solutions that were easily implemented with dataviews if my source was a data table. But it's not - it's a collection. I tried to "cheap shot" a datasource by converting the collection to an XML memory stream and them trying to .ReadXML back into a dataset but that didn't work [Root element is missing error was as far as I got in the dataset.ReadXml(ioTemp) where ioTemp was the System.IO.MemoryStream used in the xml serializer].
Because of the old desktop apps, I've never had to worry about sorting a collection since the gridview handled it once it was loaded. In fact, it's a 'standard' that the collection's .SortProperty, .SortDirection and .ApplySort all through NotSupportedExceptions (I inherited this code from programmers long gone).
Is there an easy way to convert the collection to a data table or a way to sort the collection without having to go back to the database each time? Object Data Sources won't work becuase of the intricate rules in how the objects are built - the wizards in VS2005 just can't handle what we need to do (grabbing data from several tables conditionally to make an object).
Thanks in advance.
Have you considered client side sorting instead?
I have used the jquery tablesorter plugin in the past with ASP Gridviews.
http://tablesorter.com/
I had a similar issue and i needed to implement IComparable on the objects. Basically to sort a collection of objects you need a way to distinguish their order. The IComparable interface has one method called Compare which allows the .Net framework to work out the order of the objects when you sort them. You need to implement this method yourself to get the sort method to work.
Google results
You don't mention the error message so i cant be sure if this is the case, can you post the error?
EDIT :
In regards to your comment; you can implement multi column sorting, it just requires more work. You can specify the fields to sort the collection by and then use this information within the CompareTo Method.
Have a look at this
Given that you apparently are populating the grid with a collection of your own objects, this sounds like a perfect job for Linq for Objects. With just a little elbow grease you can achieve what is effectively an SQL Select statement against your collection. Very cool stuff.
http://www.hookedonlinq.com/LINQtoObjects5MinuteOverview.ashx
Also, do you really just want to sort the data in the grid? If so, then'd definitely pursue using Linq against your objects. However, rarely does sorting the contents of the grid really answer the problem ("sorting the grid" usually translates into changing the access path of the data used to fill the grid.) Browser apps aren't like Windows apps and don't have a full-time connection to the underlying data source to make things happen quite as magically as the DataGridView in Windows makes things seem.
You can put link buttons with an On_Click event as the header's of each column.
When the event is triggered, figure out which header was clicked on (one method per header or a commandArgument value). Once that is know, do a .orderBy or .OrderByDescending by on the collection of objects, and put the result back in as datasource of the gridview and databind on that.
In the year since I originally asked this question, I managed to get a new 'standard' implemented so that collections of business objects were now generic lists.
So now a "Collection class" that is little more than a "Inherits List(Of MyBusinessObject)" with a Sort Method that looks like this (performance wasn't an issue):
Public Overloads Sub Sort(ByVal strPropertyName As String, ByVal strDirection As String)
Dim arSortedList As New ArrayList
For Each item As MyBusinessObject In Me
arSortedList.Add(item)
Next
arSortedList.Sort(New CaseInsensitiveComparer(Of MyBusinessObject)(strPropertyName, strDirection))
For intI As Integer = 0 To arSortedList.Count - 1
Item(intI) = arSortedList(intI)
Next
End Sub
This seemed to work perfectly with the methodology used by the GridView for firing events.