1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2005 Novell, Inc.
23 // Jackson Harper (jackson@ximian.com)
28 using System
.Reflection
;
29 using System
.Collections
;
30 using System
.ComponentModel
;
32 namespace System
.Windows
.Forms
{
33 public class CurrencyManager
: BindingManagerBase
{
35 protected int listposition
;
36 protected Type finalType
;
39 private bool binding_suspended
;
41 private object data_source
;
45 internal CurrencyManager ()
49 internal CurrencyManager (object data_source
)
51 SetDataSource (data_source
);
58 public override object Current
{
60 if (listposition
== -1 || listposition
>= list
.Count
) {
61 // Console.WriteLine ("throwing exception from here");
62 // Console.WriteLine (Environment.StackTrace);
63 throw new IndexOutOfRangeException ("list position");
65 return list
[listposition
];
69 public override int Count
{
70 get { return list.Count; }
73 public override int Position
{
80 if (value == list
.Count
)
81 value = list
.Count
- 1;
82 if (listposition
== value)
85 if (listposition
!= -1)
89 OnCurrentChanged (EventArgs
.Empty
);
90 OnPositionChanged (EventArgs
.Empty
);
94 internal void SetDataSource (object data_source
)
96 if (this.data_source
is IBindingList
)
97 ((IBindingList
)this.data_source
).ListChanged
-= new ListChangedEventHandler (ListChangedHandler
);
99 if (data_source
is IListSource
)
100 data_source
= ((IListSource
)data_source
).GetList();
102 this.data_source
= data_source
;
103 if (data_source
!= null)
104 this.finalType
= data_source
.GetType();
107 if (this.data_source
is IBindingList
)
108 ((IBindingList
)this.data_source
).ListChanged
+= new ListChangedEventHandler (ListChangedHandler
);
110 list
= (IList
)data_source
;
112 // XXX this is wrong. MS invokes OnItemChanged directly, which seems to call PushData.
113 ListChangedHandler (null, new ListChangedEventArgs (ListChangedType
.Reset
, -1));
116 public override PropertyDescriptorCollection
GetItemProperties ()
119 Type element
= list
.GetType ().GetElementType ();
120 return TypeDescriptor
.GetProperties (element
);
123 if (list
is ITypedList
) {
124 return ((ITypedList
)list
).GetItemProperties (null);
127 PropertyInfo
[] props
= data_source
.GetType().GetProperties ();
128 for (int i
= 0; i
< props
.Length
; i
++) {
129 if (props
[i
].Name
== "Item") {
130 Type t
= props
[i
].PropertyType
;
131 if (t
== typeof (object))
133 return GetBrowsableProperties (t
);
137 if (list
.Count
> 0) {
138 return GetBrowsableProperties (list
[0].GetType ());
141 return new PropertyDescriptorCollection (null);
144 public override void RemoveAt (int index
)
146 list
.RemoveAt (index
);
149 public override void SuspendBinding ()
151 binding_suspended
= true;
154 public override void ResumeBinding ()
156 binding_suspended
= false;
159 internal override bool IsSuspended
{
161 // Always return true if we don't have items
165 return binding_suspended
;
169 // XXX this needs re-addressing once DataViewManager.AllowNew is implemented
170 internal bool CanAddRows
{
172 /* if we're readonly, don't even bother checking if we can add new rows */
176 if (list
is IBindingList
) {
178 //return ((IBindingList)list).AllowNew;
185 public override void AddNew ()
187 IBindingList ibl
= list
as IBindingList
;
190 throw new NotSupportedException ();
194 ChangeRecordState (list
.Count
- 1, true, true, true, true);
200 IEditableObject editable
= Current
as IEditableObject
;
202 if (editable
!= null) {
204 editable
.BeginEdit ();
208 /* swallow exceptions in IEditableObject.BeginEdit () */
213 public override void CancelCurrentEdit ()
215 if (listposition
== -1)
218 IEditableObject editable
= Current
as IEditableObject
;
220 if (editable
!= null) {
222 editable
.CancelEdit ();
223 OnItemChanged (new ItemChangedEventArgs (Position
));
226 if (list
is ICancelAddNew
)
227 ((ICancelAddNew
)list
).CancelNew (listposition
);
232 public override void EndCurrentEdit ()
234 if (listposition
== -1)
237 IEditableObject editable
= Current
as IEditableObject
;
239 if (editable
!= null) {
245 if (list
is ICancelAddNew
)
246 ((ICancelAddNew
)list
).EndNew (listposition
);
250 public void Refresh ()
252 ListChangedHandler (null, new ListChangedEventArgs (ListChangedType
.Reset
, -1));
255 protected void CheckEmpty ()
257 if (list
== null || list
.Count
< 1)
258 throw new Exception ("List is empty.");
262 protected internal override void OnCurrentChanged (EventArgs e
)
264 if (onCurrentChangedHandler
!= null) {
265 onCurrentChangedHandler (this, e
);
269 // don't call OnCurrentItemChanged here, as it can be overridden
270 if (onCurrentItemChangedHandler
!= null) {
271 onCurrentItemChangedHandler (this, e
);
278 protected override void OnCurrentItemChanged (EventArgs e
)
280 if (onCurrentItemChangedHandler
!= null) {
281 onCurrentItemChangedHandler (this, e
);
286 protected virtual void OnItemChanged (ItemChangedEventArgs e
)
288 if (ItemChanged
!= null)
289 ItemChanged (this, e
);
291 transfering_data
= true;
293 transfering_data
= false;
297 void OnListChanged (ListChangedEventArgs args
)
299 if (ListChanged
!= null)
300 ListChanged (this, args
);
304 protected virtual void OnPositionChanged (EventArgs e
)
306 if (onPositionChangedHandler
!= null)
307 onPositionChangedHandler (this, e
);
310 protected internal override string GetListName (ArrayList listAccessors
)
312 if (list
is ITypedList
) {
313 PropertyDescriptor
[] pds
= null;
314 if (listAccessors
!= null) {
315 pds
= new PropertyDescriptor
[listAccessors
.Count
];
316 listAccessors
.CopyTo (pds
, 0);
318 return ((ITypedList
) list
).GetListName (pds
);
320 else if (finalType
!= null) {
321 return finalType
.Name
;
326 protected override void UpdateIsBinding ()
330 foreach (Binding binding
in Bindings
)
331 binding
.UpdateIsBinding ();
333 ChangeRecordState (listposition
, false, false, true, false);
335 OnItemChanged (new ItemChangedEventArgs (-1));
338 private void ChangeRecordState (int newPosition
,
341 bool firePositionChanged
,
347 int old_index
= listposition
;
349 listposition
= newPosition
;
351 if (listposition
>= list
.Count
)
352 listposition
= list
.Count
- 1;
354 if (old_index
!= -1 && listposition
!= -1)
355 OnCurrentChanged (EventArgs
.Empty
);
357 if (firePositionChanged
)
358 OnPositionChanged (EventArgs
.Empty
);
361 private void UpdateItem ()
363 // Probably should be validating or something here
364 if (listposition
!= -1) {
366 else if (list
.Count
> 0) {
374 internal object this [int index
] {
375 get { return list [index]; }
378 private PropertyDescriptorCollection
GetBrowsableProperties (Type t
)
380 Attribute
[] att
= new System
.Attribute
[1];
381 att
[0] = new BrowsableAttribute (true);
382 return TypeDescriptor
.GetProperties (t
, att
);
390 void OnMetaDataChanged (EventArgs e
)
392 if (MetaDataChanged
!= null)
393 MetaDataChanged (this, e
);
396 private void ListChangedHandler (object sender
, ListChangedEventArgs e
)
398 switch (e
.ListChangedType
) {
399 case ListChangedType
.PropertyDescriptorAdded
:
400 OnMetaDataChanged (EventArgs
.Empty
);
402 case ListChangedType
.PropertyDescriptorDeleted
:
403 case ListChangedType
.PropertyDescriptorChanged
:
404 OnMetaDataChanged (EventArgs
.Empty
);
406 case ListChangedType
.ItemDeleted
:
407 if (list
.Count
== 0) {
408 /* the last row was deleted */
413 OnPositionChanged (EventArgs
.Empty
);
414 OnCurrentChanged (EventArgs
.Empty
);
417 else if (e
.NewIndex
<= listposition
) {
418 /* the deleted row was either the current one, or one earlier in the list.
419 Update the index and emit PositionChanged, CurrentChanged, and ItemChanged. */
420 ChangeRecordState (e
.NewIndex
,
421 false, false, e
.NewIndex
!= listposition
, false);
424 /* the deleted row was after the current one, so we don't
425 need to update bound controls for Position/Current changed */
428 OnItemChanged (new ItemChangedEventArgs (-1));
430 case ListChangedType
.ItemAdded
:
431 if (list
.Count
== 1) {
432 /* it's the first one we've added */
433 ChangeRecordState (e
.NewIndex
,
434 false, false, true, false);
439 OnItemChanged (new ItemChangedEventArgs (-1));
444 if (e
.NewIndex
<= listposition
) {
445 ChangeRecordState (listposition
+ 1,
446 false, false, false, false);
447 OnItemChanged (new ItemChangedEventArgs (-1));
449 OnPositionChanged (EventArgs
.Empty
);
452 OnItemChanged (new ItemChangedEventArgs (-1));
456 OnItemChanged (new ItemChangedEventArgs (-1));
461 case ListChangedType
.ItemChanged
:
464 if (e
.NewIndex
== listposition
)
465 OnCurrentItemChanged (EventArgs
.Empty
);
467 OnItemChanged (new ItemChangedEventArgs (e
.NewIndex
));
472 OnItemChanged (new ItemChangedEventArgs (-1));
473 // UpdateIsBinding ();
482 public event ListChangedEventHandler ListChanged
;
484 public event ItemChangedEventHandler ItemChanged
;
485 public event EventHandler MetaDataChanged
;