2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.Collections.Generic;
19 using System.ComponentModel;
22 using Tizen.NUI.BaseComponents;
23 using Tizen.NUI.Binding.Internals;
24 using static Tizen.NUI.Binding.BindableObject;
29 /// [Draft] LayoutGroup class providing container functionality.
31 public class LayoutGroup : LayoutItem, ILayoutParent
34 /// [Draft] List of child layouts in this container.
36 /// <since_tizen> 6 </since_tizen>
37 protected List<LayoutItem> LayoutChildren { get; } // Children of this LayoutGroup
40 /// [Draft] Constructor
42 /// <since_tizen> 6 </since_tizen>
45 LayoutChildren = new List<LayoutItem>();
49 /// returns an enumerable collection of the child layouts that owner's <see cref="View.ExcludeLayouting"/> is false.
51 /// <returns>An enumerable collection of the child layouts that affected by this layout.</returns>
52 [EditorBrowsable(EditorBrowsableState.Never)]
53 protected IEnumerable<LayoutItem> IterateLayoutChildren()
55 for (int i = 0; i < LayoutChildren.Count; i++)
57 LayoutItem childLayout = LayoutChildren[i];
58 if (!childLayout?.Owner?.ExcludeLayouting ?? false)
60 yield return childLayout;
66 /// From ILayoutParent.<br />
68 /// <exception cref="ArgumentNullException"> Thrown when childLayout is null. </exception>
69 /// <since_tizen> 6 </since_tizen>
70 /// <param name="childLayout">LayoutItem to add to the layout group.</param>
71 public virtual void Add(LayoutItem childLayout)
73 if (null == childLayout)
75 throw new ArgumentNullException(nameof(childLayout));
77 LayoutChildren.Add(childLayout);
78 childLayout.SetParent(this);
79 // Child added to use a Add transition.
80 childLayout.ConditionForAnimation = ConditionForAnimation | TransitionCondition.Add;
81 // Child's parent sets all other children not being added to a ChangeOnAdd transition.
82 SetConditionsForAnimationOnLayoutGroup(TransitionCondition.ChangeOnAdd);
83 OnChildAdd(childLayout);
88 /// Remove all layout children.<br />
90 /// <since_tizen> 6 </since_tizen>
91 public void RemoveAll()
93 foreach (LayoutItem childLayout in LayoutChildren)
95 childLayout.ConditionForAnimation = ConditionForAnimation | TransitionCondition.Remove;
96 childLayout.Owner = null;
98 LayoutChildren.Clear();
99 // todo ensure child LayoutItems are still not parented to this group.
104 /// From ILayoutParent
106 /// <param name="layoutItem">LayoutItem to remove from the layout group.</param>
107 /// <since_tizen> 6 </since_tizen>
108 public virtual void Remove(LayoutItem layoutItem)
110 bool childRemoved = false;
111 foreach (LayoutItem childLayout in LayoutChildren.ToList())
113 if (childLayout == layoutItem)
115 childLayout.ClearReplaceFlag();
116 LayoutChildren.Remove(childLayout);
118 if (LayoutWithTransition)
120 if (!childLayout.IsReplaceFlag())
122 NUIApplication.GetDefaultWindow().LayoutController.AddToRemovalStack(childLayout);
125 childLayout.ConditionForAnimation = childLayout.ConditionForAnimation | TransitionCondition.Remove;
126 // Add LayoutItem to the transition stack so can animate it out.
127 NUIApplication.GetDefaultWindow().LayoutController.AddTransitionDataEntry(new LayoutData(layoutItem, ConditionForAnimation, 0, 0, 0, 0));
130 // Reset condition for animation ready for next transition when required.
131 // SetFrame usually would do this but this LayoutItem is being removed.
132 childLayout.ConditionForAnimation = TransitionCondition.Unspecified;
141 // If child removed then set all siblings not being added to a ChangeOnRemove transition.
142 SetConditionsForAnimationOnLayoutGroup(TransitionCondition.ChangeOnRemove);
144 OnChildRemove(layoutItem);
150 /// Sets the sibling order of the layout item so the layout can be defined within the same parent.
152 /// <param name="order">the sibling order of the layout item</param>
153 /// <since_tizen> 6 </since_tizen>
154 /// This will be public opened in tizen_next after ACR done. Before ACR, need to be hidden as inhouse API.
155 [EditorBrowsable(EditorBrowsableState.Never)]
156 public void ChangeLayoutSiblingOrder(int order)
160 var ownerParent = Owner.GetParent() as View;
161 if (ownerParent != null)
163 var parent = ownerParent.Layout as LayoutGroup;
165 if (parent != null && parent.LayoutChildren.Count > order)
167 parent.LayoutChildren.Remove(this);
168 parent.LayoutChildren.Insert(order, this);
175 // Attaches to View ChildAdded signal so called when a View is added to a view.
176 private void AddChildToLayoutGroup(View child)
178 // Only give children a layout if their parent is an explicit container or a pure View.
179 // Pure View meaning not derived from a View, e.g a Legacy container.
180 // layoutSet flag is true when the View became a layout using the set Layout API opposed to automatically due to it's parent.
181 // First time the set Layout API is used by any View the Window no longer has layoutingDisabled.
183 // If child already has a Layout then don't change it.
184 if (!View.LayoutingDisabled && (null == child.Layout))
186 // Only wrap View with a Layout if a child a pure View or Layout explicitly set on this Layout
187 if ((true == Owner.LayoutSet || GetType() == typeof(View)))
189 // If child of this layout is a pure View then assign it a LayoutGroup
190 // If the child is derived from a View then it may be a legacy or existing container hence will do layouting itself.
191 child.Layout = (child as TextLabel)?.CreateTextLayout() ?? new AbsoluteLayout();
196 // Add child layout to this LayoutGroup (Setting parent in the process)
197 if (child.Layout != null)
202 // Parent transitions are not attached to children.
206 /// If the child has a layout then it is removed from the parent layout.
208 /// <param name="child">Child View to remove.</param>
209 internal void RemoveChildFromLayoutGroup(View child)
211 if (child.Layout != null)
213 Remove(child.Layout);
218 /// Set all children in a LayoutGroup to the supplied condition.
219 /// Children with Add or Remove conditions should not be changed.
221 private void SetConditionsForAnimationOnLayoutGroup(TransitionCondition conditionToSet)
223 foreach (LayoutItem childLayout in LayoutChildren)
225 switch (conditionToSet)
227 case TransitionCondition.ChangeOnAdd:
229 // If other children also being added (TransitionCondition.Add) then do not change their
230 // conditions, Continue to use their Add transitions.
231 if (childLayout.ConditionForAnimation.HasFlag(TransitionCondition.Add))
233 break; // Child being Added so don't update it's condition
237 // Set siblings for the child being added to use the ChangeOnAdd transition.
238 childLayout.ConditionForAnimation = TransitionCondition.ChangeOnAdd;
242 case TransitionCondition.ChangeOnRemove:
244 if (childLayout.ConditionForAnimation.HasFlag(TransitionCondition.Remove))
246 break; // Child being Removed so don't update it's condition
250 childLayout.ConditionForAnimation = TransitionCondition.ChangeOnRemove;
254 case TransitionCondition.LayoutChanged:
256 childLayout.ConditionForAnimation = TransitionCondition.LayoutChanged;
265 /// Callback for View.ChildAdded event
267 /// <param name="sender">The object triggering the event.</param>
268 /// <param name="childAddedEvent">Arguments from the event.</param>
269 void OnChildAddedToOwner(object sender, View.ChildAddedEventArgs childAddedEvent)
271 AddChildToLayoutGroup(childAddedEvent.Added);
275 /// Callback for View.ChildRemoved event
277 /// <param name="sender">The object triggering the event.</param>
278 /// <param name="childRemovedEvent">Arguments from the event.</param>
279 void OnChildRemovedFromOwner(object sender, View.ChildRemovedEventArgs childRemovedEvent)
281 RemoveChildFromLayoutGroup(childRemovedEvent.Removed);
285 /// Calculate the right measure spec for this child.
286 /// Does the hard part of MeasureChildren: figuring out the MeasureSpec to
287 /// pass to a particular child. This method figures out the right MeasureSpec
288 /// for one dimension (height or width) of one child view.<br />
290 /// <param name="parentMeasureSpec">The requirements for this view. MeasureSpecification.</param>
291 /// <param name="padding">The padding of this view for the current dimension and margins, if applicable. LayoutLength.</param>
292 /// <param name="childDimension"> How big the child wants to be in the current dimension. LayoutLength.</param>
293 /// <returns>a MeasureSpec for the child.</returns>
294 public static MeasureSpecification GetChildMeasureSpecification(MeasureSpecification parentMeasureSpec, LayoutLength padding, LayoutLength childDimension)
296 MeasureSpecification.ModeType specMode = parentMeasureSpec.Mode;
297 MeasureSpecification.ModeType resultMode = MeasureSpecification.ModeType.Unspecified;
299 // Child only can use parent's size without parent's padding and own margin.
300 LayoutLength resultSize = new LayoutLength(Math.Max(0.0f, (parentMeasureSpec.Size - padding).AsDecimal()));
303 // Parent has imposed an exact size on us
304 case MeasureSpecification.ModeType.Exactly:
306 if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent)
308 resultMode = MeasureSpecification.ModeType.Exactly;
310 else if ((int)childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent)
312 resultMode = MeasureSpecification.ModeType.AtMost;
316 resultSize = childDimension;
317 resultMode = MeasureSpecification.ModeType.Exactly;
323 // Parent has imposed a maximum size on us
324 case MeasureSpecification.ModeType.AtMost:
326 if (childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent)
328 // Crashed. Cannot calculate.
330 // Child wants to be our size, but our size is not fixed.
331 // Constrain child to not be bigger than us.
332 resultMode = MeasureSpecification.ModeType.AtMost;
334 else if (childDimension.AsRoundedValue() == LayoutParamPolicies.WrapContent)
336 // Child wants to determine its own size. It can't be
339 // Don't need parent's size. Size of this child will be determined by its children.
340 resultMode = MeasureSpecification.ModeType.AtMost;
344 // Child wants a specific size... so be it
345 resultSize = childDimension;
346 resultMode = MeasureSpecification.ModeType.Exactly;
352 // Parent asked to see how big we want to be
353 case MeasureSpecification.ModeType.Unspecified:
355 if ((childDimension.AsRoundedValue() == LayoutParamPolicies.MatchParent))
357 // Child wants to be our size... find out how big it should be
359 // There is no one who has exact size in parent hierarchy.
361 resultMode = MeasureSpecification.ModeType.Unspecified;
363 else if (childDimension.AsRoundedValue() == (LayoutParamPolicies.WrapContent))
365 // Child wants to determine its own size.... find out how big
367 resultMode = MeasureSpecification.ModeType.Unspecified;
371 // Child wants a specific size... let him have it
372 resultSize = childDimension;
373 resultMode = MeasureSpecification.ModeType.Exactly;
379 return new MeasureSpecification(resultSize, resultMode);
383 /// Measure the layout and its content to determine the measured width and the measured height.<br />
384 /// If this method is overridden, it is the subclass's responsibility to make
385 /// sure the measured height and width are at least the layout's minimum height
386 /// and width. <br />
388 /// <param name="widthMeasureSpec">horizontal space requirements as imposed by the parent.</param>
389 /// <param name="heightMeasureSpec">vertical space requirements as imposed by the parent.</param>
390 /// <since_tizen> 6 </since_tizen>
391 protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
393 LayoutLength measuredWidth = new LayoutLength(0.0f);
394 LayoutLength measuredHeight = new LayoutLength(0.0f);
396 // Layout takes size of largest child width and largest child height dimensions
397 foreach (LayoutItem childLayout in LayoutChildren)
399 if (childLayout != null)
401 MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
402 LayoutLength childWidth = new LayoutLength(childLayout.MeasuredWidth.Size);
403 LayoutLength childHeight = new LayoutLength(childLayout.MeasuredHeight.Size);
405 Extents childMargin = childLayout.Margin;
406 measuredWidth = new LayoutLength(Math.Max(measuredWidth.AsDecimal(), childWidth.AsDecimal() + childMargin.Start + childMargin.End));
407 measuredHeight = new LayoutLength(Math.Max(measuredHeight.AsDecimal(), childHeight.AsDecimal() + childMargin.Top + childMargin.Bottom));
411 if (0 == LayoutChildren.Count)
413 // Must be a leaf as has no children
414 measuredWidth = GetDefaultSize(SuggestedMinimumWidth, widthMeasureSpec);
415 measuredHeight = GetDefaultSize(SuggestedMinimumHeight, heightMeasureSpec);
418 SetMeasuredDimensions(new MeasuredSize(measuredWidth, MeasuredSize.StateType.MeasuredSizeOK),
419 new MeasuredSize(measuredHeight, MeasuredSize.StateType.MeasuredSizeOK));
422 internal override void OnMeasureIndependentChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
424 foreach (LayoutItem childLayout in LayoutChildren.Where(item => item?.Owner?.ExcludeLayouting ?? false))
426 MeasureChildWithoutPadding(childLayout, widthMeasureSpec, heightMeasureSpec);
431 /// Called from Layout() when this layout should assign a size and position to each of its children.<br />
432 /// Derived classes with children should override this method and call Layout() on each of their children.<br />
434 /// <param name="changed">This is a new size or position for this layout.</param>
435 /// <param name="left">Left position, relative to parent.</param>
436 /// <param name="top"> Top position, relative to parent.</param>
437 /// <param name="right">Right position, relative to parent.</param>
438 /// <param name="bottom">Bottom position, relative to parent.</param>
439 /// <since_tizen> 6 </since_tizen>
440 protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
442 foreach (LayoutItem childLayout in LayoutChildren)
444 if (childLayout != null)
446 // Use position if explicitly set to child otherwise will be top left.
447 var childLeft = new LayoutLength(childLayout.Owner.PositionX);
448 var childTop = new LayoutLength(childLayout.Owner.PositionY);
454 // Margin and Padding only supported when child anchor point is TOP_LEFT.
455 if (owner.PivotPoint == PivotPoint.TopLeft || (owner.PositionUsesPivotPoint == false))
457 childLeft = childLeft + owner.Padding.Start + childLayout.Margin.Start;
458 childTop = childTop + owner.Padding.Top + childLayout.Margin.Top;
461 childLayout.Layout(childLeft, childTop, childLeft + childLayout.MeasuredWidth.Size, childTop + childLayout.MeasuredHeight.Size);
467 /// Layout independent children those Owners have true ExcludeLayouting. <br />
468 /// These children are required not to be affected by this layout. <br />
470 internal override void OnLayoutIndependentChildren(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
472 foreach (LayoutItem childLayout in LayoutChildren.Where(item => item?.Owner?.ExcludeLayouting ?? false))
474 LayoutLength childWidth = childLayout.MeasuredWidth.Size;
475 LayoutLength childHeight = childLayout.MeasuredHeight.Size;
477 LayoutLength childPositionX = new LayoutLength(childLayout.Owner.PositionX);
478 LayoutLength childPositionY = new LayoutLength(childLayout.Owner.PositionY);
480 childLayout.Layout(childPositionX, childPositionY, childPositionX + childWidth, childPositionY + childHeight, true);
485 /// Overridden method called when the layout is attached to an owner.<br />
487 /// <since_tizen> 6 </since_tizen>
488 protected override void OnAttachedToOwner()
490 // Layout takes ownership of it's owner's children.
491 foreach (View view in Owner.Children)
493 if (view is TextLabel)
495 view.Layout = (view as TextLabel)?.CreateTextLayout();
497 AddChildToLayoutGroup(view);
500 // Connect to owner ChildAdded signal.
501 Owner.ChildAdded += OnChildAddedToOwner;
503 // Removing Child from the owners View will directly call the LayoutGroup removal API.
507 /// Virtual method to allow derived classes to remove any children before it is removed from
510 [EditorBrowsable(EditorBrowsableState.Never)]
511 protected override void OnUnparent()
513 // Disconnect to owner ChildAdded signal.
514 Owner.ChildAdded -= OnChildAddedToOwner;
517 // Virtual Methods that can be overridden by derived classes.
520 /// Callback when child is added to container.<br />
521 /// Derived classes can use this to set their own child properties on the child layout's owner.<br />
523 /// <param name="child">The Layout child.</param>
524 /// <since_tizen> 6 </since_tizen>
525 protected virtual void OnChildAdd(LayoutItem child)
530 /// Callback when child is removed from container.<br />
532 /// <param name="child">The Layout child.</param>
533 /// <since_tizen> 6 </since_tizen>
534 protected virtual void OnChildRemove(LayoutItem child)
539 /// Ask all of the children of this view to measure themselves, taking into
540 /// account both the MeasureSpec requirements for this view and its padding.<br />
541 /// The heavy lifting is done in GetChildMeasureSpec.<br />
543 /// <param name="widthMeasureSpec">The width requirements for this view.</param>
544 /// <param name="heightMeasureSpec">The height requirements for this view.</param>
545 /// <since_tizen> 6 </since_tizen>
546 protected virtual void MeasureChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
548 foreach (LayoutItem childLayout in LayoutChildren)
550 MeasureChildWithMargins(childLayout, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
555 /// Ask one of the children of this view to measure itself, taking into
556 /// account both the MeasureSpec requirements for this view and its padding.<br />
557 /// The heavy lifting is done in GetChildMeasureSpecification.<br />
559 /// <param name="child">The child to measure.</param>
560 /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param>
561 /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param>
562 /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
563 /// <since_tizen> 6 </since_tizen>
564 protected virtual void MeasureChild(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, MeasureSpecification parentHeightMeasureSpec)
568 throw new ArgumentNullException(nameof(child));
571 View childOwner = child.Owner;
573 MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
574 new MeasureSpecification(new LayoutLength(parentWidthMeasureSpec.Size), parentWidthMeasureSpec.Mode),
575 new LayoutLength(Padding.Start + Padding.End),
576 new LayoutLength(childOwner.WidthSpecification));
578 MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
579 new MeasureSpecification(new LayoutLength(parentHeightMeasureSpec.Size), parentHeightMeasureSpec.Mode),
580 new LayoutLength(Padding.Top + Padding.Bottom),
581 new LayoutLength(childOwner.HeightSpecification));
583 child.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
587 /// Ask one of the children of this view to measure itself, taking into
588 /// account both the MeasureSpec requirements for this view and its padding.<br />
589 /// and margins. The heavy lifting is done in GetChildMeasureSpecification.<br />
591 /// <param name="child">The child to measure.</param>
592 /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param>
593 /// <param name="widthUsed">Extra space that has been used up by the parent horizontally (possibly by other children of the parent).</param>
594 /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param>
595 /// <param name="heightUsed">Extra space that has been used up by the parent vertically (possibly by other children of the parent).</param>
596 /// <exception cref="ArgumentNullException"> Thrown when child is null. </exception>
597 /// <since_tizen> 6 </since_tizen>
598 protected virtual void MeasureChildWithMargins(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, LayoutLength widthUsed, MeasureSpecification parentHeightMeasureSpec, LayoutLength heightUsed)
602 throw new ArgumentNullException(nameof(child));
605 View childOwner = child.Owner;
606 Extents margin = childOwner.Margin;
608 MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
609 new MeasureSpecification(
610 new LayoutLength(parentWidthMeasureSpec.Size + widthUsed - (margin.Start + margin.End)),
611 parentWidthMeasureSpec.Mode),
612 new LayoutLength(Padding.Start + Padding.End),
613 new LayoutLength(childOwner.WidthSpecification));
615 MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
616 new MeasureSpecification(
617 new LayoutLength(parentHeightMeasureSpec.Size + heightUsed - (margin.Top + margin.Bottom)),
618 parentHeightMeasureSpec.Mode),
619 new LayoutLength(Padding.Top + Padding.Bottom),
620 new LayoutLength(childOwner.HeightSpecification));
621 child.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
626 /// Ask one of the children of this view to measure itself, taking into
627 /// account both the MeasureSpec requirements for this view and without its padding.<br />
628 /// and margins. The heavy lifting is done in GetChildMeasureSpecification.<br />
630 /// <param name="child">The child to measure.</param>
631 /// <param name="parentWidthMeasureSpec">The width requirements for this view.</param>
632 /// <param name="parentHeightMeasureSpec">The height requirements for this view.</param>
633 [EditorBrowsable(EditorBrowsableState.Never)]
634 protected void MeasureChildWithoutPadding(LayoutItem child, MeasureSpecification parentWidthMeasureSpec, MeasureSpecification parentHeightMeasureSpec)
636 View childOwner = child.Owner;
638 MeasureSpecification childWidthMeasureSpec = GetChildMeasureSpecification(
639 new MeasureSpecification(new LayoutLength(parentWidthMeasureSpec.Size), parentWidthMeasureSpec.Mode),
641 new LayoutLength(childOwner.WidthSpecification));
643 MeasureSpecification childHeightMeasureSpec = GetChildMeasureSpecification(
644 new MeasureSpecification(new LayoutLength(parentHeightMeasureSpec.Size), parentHeightMeasureSpec.Mode),
646 new LayoutLength(childOwner.HeightSpecification));
648 child.Measure(childWidthMeasureSpec, childHeightMeasureSpec);
652 /// Gets the value that is contained in the attached BindableProperty.
654 /// <typeparam name="T">The return type of property</typeparam>
655 /// <param name="bindable">The bindable object.</param>
656 /// <param name="property">The BindableProperty for which to get the value.</param>
657 /// <returns>The value that is contained in the attached BindableProperty.</returns>
658 [EditorBrowsable(EditorBrowsableState.Never)]
659 public static T GetAttachedValue<T>(Binding.BindableObject bindable, Binding.BindableProperty property)
661 if (bindable == null)
662 throw new ArgumentNullException(nameof(bindable));
664 return (T)bindable.GetValue(property);
668 /// Sets the value of the attached property.
670 /// <param name="bindable">The bindable object.</param>
671 /// <param name="property">The BindableProperty on which to assign a value.</param>
672 /// <param name="value">The value to set.</param>
673 [EditorBrowsable(EditorBrowsableState.Never)]
674 public static void SetAttachedValue(Binding.BindableObject bindable, Binding.BindableProperty property, object value)
676 if (bindable == null)
677 throw new ArgumentNullException(nameof(bindable));
679 bindable.SetValueCore(property, value, SetValueFlags.None, SetValuePrivateFlags.ManuallySet, false);
681 internal static void OnChildPropertyChanged(Binding.BindableObject bindable, object oldValue, object newValue)
687 View view = bindable as View;
688 view?.Layout?.RequestLayout();
691 internal static Binding.BindableProperty.ValidateValueDelegate ValidateEnum(int enumMin, int enumMax)
693 return (Binding.BindableObject bindable, object value) =>
695 int @enum = (int)value;
696 return enumMin <= @enum && @enum <= enumMax;