Problem:
I am having problems getting the WinGrid to update after I reorder the BindingList that is bound to the grid.
Background:
I have a WinGrid with a BindingSource as its data source.
ultraGrid1.DataSource = BindingSource
I am using the BindingSource so that I can properly notify the grid that values in the BindingList objects have changed using INotifyPropertyChanged.
The BindingSource has a DataSource of a BindingList<mySimpleObject>
BindingSource.DataSource = BindingList<mySimpleObject>
I have 15 rows in the grid that are number sequentially from 1 – 15:
I select the first row and drag/drop it down between the 8th and 9th rows:
I added code to loop through my BindingList<mySimpleObject> to write out the sequence numbers. Before the changes from the move, this is the ordering of the items in my BindingList:
****************** Before Move
Seq: 1
Seq: 2
Seq: 3
Seq: 4
Seq: 5
Seq: 6
Seq: 7
Seq: 8
Seq: 9
Seq: 10
Seq: 11
Seq: 12
Seq: 13
Seq: 14
Seq: 15
Now I need to reorder the items in the list. I first store the mySimpleObject that I am moving in a local variable. Then I remove the item from the BindingList<mySimpleObject>:
BindingList.Remove(objectBeingMoved);
Next, I insert the object into the correct location:
BindingList.Insert(locIndex, objectBeingMoved);
After this, I loop across the BindingList again, writing out the sequence numbers. I get this output, which is what I would expect:
****************** After Move
My problem, though, is this:
As you can see, even though the sequence of the mySimpleObject items in the BindingList are ordered as I would expect when looping through the list, the moved item is moved all the way to the bottom of the grid. I am not doing any row refreshes, although I did try grid.Rows.Refresh(RefreshRow.ReloadData, true), but to no avail.
At this point, the only way that I can get the grid to refresh properly is to completely dump and then rebind the grid, like this:
Grid.DataSource = null;
Grid.DataSource = BindingSource;
There are at least two problems with this. First, It is terribly inefficient, and second it causes all of the sorts, filters, and column sizing to be reset.
One more data point… I wrote a simple application where I just put a grid and a button on a form to mimic this. I wanted to distill this to its simplest elements. Here is the relevant code:
public Form1()
{
InitializeComponent();
dataItems = new BindingList<DataItem>();
dataSource = new BindingSource();
dataSource.DataSource = dataItems;
}
private void Form1_Load(object sender, EventArgs e)
for (int i = 1; i < 11; i++)
var dataItem = new DataItem(i);
dataItems.Add(dataItem);
ultraGrid1.DataSource = dataSource;
private void ultraButton1_Click(object sender, EventArgs e)
DataItem temp = dataItems[0];
dataItems.RemoveAt(0);
dataItems.Insert(5, temp);
This code worked as I would have expected and the grid did reflect the new ordering of the objects in the BindingList.
Questions:
At this point, I am perplexed as to why my BindingList order looks correct, but the moved item in the BindingList always shows up at the bottom of the grid. So, I have three questions:
1. Does anyone know what might be causing my grief with the BindingList and the grid not being in sync?
2. If I have to rebind the BindingList to the grid after every drag/drop row operation (worst-case scenario), is there some easy way to save off the filters, column widths, etc so that I can reapply them after rebinding the BindingList?
3. (Best case scenario) – Is there some hidden functionality in the WinGrid that I have overlooked where I just flip a bit and row moving comes for free, automagically updating my BindingList for me? If not, why not? This would seem to be common functionality.
Hi Keith,
kdhollow said:It would be nice to have built-in hot tracking (a graphic indicator to show where the row will be dropped.
The grid actually does have built-in hot-tracking. There are HotTracking appearance for rows and cells. The problem is that these are based on mouse messages like MouseMove - like all HotTracking. And the grid doesn't get these messages during a drag - they are replaced by drag messages.
A DrawFilter is a good idea, but this can be accomplished more easily using properties. For example, you could simply set the Appearance on the row, keep track of which row you last applied an appearance to, and then when you drag over a new row, reset the previous row's appearance and apply the appearance to the new row.
This can be a little tricky if you already had an appearance applied to the row, though, so I see your point. The ideal solution, I think, would be to allow you to set the current HotTracked row. So in addition to tracking the mouse messages, you could explicitly specify a row or cell in code to be HotTracked and the grid would treat it as such. There's no way to do this right now, but it's a good idea, and I think you should Submit a feature request to Infragistics.
kdhollow said:it would be nice to have some help moving rows across bands with a bound data source.
I don't see any way the grid could do this. The grid cannot deal with your data source directly, it has to go through the BindingManager and/or the IBIndingList interface and these do not have any support for moving a row to a new parent.
With a DataSet, moving a row requires changing the data in such a way that the row ends up under a new parent when the Relationship is evaluated, so there's no way the grid can do that. In fact, never mind the BindingManager or the IBindingList interface, even the DataSet doesn't have functionality for that. So there's no way the grid could evaluate the relationship and determine what would be required in order to move a row to a new parent.
Mike,
Thank you (once again) for your help. The main sticking point here was moving rows across bands. I was already changing the underlying data relationships (which put the rows into the correct ParentCollection) but I needed to take things one step further, and that was to find the moved row and call .Move to put it in the correct place in the rows collection.
One observation... It would be nice if there was a little more help on drag/drop in the grid. It would be nice to have built-in hot tracking (a graphic indicator to show where the row will be dropped. Accomplished this with a draw filter, but it would be cool if it were built it) and it would be nice to have some help moving rows across bands with a bound data source.
Thanks again,
Keith
The Move method can only move a row within the same collection to which it already belongs. You cannot use Move to move a row from one band to another.
Moving a band from one band to another will often involve changing the actual data of the fields in that row. For example, in a DataSet with a RelationShip, moving a row from one parent under another parent means changing the foreign key.
So for scenario 1 and 4, you could easily do something like this:
row.ParentCollection.Move(row, newIndex)
That way you are always moving the row within it's own parent collection instead of always using grid.Rows which is the root rows collection only.
To move a row from one band to another or from one parent to another, you will need to first change the position of the row in the data source, then find the row in the grid in it's new collection, then call the Move method to position it in the correct place within that new collection.
Another option you may want to consider is using the UltraWinTree, instead. The tree lacks certain features of the grid, such as filtering and summaries. But you can populate the tree manually without using a data source, and in such a case, you would not have to worry about changing the data in order to move nodes around. The tree also has methods for moving nodes which allow you to move a node into a new collection in one step.
Of course, I don't know the requirements of your application, so there may be other reasons why you need to stick with the grid.
Hi Mike, and thanks for the response.
I agree with you that the code sample I sent does work. It worked for me. However, in the context of my larger application, that same logic doesn't work and I think it is because I am assuming a one-to-one synchronized view between the BindingList data source for the grid and the view on the grid.
In my app, I have a grid with two bands and I have these drag/drop move scenarios:
1. Move row from Band[0] to Band[0]
2. Move row from Band[0] to Band[1]
3. Move row from Band[1] to Band [0]
4. Move row from Band[1] to Band [1]
After your reply, I have gone back and changed my code such that a drag/drop from position to position on the 0th band works every time. To do this, I am using Grid.Rows.Move(position, row). This works within the 0th band and makes scenario 1 work.
Now I am having problems with scenarios 2-4. Anytime that I deal with Band [1], I have problems, even if it is moving rows around within this band. My results are inconsistent, but mostly result in the moved row being pushed to the bottom of the grid. I have tried both moving the Rows with Grid.Rows.Move and changing the order of objects around in the child lists of my data objects. I have discovered that it looks like the Grid.Rows collection seems to only be a collection of rows from Band[0]. So, the thing that is working, moving items within Band [0] with Grid.Rows.Move, won't work for the child rows. This has led me to change the order of items in the underlying, bound collections with inconsistent results. It appears that I can move items from Band[0] to Band [1] with success by changing the order of the items in the BindingList of objects and their child object collections and then calling Grid.Rows.Refresh(RefreshRow.ReloadData, true);. However, moving from Band[1] to Band[0] or from Band[1] to Band[1] with this same methodology is inconsistent.
I have searched for other posts in the forum where others have encountered this issue and ran across a couple. I have also worked with a few more example programs to simplify this, but the samples are small and seem to work better than my larger program. I think it might be because of the filtering that I am applying.
What I need to know is what are the API rules for moving bound items from band-to-band. From my research and your help, I am seeing these rules emerge (although I am not 100% sure these are correct):
1. Move of row(s) within the top-most band should be done with grid.Rows.Move()
2. Move of row(s) from band-to-band must be accomplished by changing the underlying BindingList collections
a. grid.Rows.Refresh() must be called to reflect underlying BindingList collection changes
I am pretty sure that I am correct about rule 1, but 2 has me stumped because it works moving rows from band 0 to 1, but not the other way.
Any thoughts on what I am missing?
Thanks in advance,
Hi,
I tried this out and it works fine for me. The row shows up in the correct place.I have attached my sample here so you can try it out and see if you get different results.
Having said that, it's generally not a good idea to rely on the grid rows being in the same order as the data source rows. This is not always true. Sorting, Filtering, and grouping will all change the order of the rows in the grid without affecting the data source. There is no reason why the data should always be in synch with the order of the rows in the data source - that's not a requirement for data binding.