Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Windows Forms / WinForm Data Binding / November 2005

Tip: Looking for answers? Try searching our database.

Exceptions in DataGridView when working with IBindingList

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Joanna Carter [TeamB] - 16 Nov 2005 10:54 GMT
I have created a class BoundList<T> class that implements IBindingList that
wraps a List<T>.

I create a List<T> and add four objects to it

I set a BindingSource's DataSource property to an instance of this
BoundList<T> class.

I then set the DataGridView DataSource to the BindingSource.

If I run the app and try to add a new object by going to the row at the
bottom of the grid with an asterisk.

If I then move straight back to the previous row, move to the "new" row
again and then try to move back to the previous row again, I get an
IndexOutOfRangeException.

If I put a breakpoint in the AddNew() method of the BoundList class, I find
that each time I move to the "new" row in the grid, another object is added
to the list (as expected, because I call list.Add(...)) but moving back
without editing the new object doesn't seem to remove that item from the
list.

Is this a known problem, and is there a workaround ?

Joanna

Signature

Joanna Carter [TeamB]
Consultant Software Engineer

Bart Mermuys - 16 Nov 2005 12:52 GMT
Hi,

>I have created a class BoundList<T> class that implements IBindingList that
> wraps a List<T>.
[quoted text clipped - 22 lines]
>
> Is this a known problem, and is there a workaround ?

Well, you are right that when you click on the NewRow that AddNew is called
and i assume you create an object inside AddNew, then add it to the
collection and then fire ListChanged *once*, so far so good.

But if your objects that you put in this custom collection don't implement
IEditableObject then there is no way for the Grid to cancel or accept the
newly added object and it crashes ....

What you should do:
Implement IEditableObject on your custom objects:  When CancelEdit is called
you remove the newly added object and fire
ListChanged(ListChangedType.ItemRemoved), when EndEdit is called you fire
ListChanged(ListChangedType.ItemAdded) a *second* time.
-or-
If you don't want to implement IEditableObject on your objects, then you
can't cancel a newly inserted record and you should fire ListChanged(
ListChangedType.ItemAdded ) *twice*  inside AddNew as per SDK documentation.

So it is like
 ListChanged(ListChangedType.Added)   // new row added
 ListChanged(ListChangedType.Added)   // new row accepted
 ListChanged(ListChangedType.Remove) // new row canceled

Note, that in NET2.0 instead of implementing IEditableObject on the objects,
you can implement ICancelAddNew on the custom list, but i have no experience
with it yet.

HTH,
Greetings

> Joanna
Joanna Carter [TeamB] - 16 Nov 2005 14:47 GMT
Hi Bart

| But if your objects that you put in this custom collection don't implement
| IEditableObject then there is no way for the Grid to cancel or accept the
| newly added object and it crashes ....

I have already implemented IEditableObject, but didn't want my objects to
have to know about the list that there were in, so ICancelAddNew seems the
best way to go; many thanks for pointing that one out :-)

I am only left with one problem :-(

If I edit an exiting object in the list, then press the Esc key to cancel
the edit, the ICancelAddNew.CancelNew() method gets called, even though I am
not cancelling the adding of a new object !!

So now all I have to do is to figure out a way to detect that the CancelNew
event is being fired by a cancellation of a *new* object instead of an
existing one <aaaarrrrgghhh>

Joanna

Signature

Joanna Carter [TeamB]
Consultant Software Engineer

Bart Mermuys - 16 Nov 2005 15:14 GMT
Hi,

> Hi Bart
>
[quoted text clipped - 19 lines]
> event is being fired by a cancellation of a *new* object instead of an
> existing one <aaaarrrrgghhh>

I second that arrghh.  I like the idea of ICancelAddNew on the list, but
then this happens.

Like stated before i never used ICancelAddNew. But it do remember reading
something about it inside the SDK documentation for ICancelAddNew (have a
look if you haven't yet).

What if you store the index of the lastly added object (lastNewIndex).  Then
when one of the ICancelAddNew members is called with an index you compare it
with lastNewIndex.  If it isn't the lastly added object then ignore the
calls.  If it is the index of the lastly added object then do what you have
to do (cancel/accept) and then set your lastNewIndex to -1.  Just an idea
...

HTH,
Greetings

> Joanna
Joanna Carter [TeamB] - 16 Nov 2005 15:33 GMT
| I second that arrghh.  I like the idea of ICancelAddNew on the list, but
| then this happens.

Ahaaah, but I got it to work whilst you were thinking about it !! :-))

| What if you store the index of the lastly added object (lastNewIndex).  Then
| when one of the ICancelAddNew members is called with an index you compare it
| with lastNewIndex.  If it isn't the lastly added object then ignore the
| calls.  If it is the index of the lastly added object then do what you have
| to do (cancel/accept) and then set your lastNewIndex to -1.  Just an idea

Well to start with, I set up a pivate field that would hold a reference to
the latest object and compared the last item in the list against that in the
CancelNew, but your solution ends up being much more efficient as it only
compares integers and not pointers :-))

So I ended up with the following :

{
 public class ... : IBindingList, ICancelAddNew
 {
   ...

   private int lastNewIndex = -1;

   object IBindingList.AddNew()
   {
     T result = new T();
     lastNewIndex = list.Count;
     list.Add(result);
     OnListChanged(ListChangedType.ItemAdded, lastNewIndex);
     return result;
   }

  void ICancelAddNew.CancelNew(int itemIndex)
   {
     if (itemIndex == lastNewIndex)
     {
       list.RemoveAt(itemIndex);
       OnListChanged(ListChangedType.ItemDeleted, itemIndex);
       lastNewIndex = -1;
     }
   }

   void ICancelAddNew.EndNew(int itemIndex)
   {
     OnListChanged(ListChangedType.ItemChanged, itemIndex);
   }

   ...
 }
}

Works a treat, many thanks for the boost.

Joanna

Signature

Joanna Carter [TeamB]
Consultant Software Engineer

Joanna Carter [TeamB] - 16 Nov 2005 15:40 GMT
|    void ICancelAddNew.EndNew(int itemIndex)
|    {
|      OnListChanged(ListChangedType.ItemChanged, itemIndex);
|    }

Now modified to avoid multiple calls ...

   void ICancelAddNew.EndNew(int itemIndex)
   {
     if (itemIndex == lastNewIndex)
     {
       lastNewIndex = -1;
       OnListChanged(ListChangedType.ItemChanged, itemIndex);
     }
   }

Thanks again

Joanna

Signature

Joanna Carter [TeamB]
Consultant Software Engineer

Bart Mermuys - 16 Nov 2005 16:18 GMT
Hi,

> |    void ICancelAddNew.EndNew(int itemIndex)
> |    {
[quoted text clipped - 9 lines]
>        lastNewIndex = -1;
>        OnListChanged(ListChangedType.ItemChanged, itemIndex);

Doesn't this need to be OnListChanged(ListChangedType.ItemAdded, itemIndex).
AFAIK you need to call ItemAdded a second time if the new item is accepted,
not ItemChanged.

>      }
>    }
>
> Thanks again

You're welcome.

Greetings

> Joanna
Joanna Carter [TeamB] - 16 Nov 2005 16:37 GMT
| Doesn't this need to be OnListChanged(ListChangedType.ItemAdded, itemIndex).
| AFAIK you need to call ItemAdded a second time if the new item is accepted,
| not ItemChanged.

No, it would appear that EndNew(...) is called after you have finished
editing the item that you have already added and notified in AddItem.
Therefore all you need to do is to ensure that the current row is
up-to-date.

Anyway, it really works very nicely now :-))

Joanna

Signature

Joanna Carter [TeamB]
Consultant Software Engineer


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.