1 /* Copyright (c) 2021 Samsung Electronics Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Collections.Specialized;
22 namespace Tizen.NUI.Components
24 // Wraps a List which implements INotifyCollectionChanged (usually an ObservableCollection)
25 // and marshals all of the list modifications to the main thread. Modifications to the underlying
26 // collection which are made off of the main thread remain invisible to consumers on the main thread
27 // until they have been processed by the main thread.
29 internal class MarshalingObservableCollection : List<object>, INotifyCollectionChanged
31 readonly IList internalCollection;
33 public MarshalingObservableCollection(IList list)
35 if (!(list is INotifyCollectionChanged incc))
37 throw new ArgumentException($"{nameof(list)} must implement {nameof(INotifyCollectionChanged)}");
40 internalCollection = list;
41 incc.CollectionChanged += InternalCollectionChanged;
43 foreach (var item in internalCollection)
49 class ResetNotifyCollectionChangedEventArgs : NotifyCollectionChangedEventArgs
51 public IList Items { get; }
52 public ResetNotifyCollectionChangedEventArgs(IList items)
53 : base(NotifyCollectionChangedAction.Reset) => Items = items;
56 public event NotifyCollectionChangedEventHandler CollectionChanged;
58 void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
60 CollectionChanged?.Invoke(this, args);
63 void InternalCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
65 if (args.Action == NotifyCollectionChangedAction.Reset)
67 var items = new List<object>();
68 for (int n = 0; n < internalCollection.Count; n++)
70 items.Add(internalCollection[n]);
73 args = new ResetNotifyCollectionChangedEventArgs(items);
76 if (Device.IsInvokeRequired)
78 Device.BeginInvokeOnMainThread(() => HandleCollectionChange(args));
82 HandleCollectionChange(args);
86 HandleCollectionChange(args);
89 void HandleCollectionChange(NotifyCollectionChangedEventArgs args)
93 case NotifyCollectionChangedAction.Add:
96 case NotifyCollectionChangedAction.Move:
99 case NotifyCollectionChangedAction.Remove:
102 case NotifyCollectionChangedAction.Replace:
105 case NotifyCollectionChangedAction.Reset:
111 void Move(NotifyCollectionChangedEventArgs args)
113 var count = args.OldItems.Count;
115 for (int n = 0; n < count; n++)
117 var toMove = this[args.OldStartingIndex];
118 RemoveAt(args.OldStartingIndex);
119 Insert(args.NewStartingIndex, toMove);
122 OnCollectionChanged(args);
125 void Remove(NotifyCollectionChangedEventArgs args)
127 var startIndex = args.OldStartingIndex + args.OldItems.Count - 1;
128 for (int n = startIndex; n >= args.OldStartingIndex; n--)
133 OnCollectionChanged(args);
136 void Replace(NotifyCollectionChangedEventArgs args)
138 var startIndex = args.NewStartingIndex;
139 foreach (var item in args.NewItems)
141 this[startIndex] = item;
145 OnCollectionChanged(args);
148 void Add(NotifyCollectionChangedEventArgs args)
150 var startIndex = args.NewStartingIndex;
151 foreach (var item in args.NewItems)
153 Insert(startIndex, item);
157 OnCollectionChanged(args);
160 void Reset(NotifyCollectionChangedEventArgs args)
162 if (!(args is ResetNotifyCollectionChangedEventArgs resetArgs))
164 throw new InvalidOperationException($"Cannot guarantee collection accuracy for Resets which do not use {nameof(ResetNotifyCollectionChangedEventArgs)}");
168 foreach (var item in resetArgs.Items)
173 OnCollectionChanged(args);