[NUI] Improve Theme load
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Wearable / src / public / CircularPagination.cs
1 /*
2  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 using System;
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
21 using Tizen.NUI.Components;
22
23 namespace Tizen.NUI.Wearable
24 {
25     /// <summary>
26     /// CircularPagination shows the number of pages available and the currently active page.
27     /// Especially, CircularPagination provides indicators specific to wearable device.
28     /// </summary>
29     /// <since_tizen> 8 </since_tizen>
30     /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
31     [EditorBrowsable(EditorBrowsableState.Never)]
32     public class CircularPagination: Control
33     {
34         private CircularPaginationStyle circularPaginationStyle;
35
36         private VisualView container;
37
38         private List<ImageVisual> indicatorList = new List<ImageVisual>();
39
40         private bool isSymmetrical = true;
41         private int middleIndex = 9;
42         private int indicatorCount = 0;
43         private int leftIndicatorCount = 0;
44         private int rightIndicatorCount = 0;
45         private int selectedIndex = -1;
46         private bool isCenterImageSet = false; // When CenterIndicatorImageURL is set, this variable becomes true.
47         private bool isCurrentIndicatorCentered = false; // When the current indicator is the center one, this variable becomes true.
48         private bool isOddNumber = true;
49         private bool uninitializedLeftIndicator = true; // Need it when the indicators are asymmetry and the right indicator count is set earlier than left one.
50         private Animation selectAnimation = null;
51         private bool isNeedAnimation = false; // TODO : Animation will support using override function later.
52
53         Position2D[] oddArray = new Position2D[] { new Position2D(36, 74), new Position2D(47, 60), new Position2D(60, 47), new Position2D(74, 36),
54                                                    new Position2D(89, 26), new Position2D(105, 18), new Position2D(122, 11), new Position2D(139, 7),
55                                                    new Position2D(157, 4), new Position2D(175, 3), new Position2D(193, 4), new Position2D(211, 7),
56                                                    new Position2D(228, 11), new Position2D(245, 18), new Position2D(261, 26), new Position2D(276, 36),
57                                                    new Position2D(290, 47), new Position2D(303, 60), new Position2D(314, 73) };
58
59         Position2D[] evenArray = new Position2D[] { new Position2D(41, 67), new Position2D(53, 53), new Position2D(67, 41), new Position2D(81, 31),
60                                                    new Position2D(97, 22), new Position2D(113, 14), new Position2D(131, 9), new Position2D(148, 5),
61                                                    new Position2D(166, 3), new Position2D(184, 3), new Position2D(202, 5), new Position2D(220, 9),
62                                                    new Position2D(237, 14), new Position2D(253, 22), new Position2D(269, 31), new Position2D(283, 41),
63                                                    new Position2D(297, 53), new Position2D(309, 67) };
64
65         static CircularPagination()
66         {
67             ThemeManager.AddPackageTheme(new DefaultThemeCreator());
68         }
69
70         /// <summary>
71         /// Creates a new instance of a CircularPagination.
72         /// </summary>
73         /// <since_tizen> 8 </since_tizen>
74         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
75         [EditorBrowsable(EditorBrowsableState.Never)]
76         public CircularPagination() : base()
77         {
78             Initialize();
79         }
80
81         /// <summary>
82         /// Creates a new instance of a CircularPagination using style.
83         /// </summary>
84         /// <since_tizen> 8 </since_tizen>
85         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
86         [EditorBrowsable(EditorBrowsableState.Never)]
87         public CircularPagination(CircularPaginationStyle style) : base(style)
88         {
89             Initialize();
90         }
91
92         /// <summary>
93         /// Gets or sets the size of the indicator.
94         /// </summary>
95         /// <since_tizen> 8 </since_tizen>
96         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
97         [EditorBrowsable(EditorBrowsableState.Never)]
98         public Size IndicatorSize
99         {
100             get
101             {
102                 return circularPaginationStyle?.IndicatorSize;
103             }
104             set
105             {
106                 if (value == null || circularPaginationStyle == null)
107                 {
108                     return;
109                 }
110                 circularPaginationStyle.IndicatorSize = value;
111                 UpdateVisual();
112             }
113         }
114
115         /// <summary>
116         /// Gets or sets the background resource of indicator.
117         /// </summary>
118         /// <since_tizen> 8 </since_tizen>
119         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
120         [EditorBrowsable(EditorBrowsableState.Never)]
121         public Selector<string> IndicatorImageURL
122         {
123             get
124             {
125                 return circularPaginationStyle?.IndicatorImageURL;
126             }
127             set
128             {
129                 if (value == null || circularPaginationStyle == null)
130                 {
131                     return;
132                 }
133                 circularPaginationStyle.IndicatorImageURL = value;
134                 UpdateVisual();
135             }
136         }
137
138         /// <summary>
139         /// Gets or sets the background resource of the center indicator.
140         /// </summary>
141         /// <since_tizen> 8 </since_tizen>
142         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
143         [EditorBrowsable(EditorBrowsableState.Never)]
144         public Selector<string> CenterIndicatorImageURL
145         {
146             get
147             {
148                 if (isCenterImageSet)
149                 {
150                     return circularPaginationStyle?.CenterIndicatorImageURL;
151                 }
152                 else
153                 {
154                     Log.Info("NUI", "CenterIndicatorImageURL is not set yet. \n");
155                     return "";
156                 }
157
158             }
159             set
160             {
161                 if (value == null || circularPaginationStyle == null)
162                 {
163                     return;
164                 }
165                 circularPaginationStyle.CenterIndicatorImageURL = value;
166                 isCenterImageSet = true;
167                 UpdateVisual();
168             }
169         }
170
171         /// <summary>
172         /// Checks whether the indicators are symmetrical or not.
173         ///
174         /// The default value is true.
175         /// If the value is true, the user just can set IndicatorCount.
176         /// If false, the user should set both the number of Left Indicators and the number of Right Indicators.
177         /// Please refer to LeftIndicatorCount and RightIndicatorCount.
178         /// </summary>
179         /// <since_tizen> 8 </since_tizen>
180         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
181         [EditorBrowsable(EditorBrowsableState.Never)]
182         public bool IsSymmetrical
183         {
184             get
185             {
186                 return isSymmetrical;
187             }
188             set
189             {
190                 if (isSymmetrical == value)
191                 {
192                     return;
193                 }
194                 if (value == false)
195                 {
196                     isOddNumber = true;
197                     CreateIndicator(middleIndex);
198                 }
199
200                 isSymmetrical = value;
201                 UpdateContainer();
202                 UpdateVisual();
203             }
204         }
205
206
207         /// <summary>
208         /// Gets or sets the number of the pages/indicators.
209         ///
210         /// This value is for symmetrical indicators.
211         /// </summary>
212         /// <since_tizen> 8 </since_tizen>
213         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
214         [EditorBrowsable(EditorBrowsableState.Never)]
215         public int IndicatorCount
216         {
217             get
218             {
219                 return indicatorCount;
220             }
221             set
222             {
223                 if (indicatorCount == value || indicatorCount < 0 || value <= 0)
224                 {
225                     return;
226                 }
227                 if (isSymmetrical == false)
228                 {
229                     Log.Info("NUI", "This property is not for asymmetric pagination. Change to symmetrical pagination.\n");
230                     isSymmetrical = true;
231                 }
232
233                 if (value % 2 == 1) // Odd number
234                 {
235                     isOddNumber = true;
236                 }
237                 else // Even number
238                 {
239                     isOddNumber = false;
240                 }
241
242                 if (indicatorCount < value)
243                 {
244                     int arrayIndex = 0;
245                     if (isOddNumber)
246                     {
247                         arrayIndex = (19 - value) / 2;
248                     }
249                     else
250                     {
251                         arrayIndex = (18 - value) / 2;
252                     }
253                     if (arrayIndex < 0) return;
254
255                     for (int i = (indicatorCount + 1); i <= value; i++)
256                     {
257                         CreateIndicator( arrayIndex );
258                         arrayIndex++;
259                     }
260
261                     // If selectedIndex is not set yet, the default value is middle index.
262                     if (selectedIndex == -1)
263                     {
264                         selectedIndex = value / 2;
265                         SelectIn(indicatorList[selectedIndex]);
266                     }
267                 }
268                 else
269                 {
270                     for (int i = value; i < indicatorCount; i++)
271                     {
272                         ImageVisual indicator = indicatorList[i];
273                         container.RemoveVisual("Indicator" + i);
274                     }
275                     indicatorList.RemoveRange(value, indicatorCount - value);
276
277                     if (selectedIndex >= value)
278                     {
279                         selectedIndex = value - 1;
280                         SelectIn(indicatorList[selectedIndex]);
281                     }
282                 }
283                 indicatorCount = value;
284
285                 UpdateContainer();
286                 UpdateVisual();
287             }
288         }
289
290         /// <summary>
291         /// Gets or sets the number of the left pages/indicators.
292         ///
293         /// This value can be set when IsSymmetrical API is false.
294         /// </summary>
295         /// <since_tizen> 8 </since_tizen>
296         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
297         [EditorBrowsable(EditorBrowsableState.Never)]
298         public int LeftIndicatorCount
299         {
300             get
301             {
302                 return leftIndicatorCount;
303             }
304             set
305             {
306                 if (isSymmetrical == true)
307                 {
308                     Log.Info("NUI", "This variable is not for symmetric pagination. \n");
309                     isSymmetrical = false;
310                     //return;
311                 }
312                 if (leftIndicatorCount == value || leftIndicatorCount < 0 || value > 9 || value < 0)
313                 {
314                     return;
315                 }
316
317                 isOddNumber = true;
318
319                 if (leftIndicatorCount < value)
320                 {
321                     for (int i = (middleIndex - value); i < (middleIndex - leftIndicatorCount); i++)
322                     {
323                         CreateIndicator( i );
324                         selectedIndex++;
325                     }
326                 }
327                 else
328                 {
329                     for (int i = 0; i < (leftIndicatorCount - value); i++)
330                     {
331                         ImageVisual indicator = indicatorList[i];
332                         container.RemoveVisual("Indicator" + i);
333                     }
334                     indicatorList.RemoveRange(0, (leftIndicatorCount - value)); // LeftIndicator starts from index 0.
335
336                     if (selectedIndex == 0)
337                     {
338                         selectedIndex++;
339                         SelectIn(indicatorList[selectedIndex]);
340                     }
341                     else
342                     {
343                         selectedIndex--;
344                         SelectIn(indicatorList[selectedIndex]);
345                     }
346                 }
347                 leftIndicatorCount = value;
348                 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
349
350                 // When RightIndicatorCount is set before, then selectedIndex should be updated using the current LeftIndicatorCount.
351                 if (uninitializedLeftIndicator && selectedIndex == 0)
352                 {
353                     selectedIndex = leftIndicatorCount;
354                 }
355                 uninitializedLeftIndicator = false;
356
357                 UpdateContainer();
358                 UpdateAsymmetry();
359             }
360         }
361
362         /// <summary>
363         /// Gets or sets the number of the right pages/indicators.
364         ///
365         /// This value can be set when IsSymmetrical API is false.
366         /// </summary>
367         /// <since_tizen> 8 </since_tizen>
368         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
369         [EditorBrowsable(EditorBrowsableState.Never)]
370         public int RightIndicatorCount
371         {
372             get
373             {
374                 return rightIndicatorCount;
375             }
376             set
377             {
378                 if (isSymmetrical == true)
379                 {
380                     Log.Info("NUI", "This variable is not for symmetric pagination. \n");
381                     isSymmetrical = false;
382                     //return;
383                 }
384                 if (rightIndicatorCount == value || rightIndicatorCount < 0 || value > 9 || value < 0)
385                 {
386                     return;
387                 }
388
389                 isOddNumber = true;
390
391                 if (rightIndicatorCount < value)
392                 {
393                     for (int i = (middleIndex + rightIndicatorCount + 1); i <= (middleIndex + value); i++)
394                     {
395                         CreateIndicator( i );
396                     }
397                 }
398                 else
399                 {
400                     for (int i = (leftIndicatorCount + value + 1); i < (leftIndicatorCount + rightIndicatorCount); i++)
401                     {
402                         ImageVisual indicator = indicatorList[i];
403                         container.RemoveVisual("Indicator" + i);
404                     }
405                     indicatorList.RemoveRange((leftIndicatorCount + value), (rightIndicatorCount - value));
406
407                     if (selectedIndex >= (leftIndicatorCount + rightIndicatorCount))
408                     {
409                         selectedIndex--;
410                         SelectIn(indicatorList[selectedIndex]);
411                     }
412                 }
413                 rightIndicatorCount = value;
414                 indicatorCount = leftIndicatorCount + rightIndicatorCount + 1;
415
416                 UpdateContainer();
417                 UpdateAsymmetry();
418             }
419         }
420
421         /// <summary>
422         /// Gets or sets the index of the select indicator.
423         ///
424         /// If no value is set, the default value is the center indicator.
425         /// </summary>
426         /// <since_tizen> 8 </since_tizen>
427         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
428         [EditorBrowsable(EditorBrowsableState.Never)]
429         public int SelectedIndex
430         {
431             get
432             {
433                 return selectedIndex;
434             }
435             set
436             {
437                 if (selectedIndex == value || value < 0 || value >= indicatorCount)
438                 {
439                     return;
440                 }
441
442                 // TODO : Here it needs to add virtual function for Animation.
443
444                 if (selectedIndex >= 0)
445                 {
446                     if ( (isSymmetrical && selectedIndex < indicatorCount) ||
447                          (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
448                     {
449                         CheckCenterIndicator(selectedIndex);
450                         SelectOut(indicatorList[selectedIndex]);
451                     }
452                 }
453                 selectedIndex = value;
454                 if (selectedIndex >= 0)
455                 {
456                     if ( (isSymmetrical && selectedIndex < indicatorCount) ||
457                          (!isSymmetrical && selectedIndex <= (middleIndex + rightIndicatorCount) ) )
458                     {
459                         CheckCenterIndicator(selectedIndex);
460                         SelectIn(indicatorList[selectedIndex]);
461                     }
462                 }
463             }
464         }
465
466         /// <summary>
467         /// Retrieves the position of a indicator by index.
468         /// </summary>
469         /// <param name="index">Indicator index</param>
470         /// <returns>The position of a indicator by index</returns>
471         /// <since_tizen> 8 </since_tizen>
472         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
473         [EditorBrowsable(EditorBrowsableState.Never)]
474         public Position GetIndicatorPosition(int index)
475         {
476             if (index < 0 || index >= indicatorList.Count)
477             {
478                 return null;
479             }
480             return new Position(indicatorList[index].Position.X, indicatorList[index].Position.Y);
481         }
482
483         /// <summary>
484         /// Sets the position of a indicator by index.
485         /// </summary>
486         /// <param name="index">Indicator index</param>
487         /// <param name="position">The position of a indicator by index</param>
488         /// <exception cref="ArgumentNullException">This exception can occur by the position is null.</exception>
489         /// <since_tizen> 8 </since_tizen>
490         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
491         [EditorBrowsable(EditorBrowsableState.Never)]
492         public virtual void SetIndicatorPosition(int index, Position position)
493         {
494             if (position == null)
495             {
496                 throw new ArgumentNullException(nameof(position));
497             }
498             // Update odd / even Array and List by each converted index.
499             if (isOddNumber)
500             {
501                 if (isSymmetrical)
502                 {
503                     oddArray[(middleIndex - (indicatorCount / 2) + index)] = position;
504                 }
505                 else // IsSymmetrical is false and it means the number of left indicators is different from that of right ones.
506                 {
507                     oddArray[(middleIndex - leftIndicatorCount) + index] = position;
508                 }
509                 indicatorList[index].Position = new Vector2(position.X, position.Y);
510             }
511             else // Only symmetry circular pagination can be even number.
512             {
513                 evenArray[(middleIndex - (indicatorCount / 2) + index)] = position;
514                 indicatorList[index].Position = new Vector2(position.X, position.Y);
515             }
516             UpdateVisual();
517         }
518
519         private void CreateSelectAnimation()
520         {
521             if (selectAnimation == null)
522             {
523                 selectAnimation = new Animation(250);
524             }
525         }
526
527         /// <summary>
528         /// You can override it to do your select out operation.
529         /// </summary>
530         /// <param name="selectOutIndicator">The indicator will be selected out</param>
531         /// <since_tizen> 8 </since_tizen>
532         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
533         [EditorBrowsable(EditorBrowsableState.Never)]
534         protected virtual void SelectOut(VisualMap selectOutIndicator)
535         {
536             if (!(selectOutIndicator is ImageVisual visual)) return;
537             if (isCurrentIndicatorCentered)
538             {
539                 visual.URL = circularPaginationStyle?.CenterIndicatorImageURL?.Normal;
540             }
541             else
542             {
543                 visual.URL = circularPaginationStyle?.IndicatorImageURL?.Normal;
544             }
545             visual.Opacity = 0.5f;
546         }
547
548         /// <summary>
549         /// You can override it to do your select in operation.
550         /// </summary>
551         /// <param name="selectInIndicator">The indicator will be selected in</param>
552         /// <since_tizen> 8 </since_tizen>
553         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
554         [EditorBrowsable(EditorBrowsableState.Never)]
555         protected virtual void SelectIn(VisualMap selectInIndicator)
556         {
557             if (!(selectInIndicator is ImageVisual visual)) return;
558             if (isCurrentIndicatorCentered)
559             {
560                 visual.URL = circularPaginationStyle?.CenterIndicatorImageURL?.Selected;
561             }
562             else
563             {
564                 visual.URL = circularPaginationStyle?.IndicatorImageURL?.Selected;
565             }
566             visual.Opacity = 1.0f;
567         }
568
569         /// <summary>
570         /// you can override it to create your own default style.
571         /// </summary>
572         /// <since_tizen> 8 </since_tizen>
573         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
574         [EditorBrowsable(EditorBrowsableState.Never)]
575         protected override ViewStyle CreateViewStyle()
576         {
577             return new CircularPaginationStyle();
578         }
579
580         /// <summary>
581         /// you can override it to clean-up your own resources.
582         /// </summary>
583         /// <param name="type">DisposeTypes</param>
584         /// <since_tizen> 8 </since_tizen>
585         /// This will be public opened in tizen_6.0 after ACR done. Before ACR, need to be hidden as inhouse API.
586         [EditorBrowsable(EditorBrowsableState.Never)]
587         protected override void Dispose(DisposeTypes type)
588         {
589             if (disposed)
590             {
591                 return;
592             }
593
594             if (type == DisposeTypes.Explicit)
595             {
596                 if (selectAnimation != null)
597                 {
598                     if (selectAnimation.State == Animation.States.Playing)
599                     {
600                         selectAnimation.Stop();
601                     }
602                     selectAnimation.Dispose();
603                     selectAnimation = null;
604                 }
605
606                 container.RemoveAll();
607                 indicatorList.Clear();
608
609                 this.Remove(container);
610                 container.Dispose();
611                 container = null;
612             }
613
614             base.Dispose(type);
615         }
616
617         private void Initialize()
618         {
619             circularPaginationStyle = Style as CircularPaginationStyle;
620             if (circularPaginationStyle == null)
621             {
622                 throw new Exception("CircularPagination style is null.");
623             }
624
625             container = new VisualView()
626             {
627                 Name = "Container",
628                 ParentOrigin = Tizen.NUI.ParentOrigin.TopLeft,
629                 PivotPoint = Tizen.NUI.PivotPoint.TopLeft,
630                 PositionUsesPivotPoint = true,
631             };
632             this.Add(container);
633         }
634
635         // The parameter, index, is for the index of either oddArray or evenArray.
636         private void CreateIndicator(int index)
637         {
638             if (circularPaginationStyle == null || circularPaginationStyle.IndicatorSize == null)
639             {
640                 return;
641             }
642
643             ImageVisual indicator = new ImageVisual
644             {
645                 URL = circularPaginationStyle.IndicatorImageURL?.Normal,
646                 Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
647                 Opacity = 0.5f,
648             };
649
650             if (isOddNumber)
651             {
652                 indicator.Position = oddArray[index];
653             }
654             else
655             {
656                 indicator.Position = evenArray[index];
657             }
658
659             container.AddVisual("Indicator" + indicatorList.Count, indicator);
660             indicatorList.Add(indicator);
661         }
662
663         private void CheckCenterIndicator(int index)
664         {
665             if (isCenterImageSet &&
666                 (isSymmetrical && (index == indicatorCount / 2)) ||
667                 (!isSymmetrical && (index == leftIndicatorCount)) )
668             {
669                 isCurrentIndicatorCentered  = true;
670             }
671             else
672             {
673                 isCurrentIndicatorCentered = false;
674             }
675         }
676
677         private void UpdateContainer()
678         {
679             if (circularPaginationStyle == null || circularPaginationStyle.IndicatorSize == null || container == null)
680             {
681                 return;
682             }
683             if (indicatorList.Count > 0)
684             {
685                 container.SizeWidth = (circularPaginationStyle.IndicatorSize.Width) * indicatorList.Count;
686             }
687             else
688             {
689                 container.SizeWidth = 0;
690             }
691             container.SizeHeight = circularPaginationStyle.IndicatorSize.Height;
692         }
693
694         private void UpdateVisual()
695         {
696             if (null == circularPaginationStyle.IndicatorSize) return;
697             if (null == circularPaginationStyle.IndicatorImageURL) return;
698             if (indicatorCount <= 0) return;
699
700             for (int i = 0; i < indicatorList.Count; i++)
701             {
702                 ImageVisual indicator = indicatorList[i];
703                 indicator.Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height);
704
705                 CheckCenterIndicator(i);
706
707                 if (i == selectedIndex)
708                 {
709                     // If the center image is set before, then should update the center visual separately.
710                     if (isCurrentIndicatorCentered)
711                     {
712                         indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
713                     }
714                     else
715                     {
716                         indicator.URL = circularPaginationStyle.IndicatorImageURL.Selected;
717                     }
718                     indicator.Opacity = 1.0f;
719                 }
720                 else
721                 {
722                     // If the center image is set before, then should update the center visual separately.
723                     if (isCurrentIndicatorCentered)
724                     {
725                         indicator.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
726                     }
727                     else
728                     {
729                         indicator.URL = circularPaginationStyle.IndicatorImageURL.Normal;
730                     }
731                     indicator.Opacity = 0.5f;
732                 }
733
734                 if (isOddNumber)
735                 {
736                     if (isSymmetrical)
737                     {
738                         indicator.Position = oddArray[middleIndex - (indicatorCount / 2) + i];
739                     }
740                     else
741                     {
742                         indicator.Position = oddArray[(middleIndex - leftIndicatorCount) + i];
743                     }
744
745                 }
746                 else
747                 {
748                     indicator.Position = evenArray[middleIndex - (indicatorCount / 2) + i];
749                 }
750             }
751         }
752
753         private void UpdateAsymmetry()
754         {
755             if (null == circularPaginationStyle.IndicatorSize) return;
756             if (null == circularPaginationStyle.IndicatorImageURL) return;
757
758             int listCount = indicatorList.Count;
759
760             for (int i = 0; i < listCount; i++)
761             {
762                 container.RemoveVisual("Indicator" + i);
763             }
764             container.RemoveAll();
765             indicatorList.Clear();
766
767             for (int i = 0; i < listCount; i++)
768             {
769                 ImageVisual newOne = new ImageVisual
770                 {
771                     Size = new Size2D((int)circularPaginationStyle.IndicatorSize.Width, (int)circularPaginationStyle.IndicatorSize.Height),
772                     Position = oddArray[i + (middleIndex - leftIndicatorCount)]
773                 };
774
775                 if (isCenterImageSet && !isSymmetrical && (i == leftIndicatorCount))
776                 {
777                     newOne.URL = circularPaginationStyle.CenterIndicatorImageURL.Normal;
778                 }
779                 else
780                 {
781                     newOne.URL = circularPaginationStyle.IndicatorImageURL.Normal;
782                 }
783                 newOne.Opacity  = 0.5f;
784                 container.AddVisual("Indicator" + i, newOne);
785                 indicatorList.Add(newOne);
786             }
787
788             // If selectedIndex is not set yet, the default value is middle index.
789             if (selectedIndex == -1)
790             {
791                 selectedIndex = leftIndicatorCount;
792             }
793
794             if (isCenterImageSet && (selectedIndex == leftIndicatorCount))
795             {
796                 indicatorList[selectedIndex].URL = circularPaginationStyle.CenterIndicatorImageURL.Selected;
797                 indicatorList[selectedIndex].Opacity = 1.0f;
798             }
799             else
800             {
801                 indicatorList[selectedIndex].URL = circularPaginationStyle.IndicatorImageURL.Selected;
802                 indicatorList[selectedIndex].Opacity = 1.0f;
803             }
804         }
805     }
806 }