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)
24 // Ivan N. Zlatev (contact@i-nz.net)
29 using System.Reflection;
30 using System.Collections;
31 using System.ComponentModel;
33 namespace System.Windows.Forms {
34 public class CurrencyManager : BindingManagerBase {
36 protected int listposition;
37 protected Type finalType;
40 private bool binding_suspended;
42 private object data_source;
46 internal CurrencyManager ()
50 internal CurrencyManager (object data_source)
52 SetDataSource (data_source);
59 public override object Current {
61 if (listposition == -1 || listposition >= list.Count) {
62 // Console.WriteLine ("throwing exception from here");
63 // Console.WriteLine (Environment.StackTrace);
64 throw new IndexOutOfRangeException ("list position");
66 return list [listposition];
70 public override int Count {
71 get { return list.Count; }
74 public override int Position {
81 if (value >= list.Count)
82 value = list.Count - 1;
83 if (listposition == value)
86 if (listposition != -1)
90 OnCurrentChanged (EventArgs.Empty);
91 OnPositionChanged (EventArgs.Empty);
95 internal void SetDataSource (object data_source)
97 if (this.data_source is IBindingList)
98 ((IBindingList)this.data_source).ListChanged -= new ListChangedEventHandler (ListChangedHandler);
100 if (data_source is IListSource)
101 data_source = ((IListSource)data_source).GetList();
103 this.data_source = data_source;
104 if (data_source != null)
105 this.finalType = data_source.GetType();
108 if (this.data_source is IBindingList)
109 ((IBindingList)this.data_source).ListChanged += new ListChangedEventHandler (ListChangedHandler);
111 list = (IList)data_source;
113 // XXX this is wrong. MS invokes OnItemChanged directly, which seems to call PushData.
114 ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1));
117 public override PropertyDescriptorCollection GetItemProperties ()
119 return ListBindingHelper.GetListItemProperties (list);
122 public override void RemoveAt (int index)
124 list.RemoveAt (index);
127 public override void SuspendBinding ()
129 binding_suspended = true;
132 public override void ResumeBinding ()
134 binding_suspended = false;
137 internal override bool IsSuspended {
139 // Always return true if we don't have items
143 return binding_suspended;
147 internal bool AllowNew {
149 if (list is IBindingList)
150 return ((IBindingList)list).AllowNew;
159 internal bool AllowRemove {
164 if (list is IBindingList)
165 return ((IBindingList)list).AllowRemove;
171 internal bool AllowEdit {
173 if (list is IBindingList)
174 return ((IBindingList)list).AllowEdit;
180 public override void AddNew ()
182 IBindingList ibl = list as IBindingList;
185 throw new NotSupportedException ();
189 bool validate = (Position != (list.Count - 1));
190 ChangeRecordState (list.Count - 1, validate, validate, true, true);
196 IEditableObject editable = Current as IEditableObject;
198 if (editable != null) {
200 editable.BeginEdit ();
204 /* swallow exceptions in IEditableObject.BeginEdit () */
209 public override void CancelCurrentEdit ()
211 if (listposition == -1)
214 IEditableObject editable = Current as IEditableObject;
216 if (editable != null) {
218 editable.CancelEdit ();
219 OnItemChanged (new ItemChangedEventArgs (Position));
222 if (list is ICancelAddNew)
223 ((ICancelAddNew)list).CancelNew (listposition);
228 public override void EndCurrentEdit ()
230 if (listposition == -1)
233 IEditableObject editable = Current as IEditableObject;
235 if (editable != null) {
241 if (list is ICancelAddNew)
242 ((ICancelAddNew)list).EndNew (listposition);
246 public void Refresh ()
248 ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1));
251 protected void CheckEmpty ()
253 if (list == null || list.Count < 1)
254 throw new Exception ("List is empty.");
258 protected internal override void OnCurrentChanged (EventArgs e)
260 if (onCurrentChangedHandler != null) {
261 onCurrentChangedHandler (this, e);
265 // don't call OnCurrentItemChanged here, as it can be overridden
266 if (onCurrentItemChangedHandler != null) {
267 onCurrentItemChangedHandler (this, e);
274 protected override void OnCurrentItemChanged (EventArgs e)
276 if (onCurrentItemChangedHandler != null) {
277 onCurrentItemChangedHandler (this, e);
282 protected virtual void OnItemChanged (ItemChangedEventArgs e)
284 if (ItemChanged != null)
285 ItemChanged (this, e);
287 transfering_data = true;
289 transfering_data = false;
293 void OnListChanged (ListChangedEventArgs args)
295 if (ListChanged != null)
296 ListChanged (this, args);
300 protected virtual void OnPositionChanged (EventArgs e)
302 if (onPositionChangedHandler != null)
303 onPositionChangedHandler (this, e);
306 protected internal override string GetListName (ArrayList listAccessors)
308 if (list is ITypedList) {
309 PropertyDescriptor [] pds = null;
310 if (listAccessors != null) {
311 pds = new PropertyDescriptor [listAccessors.Count];
312 listAccessors.CopyTo (pds, 0);
314 return ((ITypedList) list).GetListName (pds);
316 else if (finalType != null) {
317 return finalType.Name;
322 protected override void UpdateIsBinding ()
326 foreach (Binding binding in Bindings)
327 binding.UpdateIsBinding ();
329 ChangeRecordState (listposition, false, false, true, false);
331 OnItemChanged (new ItemChangedEventArgs (-1));
334 private void ChangeRecordState (int newPosition,
337 bool firePositionChanged,
343 int old_index = listposition;
345 listposition = newPosition;
347 if (listposition >= list.Count)
348 listposition = list.Count - 1;
350 if (old_index != -1 && listposition != -1)
351 OnCurrentChanged (EventArgs.Empty);
353 if (firePositionChanged)
354 OnPositionChanged (EventArgs.Empty);
357 private void UpdateItem ()
359 // Probably should be validating or something here
360 if (!transfering_data && listposition == -1 && list.Count > 0) {
366 internal object this [int index] {
367 get { return list [index]; }
370 private PropertyDescriptorCollection GetBrowsableProperties (Type t)
372 Attribute [] att = new System.Attribute [1];
373 att [0] = new BrowsableAttribute (true);
374 return TypeDescriptor.GetProperties (t, att);
382 void OnMetaDataChanged (EventArgs e)
384 if (MetaDataChanged != null)
385 MetaDataChanged (this, e);
388 private void ListChangedHandler (object sender, ListChangedEventArgs e)
390 switch (e.ListChangedType) {
391 case ListChangedType.PropertyDescriptorAdded:
392 case ListChangedType.PropertyDescriptorDeleted:
393 case ListChangedType.PropertyDescriptorChanged:
394 OnMetaDataChanged (EventArgs.Empty);
399 case ListChangedType.ItemDeleted:
400 if (list.Count == 0) {
401 /* the last row was deleted */
406 OnPositionChanged (EventArgs.Empty);
407 OnCurrentChanged (EventArgs.Empty);
410 else if (e.NewIndex <= listposition) {
411 /* the deleted row was either the current one, or one earlier in the list.
412 Update the index and emit PositionChanged, CurrentChanged, and ItemChanged. */
413 ChangeRecordState (e.NewIndex,
414 false, false, e.NewIndex != listposition, false);
417 /* the deleted row was after the current one, so we don't
418 need to update bound controls for Position/Current changed */
421 OnItemChanged (new ItemChangedEventArgs (-1));
426 case ListChangedType.ItemAdded:
427 if (list.Count == 1) {
428 /* it's the first one we've added */
429 ChangeRecordState (e.NewIndex,
430 false, false, true, false);
435 OnItemChanged (new ItemChangedEventArgs (-1));
441 if (e.NewIndex <= listposition) {
442 ChangeRecordState (listposition + 1,
443 false, false, false, false);
444 OnItemChanged (new ItemChangedEventArgs (-1));
446 OnPositionChanged (EventArgs.Empty);
449 OnItemChanged (new ItemChangedEventArgs (-1));
453 OnItemChanged (new ItemChangedEventArgs (-1));
458 case ListChangedType.ItemChanged:
461 if (e.NewIndex == listposition)
462 OnCurrentItemChanged (EventArgs.Empty);
464 OnItemChanged (new ItemChangedEventArgs (e.NewIndex));
470 case ListChangedType.Reset:
486 public event ListChangedEventHandler ListChanged;
488 public event ItemChangedEventHandler ItemChanged;
489 public event EventHandler MetaDataChanged;