I am trying to bind the grid to a datasource defined as an interface. The interface has one property that is an ienumerable of another interface. When I set the first interface as the datasource for the grid, the designer will not let me set properties for the inner band, as it doesn't show me any of the columns for that band. I have attached a sample project to demonstrate this. Here are some screenshots to illustrate the problem:
1) This shows the grid in the designer with the inner band expanded
2) This shows the columns collection within the grid's custom property editor
And finally, here is the grid at runtime, populated with sample data. The inner band is displaying correctly at run time, but without design time access I can't control its appearance:
Hmm, the problem seems to be with the declaration of the child interface property within the parent interface. Neither of the following work:
Property
If I use the concrete class variant though, and declare it as a List(of ConcreteRate) then it works properly.
How can I get around this?
Hi,
peelports said:How can I get around this?
It seems as though you have already discovered how to get around it. :)
Using an Interface as a type for DataBinding is not a good idea. The BindingManager needs to determine the data structure for the grid. If there are actual rows of data, then it can use these rows to determine the structure, which is probably why it works at run-time.
But at design-time, when there are no rows, it will try to create one. Since it cannot create an instance of an interface, it won't be able to determine the structure.
I strongly recommend not binding to Interface types like IList<T> or IBindingList<T>. You will run into all sorts of problems with this. Use List<T> or BindingList<T> instead. Also note that BindingList<T> is far more robust for binding. List<T> will impose all sorts of limitations - for example, your will not be able to add new rows to the grid.
Problem with that is that it breaks the design pattern we are using. All our forms have an interface that defines their behaviour to the controller classes. The controllers know about the interfaces, but have no direct access to the forms assembly. Dependency injection ties them all together at run time. Each form's interface defines the summary types it needs to interact with as interfaces within itself. This means the form logic only has to interact with the display elements it needs to know about, and all business logic to driver the forms is wrapped up in the controller classes. The controller classes contain concrete implementations of the summary type interfaces which wrap up any interaction with the business model.
Now since the forms assembly has no reference to the controllers assembly, it's impossible for it to bind to the classes defined within it. Potentially we could use inheritance from concrete classes defined in the view interfaces assembly, instead of implementing interfaces, but that's far less natural, and not required for anything other than this issue with the ultragrid.
I had hoped that I could get around it by manually defining a schema, but the grid kept on discarding my definition, not sure why. I have got a workaround, whereby I declare a throwaway (and otherwise unused) copy of the interface converted to a class within the form code, and bind to that. At run time this isn't used. It's not ideal though, as changes in the interface now require changes in this class, and we're trying to avoid that sort of tight coupling, especially when it's hidden away like this.
Shame the grid can't create a type on the fly to implement the interface, and use that within the designer. It would allow a bit more flexibility
We don't have a lot of choice, unfortunately, and in all honesty this is the only issue we've encountered. No problems at all at run time. The workaround may be aesthetically unpleasant but it works. Perhaps if we had more multiband grids we'd need to look further, but most of our grids are single band.
None of this really has anything to do with the grid. The grid gets the data structure from the DotNet BindingManager, and the BindingManager simply doesn't work well with interfaces.
If it's getting the structure for the root band, then my guess is that this is because there are rows in the root band at design-time. Either that, or the root band works because there is a concrete implementation.
I understand what you are saying about using interfaces for your design pattern, but what you are doing simply won't work with DotNet DataBinding. In my experience, DataBinding to an interface instead of a concrete type will cause you all sorts of problems both at design-time and run-time. Just as using IList instead of IBindingList will impose limitations.
Hang on though, this is only a problem if there's more than one band - for the outer band it works fine with interfaces, so you must already be doing something to go from the interface to a class to use within the designer?