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
{
160 get { return binding_suspended; }
163 // XXX this needs re-addressing once DataViewManager.AllowNew is implemented
164 internal bool CanAddRows
{
166 /* if we're readonly, don't even bother checking if we can add new rows */
170 if (list
is IBindingList
) {
172 //return ((IBindingList)list).AllowNew;
179 public override void AddNew ()
181 IBindingList ibl
= list
as IBindingList
;
184 throw new NotSupportedException ();
188 ChangeRecordState (list
.Count
- 1, true, true, true, true);
194 IEditableObject editable
= Current
as IEditableObject
;
196 if (editable
!= null) {
198 editable
.BeginEdit ();
202 /* swallow exceptions in IEditableObject.BeginEdit () */
207 public override void CancelCurrentEdit ()
209 if (listposition
== -1)
212 IEditableObject editable
= Current
as IEditableObject
;
214 if (editable
!= null) {
216 editable
.CancelEdit ();
217 OnItemChanged (new ItemChangedEventArgs (Position
));
221 public override void EndCurrentEdit ()
223 if (listposition
== -1)
226 IEditableObject editable
= Current
as IEditableObject
;
228 if (editable
!= null) {
234 public void Refresh ()
236 ListChangedHandler (null, new ListChangedEventArgs (ListChangedType
.Reset
, -1));
239 protected void CheckEmpty ()
241 if (list
== null || list
.Count
< 1)
242 throw new Exception ("List is empty.");
246 protected internal override void OnCurrentChanged (EventArgs e
)
248 if (onCurrentChangedHandler
!= null) {
249 onCurrentChangedHandler (this, e
);
253 // don't call OnCurrentItemChanged here, as it can be overridden
254 if (onCurrentItemChangedHandler
!= null) {
255 onCurrentItemChangedHandler (this, e
);
262 protected override void OnCurrentItemChanged (EventArgs e
)
264 if (onCurrentItemChangedHandler
!= null) {
265 onCurrentItemChangedHandler (this, e
);
270 protected virtual void OnItemChanged (ItemChangedEventArgs e
)
272 if (ItemChanged
!= null)
273 ItemChanged (this, e
);
276 // XXX see the commend in BindingManagerbase.PushData
277 if (listposition
!= -1)
282 protected virtual void OnPositionChanged (EventArgs e
)
284 if (onPositionChangedHandler
!= null)
285 onPositionChangedHandler (this, e
);
288 protected internal override string GetListName (ArrayList accessors
)
290 if (list
is ITypedList
) {
291 PropertyDescriptor
[] pds
= null;
292 if (accessors
!= null) {
293 pds
= new PropertyDescriptor
[accessors
.Count
];
294 accessors
.CopyTo (pds
, 0);
296 return ((ITypedList
) list
).GetListName (pds
);
298 else if (finalType
!= null) {
299 return finalType
.Name
;
304 protected override void UpdateIsBinding ()
308 foreach (Binding binding
in Bindings
)
309 binding
.UpdateIsBinding ();
311 ChangeRecordState (listposition
, false, false, true, false);
313 OnItemChanged (new ItemChangedEventArgs (-1));
316 private void ChangeRecordState (int newPosition
,
319 bool firePositionChanged
,
325 int old_index
= listposition
;
327 listposition
= newPosition
;
329 if (listposition
>= list
.Count
)
330 listposition
= list
.Count
- 1;
332 if (old_index
!= -1 && listposition
!= -1)
333 OnCurrentChanged (EventArgs
.Empty
);
335 if (firePositionChanged
)
336 OnPositionChanged (EventArgs
.Empty
);
339 private void UpdateItem ()
341 // Probably should be validating or something here
342 if (listposition
!= -1) {
344 else if (list
.Count
> 0) {
352 internal object this [int index
] {
353 get { return list [index]; }
356 private PropertyDescriptorCollection
GetBrowsableProperties (Type t
)
358 Attribute
[] att
= new System
.Attribute
[1];
359 att
[0] = new BrowsableAttribute (true);
360 return TypeDescriptor
.GetProperties (t
, att
);
368 void OnMetaDataChanged (EventArgs args
)
370 if (MetaDataChanged
!= null)
371 MetaDataChanged (this, args
);
374 private void ListChangedHandler (object sender
, ListChangedEventArgs e
)
376 switch (e
.ListChangedType
) {
377 case ListChangedType
.PropertyDescriptorAdded
:
378 OnMetaDataChanged (EventArgs
.Empty
);
381 OnMetaDataChanged (EventArgs
.Empty
);
384 case ListChangedType
.PropertyDescriptorDeleted
:
385 case ListChangedType
.PropertyDescriptorChanged
:
386 OnMetaDataChanged (EventArgs
.Empty
);
388 case ListChangedType
.ItemDeleted
:
389 if (list
.Count
== 0) {
390 /* the last row was deleted */
395 OnPositionChanged (EventArgs
.Empty
);
396 OnCurrentChanged (EventArgs
.Empty
);
399 else if (e
.NewIndex
<= listposition
) {
400 /* the deleted row was either the current one, or one earlier in the list.
401 Update the index and emit PositionChanged, CurrentChanged, and ItemChanged. */
402 ChangeRecordState (e
.NewIndex
,
403 false, false, e
.NewIndex
!= listposition
, false);
406 /* the deleted row was after the current one, so we don't
407 need to update bound controls for Position/Current changed */
410 OnItemChanged (new ItemChangedEventArgs (-1));
412 case ListChangedType
.ItemAdded
:
413 if (list
.Count
== 1) {
414 /* it's the first one we've added */
415 ChangeRecordState (e
.NewIndex
,
416 false, false, true, false);
421 OnItemChanged (new ItemChangedEventArgs (-1));
426 if (e
.NewIndex
<= listposition
) {
427 ChangeRecordState (listposition
+ 1,
428 false, false, false, false);
429 OnItemChanged (new ItemChangedEventArgs (-1));
430 OnPositionChanged (EventArgs
.Empty
);
433 OnItemChanged (new ItemChangedEventArgs (-1));
436 OnItemChanged (new ItemChangedEventArgs (-1));
441 case ListChangedType
.ItemChanged
:
444 if (e
.NewIndex
== listposition
)
445 OnCurrentItemChanged (EventArgs
.Empty
);
447 OnItemChanged (new ItemChangedEventArgs (e
.NewIndex
));
452 OnItemChanged (new ItemChangedEventArgs (-1));
453 // UpdateIsBinding ();
459 public event ListChangedEventHandler ListChanged
;
461 public event ItemChangedEventHandler ItemChanged
;
462 public event EventHandler MetaDataChanged
;