2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Collections.ObjectModel;
5 using System.Collections.Specialized;
9 namespace Tizen.Xamarin.Forms.Extension
12 /// The ContextPopup class allows a contextual popup to be anchored at a View.
16 /// ContextPopup popup = new ContextPopup();
17 /// popup.Items.Add(new ContextPopupItem("Text only item"));
18 /// popup.Items.Add(new ContextPopupItem("Home icon", "home"));
19 /// popup.SelectedIndexChanged += (s, e) =>
21 /// var ctxPopup = s as ContextPopup;
22 /// Debug.WriteLine("Item with index {0} selected", ctxPopup.SelectedIndex);
23 /// Debug.WriteLine("It has label: " + (ctxPopup.SelectedItem as ContextPopupItem).Label);
26 /// Button btn = new Button
28 /// Text = "Toggle popup"
30 /// btn.Clicked += (s, e) =>
32 /// popup.Show(s as Button);
33 /// popupShowing = true;
37 public class ContextPopup : BindableObject
39 IContextPopup _contextPopup;
40 ObservableCollection<ContextPopupItem> _items;
42 static ContextPopupDirectionPriorities _priorities =
43 new ContextPopupDirectionPriorities(ContextPopupDirection.Up, ContextPopupDirection.Left, ContextPopupDirection.Right, ContextPopupDirection.Down);
45 public static readonly BindableProperty OrientationProperty = BindableProperty.Create(nameof(Orientation), typeof(ContextPopupOrientation), typeof(ContextPopup),
46 defaultValue: ContextPopupOrientation.Vertical);
48 public static readonly BindableProperty IsAutoHidingEnabledProperty = BindableProperty.Create(nameof(IsAutoHidingEnabled), typeof(bool), typeof(ContextPopup), defaultValue: true);
50 public static readonly BindableProperty DirectionPrioritiesProperty = BindableProperty.Create(nameof(DirectionPriorities), typeof(ContextPopupDirectionPriorities),
51 typeof(ContextPopup), defaultValue: _priorities);
53 public static readonly BindableProperty SelectedIndexProperty = BindableProperty.Create(nameof(SelectedIndex), typeof(int), typeof(ContextPopup), defaultValue: -1,
54 propertyChanged: OnSelectedIndexChanged);
56 public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create(nameof(SelectedItem), typeof(object), typeof(ContextPopup), null,
57 propertyChanged: OnSelectedItemChanged);
59 public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(ContextPopup), default(IList),
60 propertyChanged: OnItemsSourceChanged);
64 _contextPopup = DependencyService.Get<IContextPopup>(DependencyFetchTarget.NewInstance);
65 _contextPopup.Dismissed += (s, e) => Dismissed?.Invoke(this, EventArgs.Empty);
66 _contextPopup.SelectedIndexChanged += (s, e) => SelectedIndexChanged?.Invoke(this, EventArgs.Empty);
68 _items = new ObservableCollection<ContextPopupItem>();
69 _items.CollectionChanged += ItemsCollectionChanged;
71 SetBinding(OrientationProperty, new Binding(nameof(Orientation), mode: BindingMode.TwoWay, source: _contextPopup));
72 SetBinding(IsAutoHidingEnabledProperty, new Binding(nameof(IsAutoHidingEnabled), mode: BindingMode.TwoWay, source: _contextPopup));
73 SetBinding(DirectionPrioritiesProperty, new Binding(nameof(DirectionPriorities), mode: BindingMode.TwoWay, source: _contextPopup));
74 SetBinding(SelectedItemProperty, new Binding(nameof(SelectedItem), mode: BindingMode.TwoWay, source: _contextPopup));
78 /// Occurs when the ContextPopup is dismissed.
80 public event EventHandler Dismissed;
83 /// Occurs when an item is selected.
85 public event EventHandler SelectedIndexChanged;
88 /// Gets or sets the orientation of the ContextPopup.
89 /// The default value is ContextPopupOrientation.Vertical.
91 public ContextPopupOrientation Orientation
93 get { return (ContextPopupOrientation)GetValue(OrientationProperty); }
94 set { SetValue(OrientationProperty, value); }
97 public bool IsAutoHidingEnabled
99 get { return (bool)GetValue(IsAutoHidingEnabledProperty); }
100 set { SetValue(IsAutoHidingEnabledProperty, value); }
104 /// Gets or sets the direction priorities for the ContextPopup.
106 public ContextPopupDirectionPriorities DirectionPriorities
108 get { return (ContextPopupDirectionPriorities)GetValue(DirectionPrioritiesProperty); }
109 set { SetValue(DirectionPrioritiesProperty, value); }
113 /// Gets or sets the index of the selected item of the ContextPopup.
114 /// It is -1 when no item is selected.
116 public int SelectedIndex
118 get { return (int)GetValue(SelectedIndexProperty); }
119 set { SetValue(SelectedIndexProperty, value); }
123 /// Gets or sets the selected item of the ContextPopup.
125 public object SelectedItem
127 get { return (ContextPopupItem)GetValue(SelectedItemProperty); }
128 set { SetValue(SelectedItemProperty, value); }
132 /// Gets or sets the source list of items for the ContextPopup.
134 public IList ItemsSource
136 get { return (IList)GetValue(ItemsSourceProperty); }
137 set { SetValue(ItemsSourceProperty, value); }
141 /// Gets the list of items in the ContextPopup.
143 public IList<ContextPopupItem> Items
152 /// Shows the ContextPopup.
154 /// <param name="anchor">The View to which the popup should be anchored.</param>
155 public void Show(View anchor)
157 _contextPopup.Show(anchor);
161 /// Dismisses the ContextPopup.
163 public void Dismiss()
165 _contextPopup.Dismiss();
169 /// Gets the direction of the ContextPopup if it is shown.
170 /// This method returns false if it is not shown and the output argument is a default value.
172 /// <param name="direction">The direction of the ContextPopup.</param>
173 /// <returns>true if the ContextPopup is shown, false otherwise.</returns>
174 public bool TryGetContextPopupDirection(out ContextPopupDirection direction)
176 direction = default(ContextPopupDirection);
177 return _contextPopup.TryGetContextPopupDirection(out direction);
180 void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
184 case NotifyCollectionChangedAction.Add:
185 var addedItems = e.NewItems.OfType<ContextPopupItem>();
186 _contextPopup.AddItems(addedItems);
187 foreach (var item in addedItems)
189 item.PropertyChanged += ContextPopupItemPropertyChanged;
193 case NotifyCollectionChangedAction.Remove:
194 _contextPopup.RemoveItems(e.OldItems.OfType<ContextPopupItem>());
197 case NotifyCollectionChangedAction.Reset:
198 _contextPopup.ClearItems();
202 default: //Clear and add again for anything else such as Move
203 _contextPopup.ClearItems();
204 _contextPopup.AddItems(_items);
210 void ContextPopupItemPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
212 //Clear completely and then add all items again because for example, if initially there was
213 //no icon, an icon cannot be added later using the native APIs. However, this scenario is
214 //possible in the Tizen.Xamarin.Forms.Extension APIs.
215 //And, the native APIs do not provide any method to "InsertAt" or "Replace", so we cannot
216 //just replace the item which had a property change.
217 _contextPopup.ClearItems();
218 _contextPopup.AddItems(_items);
221 static void OnSelectedIndexChanged(BindableObject bindable, object oldValue, object newValue)
223 var contextPopup = (ContextPopup)bindable;
224 contextPopup.UpdateSelectedItem();
225 contextPopup.SelectedIndexChanged?.Invoke(contextPopup, EventArgs.Empty);
228 void UpdateSelectedItem()
230 if (SelectedIndex == -1)
236 if (ItemsSource != null)
238 SelectedItem = ItemsSource[SelectedIndex];
242 SelectedItem = Items[SelectedIndex];
245 static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
247 var contextPopup = (ContextPopup)bindable;
248 contextPopup.UpdateSelectedIndex(newValue);
251 void UpdateSelectedIndex(object selectedItem)
253 SelectedIndex = Items.IndexOf(selectedItem);
256 static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
258 ((ContextPopup)bindable).OnItemsSourceChanged((IList)oldValue, (IList)newValue);
261 void OnItemsSourceChanged(IList oldValue, IList newValue)
263 var oldObservable = oldValue as INotifyCollectionChanged;
264 if (oldObservable != null)
265 oldObservable.CollectionChanged -= ItemsCollectionChanged;
267 var newObservable = newValue as INotifyCollectionChanged;
268 if (newObservable != null)
270 newObservable.CollectionChanged += ItemsCollectionChanged;
273 if (newValue != null)
276 foreach (var item in newValue)
278 _items.Add(item as ContextPopupItem);