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