public override void Apply(XmlElement xmlElement, string value, AttributeDictionary elementAttributes) { var _xmlElement = xmlElement; if (!Application.isPlaying) { XmlLayoutTimer.AtEndOfFrame(() => { var size = _xmlElement.GetComponent <UnityEngine.UI.Image>().sprite.rect; _xmlElement.SetAttribute("width", size.width.ToString()); _xmlElement.SetAttribute("height", size.height.ToString()); _xmlElement.ApplyAttributes(); }, _xmlElement); } else { _xmlElement.ExecuteNowOrWhenElementIsEnabled(() => { XmlLayoutTimer.AtEndOfFrame(() => { if (_xmlElement == null) { return; } var size = _xmlElement.GetComponent <UnityEngine.UI.Image>().sprite.rect; _xmlElement.SetAttribute("width", size.width.ToString()); _xmlElement.SetAttribute("height", size.height.ToString()); _xmlElement.ApplyAttributes(); }, null, true); }); } }
public override void Close() { var section = currentInstanceTransform.GetComponentInParent <XmlLayoutAccordionSection>(); var transformTemp = transformToAddChildrenTo; XmlLayoutTimer.AtEndOfFrame(() => { transformTemp.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, section.contentHeight); if (transformTemp.childCount > 0) { var firstChild = transformTemp.GetChild(0); var xmlElement = firstChild.GetComponent <XmlElement>(); if (xmlElement != null && xmlElement.tagType.EndsWith("ScrollView")) { xmlElement.ApplyAttributes(new AttributeDictionary() { { "width", "100%" }, { "height", "100%" } }); } } }, section); }
public override void ApplyAttributes(AttributeDictionary attributesToApply) { // reset XmlElement values to their default (except for new values provided) // the reason we do this is because unlike most XmlElements, the XmlLayout's XmlElement is not destroyed before a rebuild // if we don't reset the values here, then if you were to clear an existing attribute value (e.g. showAnimation), then it would still be there after the rebuild attributesToApply = XmlLayoutUtilities.MergeAttributes(defaultAttributeValues, attributesToApply); base.ApplyAttributes(attributesToApply); if (!Application.isPlaying) { return; } if (attributesToApply.ContainsKey("cursor")) { XmlLayoutTimer.AtEndOfFrame(() => { XmlLayoutCursorController.Instance.SetCursorForState(XmlLayoutCursorController.eCursorState.Default, attributesToApply["cursor"].ToCursorInfo(), true); }, currentXmlElement); } if (attributesToApply.ContainsKey("cursorClick")) { XmlLayoutTimer.AtEndOfFrame(() => { XmlLayoutCursorController.Instance.SetCursorForState(XmlLayoutCursorController.eCursorState.Click, attributesToApply["cursorClick"].ToCursorInfo(), true); }, currentXmlElement); } }
public override void Close() { var tmp = (primaryComponent as TextMeshProUGUI); XmlLayoutTimer.AtEndOfFrame(() => { // if we don't do this again, some attributes like outline/etc. seem to be lost ApplyAttributes(currentXmlElement.attributes); }, tmp); }
public override void Apply(XmlElement xmlElement, string value, AttributeDictionary attributes) { var aspectRatioFitter = GetAspectRatioFitter(xmlElement); aspectRatioFitter.aspectRatio = ParseAspectRatioStringValue(value); // Seems to only take effect if we set it again at the end of the frame XmlLayoutTimer.AtEndOfFrame(() => { aspectRatioFitter.aspectRatio = ParseAspectRatioStringValue(value); }, xmlElement); }
public override void Apply(XmlElement xmlElement, string value, AttributeDictionary elementAttributes) { if (value.ToBoolean()) { var selectable = xmlElement.GetComponent <Selectable>(); if (selectable != null) { XmlLayoutTimer.AtEndOfFrame(() => selectable.Select(), xmlElement); } } }
/// <summary> /// Render a new list item element /// </summary> /// <param name="itemData"></param> /// <param name="dataSource"></param> /// <param name="itemTemplate"></param> /// <param name="list"></param> internal void RenderListItem(object itemData, string dataSource, XmlElement itemTemplate, IObservableList list) { // Create the new item based on the item template for this list var element = (XmlElement)GameObject.Instantiate(itemTemplate); var parent = currentListElement.listElement; // Add the new item to the list container parent.AddChildElement(element); // The template will be inactive by default, we need to make it active element.SetAttribute("active", "true"); // Get/Add the List Item component and add it to the element var listItemComponent = element.gameObject.GetComponent <XmlLayoutListItem>() ?? element.gameObject.AddComponent <XmlLayoutListItem>(); listItemComponent.guid = list.GetGUID(itemData); // Add this list item to the list's item collection var index = list.IndexOf(itemData); currentListElement.listItems.Insert(index, listItemComponent); // Load the data from 'itemData' and apply it to our new list element ApplyViewModelData(element, itemData, dataSource, itemTemplate, list, null, true, true); // apply our attributes (especially necessary for things like event handlers and the like) element.Initialise(currentXmlLayoutInstance, element.rectTransform, element.tagHandler); element.ApplyAttributes(); element.AnimationDuration = currentListElement.itemAnimationDuration; element.ShowAnimation = currentListElement.itemShowAnimation; element.HideAnimation = currentListElement.itemHideAnimation; if (element.ShowAnimation != "None") { element.Show(); } // Rebuild the layout at the end of the frame XmlLayoutTimer.AtEndOfFrame(() => LayoutRebuilder.MarkLayoutForRebuild(element.rectTransform), element); #if UNITY_5_4 // Due to differences in how 5.4 and 5.5 handle layout rebuilds, we sometimes have to manually notify child TableLayouts to update XmlLayoutTimer.DelayedCall(0.05f, () => { var tableLayouts = element.GetComponentsInChildren <UI.Tables.TableLayout>(); foreach (var tableLayout in tableLayouts) { tableLayout.UpdateLayout(); } }, element); #endif }
private void HideThenShow(bool initial = false) { xmlLayout.GetElementById("grid").Hide(); XmlLayoutTimer.DelayedCall(initial ? 0.5f : 3f, () => { xmlLayout.GetElementById("grid").Show(); // repeat XmlLayoutTimer.DelayedCall(3f, () => HideThenShow(false), this); }, this); }
public void AddListItem(IObservableList list, object item, string listName) { var listElement = ListElements[list.guid]; this.SetInstance(listElement.rectTransform, listElement.listElement.xmlLayoutInstance); RenderListItem(item, listElement.DataSource, listElement.itemTemplate, list); XmlLayoutTimer.AtEndOfFrame(() => { UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(listElement.rectTransform); }, listElement); }
public override void Close() { base.Close(); var scrollRect = ((ScrollRect)primaryComponent); var content = scrollRect.content; XmlLayoutTimer.DelayedCall(0.05f, () => { var simpleContentSizeFitter = content.GetComponent <SimpleContentSizeFitter>(); simpleContentSizeFitter.MatchChildDimensions(); }, scrollRect); }
public void UpdateListItem(IObservableList list, int index, object item, string listName, string changedField = null) { var listElement = ListElements[list.guid]; var itemGuid = list.GetGUID(item); var itemElement = listElement.listItems.FirstOrDefault(f => f.guid == itemGuid); this.SetInstance(itemElement.xmlElement.rectTransform, itemElement.xmlElement.xmlLayoutInstance); this.ApplyViewModelData(itemElement.xmlElement, item, listElement.DataSource, listElement.itemTemplate, list, changedField); XmlLayoutTimer.AtEndOfFrame(() => { UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(listElement.rectTransform); }, listElement); }
public override void Apply(XmlElement xmlElement, string value, AttributeDictionary attributes) { var selectable = xmlElement.GetComponent <Selectable>(); if (selectable != null) { // We're delaying here because the element we're referencing may not yet have been parsed // at this point in time XmlLayoutTimer.AtEndOfFrame(() => { var navigation = selectable.navigation; navigation.selectOnDown = FindElement(xmlElement, value); selectable.navigation = navigation; }, xmlElement, true); } }
public override void ApplyAttributes(AttributeDictionary attributesToApply) { base.ApplyAttributes(attributesToApply); var toggleComponent = currentInstanceTransform.GetComponent <Toggle>(); if (attributesToApply.ContainsKey("colors")) { toggleComponent.colors = attributesToApply["colors"].ToColorBlock(currentXmlLayoutInstance); } if (attributesToApply.ContainsKey("ison")) { toggleComponent.isOn = attributesToApply["ison"].ToBoolean(); } if (attributesToApply.ContainsKey("selectedicon")) { var xmlLayoutToggleButton = toggleComponent.GetComponent <XmlLayoutToggleButton>(); xmlLayoutToggleButton.SelectedIconSprite = attributesToApply["selectedicon"].ToSprite(); xmlLayoutToggleButton.DeselectedIconSprite = xmlLayoutToggleButton.IconComponent.sprite; } if (ToggleGroupTagHandler.CurrentToggleGroupInstance != null) { var xmlLayoutToggleGroupInstance = ToggleGroupTagHandler.CurrentToggleGroupInstance; xmlLayoutToggleGroupInstance.AddToggle(toggleComponent); xmlLayoutToggleGroupInstance.UpdateToggleElement(toggleComponent); toggleComponent.onValueChanged.AddListener((e) => { if (e) { var value = xmlLayoutToggleGroupInstance.GetValueForElement(toggleComponent); xmlLayoutToggleGroupInstance.SetSelectedValue(value); } }); } XmlLayoutTimer.AtEndOfFrame(() => { var xmlLayoutToggleButton = toggleComponent.GetComponent <XmlLayoutToggleButton>(); xmlLayoutToggleButton.UpdateDisplay(); }, toggleComponent); }
public void AddListItem(IObservableList list, object item, string listName) { var listElement = ListElements[list.guid]; this.SetInstance(listElement.rectTransform, listElement.listElement.xmlLayoutInstance); RenderListItem(item, listElement.DataSource, listElement.itemTemplate, list); var row = listElement.GetComponent <UI.Tables.TableRow>(); if (row != null) { row.NotifyTableRowPropertiesChanged(); } XmlLayoutTimer.AtEndOfFrame(() => { UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(listElement.rectTransform); }, listElement); }
private void _RemoveListItem(XmlLayoutList list, XmlLayoutListItem item) { if (item.xmlElement != null) { list.listItems.Remove(item); list.listElement.RemoveChildElement(item.xmlElement); } if (Application.isPlaying) { GameObject.Destroy(item.gameObject); } else { GameObject.DestroyImmediate(item.gameObject); } XmlLayoutTimer.AtEndOfFrame(() => { UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(list.rectTransform); }, list); }
public override void ApplyAttributes(AttributeDictionary attributesToApply) { base.ApplyAttributes(attributesToApply); var xmlLayoutButton = currentInstanceTransform.GetComponent <XmlLayoutButton>(); var textComponent = currentInstanceTransform.GetComponentInChildren <Text>(true); bool applyTextAttributesFromButton = true; ColorBlock textColors = new ColorBlock() { normalColor = Color.black, highlightedColor = Color.black, disabledColor = Color.black, pressedColor = Color.black, colorMultiplier = 1 }; if (attributesToApply.ContainsKey("textcolors")) { textColors = attributesToApply["textcolors"].ToColorBlock(currentXmlLayoutInstance); } else if (attributesToApply.ContainsKey("textcolor") || attributesToApply.ContainsKey("deselectedtextcolor")) { // draw the text colors from the textColor attribute var textColor = (attributesToApply.ContainsKey("textcolor") ? attributesToApply["textcolor"] : attributesToApply["deselectedtextcolor"]).ToColor(currentXmlLayoutInstance); SetColorBlockColor(ref textColors, textColor); } if (currentXmlElement.childElements.Count > 0) { var child = currentXmlElement.childElements.First(); if (child.tagType == "Text") { if (child.gameObject != textComponent.gameObject) { // Replace the original text component with the child element child.rectTransform.SetParent(textComponent.rectTransform.parent); if (Application.isPlaying) { GameObject.Destroy(textComponent.gameObject); } else { GameObject.DestroyImmediate(textComponent.gameObject); } textComponent = child.GetComponent <Text>(); } applyTextAttributesFromButton = false; if (child.attributes.ContainsKey("color")) { SetColorBlockColor(ref textColors, textComponent.color); } xmlLayoutButton.TextComponent = new TextComponentWrapper(textComponent); } #if TEXTMESHPRO_PRESENT else if (child.tagType == "TextMeshPro") { if (textComponent != null && child.gameObject != textComponent.gameObject) { // Replace the original text component with the child element child.rectTransform.SetParent(textComponent.rectTransform.parent); if (Application.isPlaying) { GameObject.Destroy(textComponent.gameObject); } else { GameObject.DestroyImmediate(textComponent.gameObject); } } var textMeshPro = child.GetComponent <TextMeshProUGUI>(); applyTextAttributesFromButton = false; if (child.attributes.ContainsKey("color")) { SetColorBlockColor(ref textColors, textMeshPro.color); } xmlLayoutButton.TextComponent = new TextComponentWrapper(textMeshPro); } #endif if (!child.attributes.ContainsKey("text") && attributesToApply.ContainsKey("text")) { child.SetAndApplyAttribute("text", attributesToApply["text"]); } child.rectTransform.localScale = Vector3.one; } else { xmlLayoutButton.TextComponent = new TextComponentWrapper(textComponent); } if (applyTextAttributesFromButton) { var tagHandler = XmlLayoutUtilities.GetXmlTagHandler("Text"); tagHandler.SetInstance(textComponent.rectTransform, this.currentXmlLayoutInstance); var textAttributes = new AttributeDictionary( attributesToApply.Where(a => TextTagHandler.TextAttributes.Contains(a.Key, StringComparer.OrdinalIgnoreCase)) .ToDictionary(a => a.Key, b => b.Value)); if (attributesToApply.ContainsKey("textshadow")) { textAttributes.Add("shadow", attributesToApply["textshadow"]); } if (attributesToApply.ContainsKey("textoutline")) { textAttributes.Add("outline", attributesToApply["textoutline"]); } if (attributesToApply.ContainsKey("textcolor")) { textAttributes.Add("color", attributesToApply["textcolor"]); } if (attributesToApply.ContainsKey("textalignment")) { textAttributes.Add("alignment", attributesToApply["textalignment"]); } tagHandler.ApplyAttributes(textAttributes); } // preserve aspect for button background var imageComponent = currentInstanceTransform.GetComponent <Image>(); if (attributesToApply.ContainsKey("preserveaspect")) { imageComponent.preserveAspect = attributesToApply["preserveaspect"].ToBoolean(); } // Button image if (attributesToApply.ContainsKey("icon") || this.currentXmlElement.attributes.ContainsKey("icon")) { var cell = xmlLayoutButton.IconCell; // position the cell on the left or the right var alignmentParameter = attributesToApply.ContainsKey("iconAlignment") ? attributesToApply["iconAlignment"] : (currentXmlElement.attributes.ContainsKey("iconAlignment") ? currentXmlElement.attributes["iconAlignment"] : "Left"); var imageAlignment = (ButtonIconAlignment)Enum.Parse(typeof(ButtonIconAlignment), alignmentParameter); var buttonImageWidth = attributesToApply.ContainsKey("iconwidth") ? attributesToApply["iconwidth"].ToFloat() : currentXmlElement.attributes.ContainsKey("iconwidth") ? currentXmlElement.attributes["iconwidth"].ToFloat() : 0; xmlLayoutButton.ButtonTableLayout.ColumnWidths = new List <float>() { 0, 0 }; if (imageAlignment == ButtonIconAlignment.Left) { cell.transform.SetAsFirstSibling(); xmlLayoutButton.ButtonTableLayout.ColumnWidths[0] = buttonImageWidth; } else { cell.transform.SetAsLastSibling(); xmlLayoutButton.ButtonTableLayout.ColumnWidths[1] = buttonImageWidth; } xmlLayoutButton.IconComponent.preserveAspect = true; if (attributesToApply.ContainsKey("icon")) { xmlLayoutButton.IconComponent.sprite = attributesToApply["icon"].ToSprite(); } if (attributesToApply.ContainsKey("iconcolor")) { xmlLayoutButton.IconColor = attributesToApply["iconcolor"].ToColor(currentXmlLayoutInstance); } if (attributesToApply.ContainsKey("iconhovercolor")) { xmlLayoutButton.IconHoverColor = attributesToApply["iconhovercolor"].ToColor(currentXmlLayoutInstance); } if (attributesToApply.ContainsKey("icondisabledcolor")) { xmlLayoutButton.IconDisabledColor = attributesToApply["icondisabledcolor"].ToColor(currentXmlLayoutInstance); } if (attributesToApply.ContainsKey("iconimagetype")) { xmlLayoutButton.IconComponent.type = (Image.Type)Enum.Parse(typeof(Image.Type), attributesToApply["iconimagetype"]); } cell.gameObject.SetActive(true); // show or hide the text cell if there is (or is not) any text if ((!attributesToApply.ContainsKey("text") || String.IsNullOrEmpty(attributesToApply["text"])) && !currentXmlElement.attributes.ContainsKey("text")) { xmlLayoutButton.TextCell.gameObject.SetActive(false); } else { xmlLayoutButton.TextCell.gameObject.SetActive(true); } } if (attributesToApply.ContainsKey("padding")) { xmlLayoutButton.ButtonTableLayout.padding = attributesToApply["padding"].ToRectOffset(); } xmlLayoutButton.TextColors = textColors; XmlLayoutTimer.DelayedCall(0, () => { if (xmlLayoutButton.mouseIsOver) { xmlLayoutButton.OnPointerEnter(null); } else { xmlLayoutButton.OnPointerExit(null); } }, xmlLayoutButton); }
public override void ApplyAttributes(AttributeDictionary attributesToApply) { if (currentInstanceTransform == null) { return; } if (currentXmlElement.name == "GameObject" || currentXmlElement.name == "Xml Element") { currentXmlElement.name = "TextMesh Pro"; } var go = currentInstanceTransform.gameObject; //var mt = new MethodTimer("TMP::Get/Add Component"); var tmp = go.GetComponent <TextMeshProUGUI>() ?? go.AddComponent <TextMeshProUGUI>(); //mt.Dispose(); // If we don't add a LayoutElement component, TextMeshPro will use up any layout parameters (as it uses the ILayoutElement interface) // and it doesn't use them in the same way as a LayoutElement so things like, for example, 'flexibleWidth' will not work as expected // by adding a LayoutElement, we ensure that the TMP element responds to layout attributes in the same way as other elements var layoutElement = tmp.GetComponent <LayoutElement>(); if (layoutElement == null) { tmp.gameObject.AddComponent <LayoutElement>(); } if (!attributesToApply.ContainsKey("dontMatchParentDimensions") && !currentXmlElement.HasAttribute("delayedProcessingScheduled")) { MatchParentDimensions(); } // default alignment, as per standard UI text if (!attributesToApply.ContainsKey("alignment") && !currentXmlElement.attributes.ContainsKey("alignment")) { tmp.alignment = TextAlignmentOptions.Center; } // default font size, as per standard UI text if (!attributesToApply.ContainsKey("fontSize") && !currentXmlElement.attributes.ContainsKey("fontSize")) { tmp.fontSize = 14f; } Material fontMaterial = null; if (attributesToApply.ContainsKey("fontMaterial")) { if (currentXmlLayoutInstance.textMeshProMaterials.ContainsKey(attributesToApply["fontMaterial"])) { fontMaterial = currentXmlLayoutInstance.textMeshProMaterials[attributesToApply["fontMaterial"]]; attributesToApply.Remove("fontMaterial"); } } //using (new MethodTimer("TMP::Base::ApplyAttributes")) { base.ApplyAttributes(attributesToApply); } if (fontMaterial != null) { tmp.fontMaterial = fontMaterial; } if (attributesToApply.ContainsKey("colorGradient")) { tmp.enableVertexGradient = true; } if (attributesToApply.ContainsKey("text")) { tmp.text = StringExtensions.DecodeEncodedNonAsciiCharacters(attributesToApply["text"]); } // For some reason, some TMP properties (such as text) // will not accept changes until after some internal TMP setup // has been completed. As such, it is necessary to delay our processing briefly // and then attempt to apply attributes again. // This only needs to be done once, as further ApplyAttributes() calls // will happen after TMP has already completed its setup. if (!currentXmlElement.HasAttribute("delayedProcessingScheduled")) { var attributeDictionaryCopy = attributesToApply; var _currentElement = currentXmlElement; XmlLayoutTimer.AtEndOfFrame(() => { if (_currentElement != null) { if (_currentElement.gameObject.activeInHierarchy) { _currentElement.ApplyAttributes(); } else { _currentElement.m_onEnableEventsOnceOff.Enqueue(() => { _currentElement.ApplyAttributes(attributeDictionaryCopy); }); } } }, _currentElement, true); currentXmlElement.attributes.AddIfKeyNotExists("delayedProcessingScheduled", "true"); } else { // TextMeshPro seems to sometimes ignore changes to the color property; // calling it again sorts this out if (attributesToApply.ContainsKey("color") && !attributesToApply.ContainsKey("delayedColorSet")) { var _currentElement = currentXmlElement; XmlLayoutTimer.AtEndOfFrame(() => { _currentElement.ApplyAttributes(new Dictionary <string, string>() { { "color", attributesToApply["color"] }, { "delayedColorSet", "1" } }); _currentElement.RemoveAttribute("delayedColorSet"); }, _currentElement, true); } } }
public void UpdateDisplay() { // Delaying until the end of the frame allows us to be certain that XmlLayout is completely set up before we execute this code XmlLayoutTimer.AtEndOfFrame(_UpdateDisplay, this); }
public override void Close() { var uiObject3D = primaryComponent as UIObject3D; XmlLayoutTimer.DelayedCall(0.01f, () => uiObject3D.HardUpdateDisplay(), uiObject3D); }
public override void ParseChildElements(System.Xml.XmlNode xmlNode) { var dataSource = currentXmlElement.DataSource; if (String.IsNullOrEmpty(dataSource)) { return; } var xmlLayoutController = currentXmlLayoutInstance.XmlLayoutController; if (xmlLayoutController == null) { return; } var viewModelProperty = xmlLayoutController.GetType().GetProperty("viewModel"); if (viewModelProperty == null) { Debug.LogWarning("[XmlLayout] Warning: Useage of the <List> element requires the XmlLayoutController to have a view model type defined."); return; } var viewModel = viewModelProperty.GetValue(xmlLayoutController, null); var listMember = viewModel.GetType().GetMember(dataSource).FirstOrDefault(); if (listMember == null) { Debug.LogWarning("[XmlLayout] Warning: View Model does not contain a field or property for data source '" + dataSource + "'."); return; } IList list = (IList)listMember.GetMemberValue(viewModel); var observableList = (IObservableList)list; if (list == null || observableList == null) { // no data yet return; } var dataTableComponent = this.primaryComponent as XmlLayoutDataTable; string id = String.Empty; if (currentXmlElement.HasAttribute("id")) { id = currentXmlElement.attributes["id"]; } else { id = observableList.guid; XmlLayoutTimer.AtEndOfFrame(() => { if (currentXmlElement != null) { currentXmlElement.SetAndApplyAttribute("id", id); } }, currentXmlElement, true); } if (dataTableElements.ContainsKey(id)) { dataTableElements[id] = dataTableComponent; } else { dataTableElements.Add(id, dataTableComponent); } }
public override void ParseChildElements(System.Xml.XmlNode xmlNode) { if (String.IsNullOrEmpty(currentXmlElement.DataSource)) { // no data source; no rendering to do here // I've decided not to log anything here, as this could be desired behaviour (a data source could be set later, for example) return; } var dataSource = currentXmlElement.DataSource; var xmlLayoutController = currentXmlLayoutInstance.XmlLayoutController; if (xmlLayoutController == null) { return; } var viewModelProperty = xmlLayoutController.GetType().GetProperty("viewModel"); if (viewModelProperty == null) { Debug.LogWarning("[XmlLayout] Warning: Useage of the <List> element requires the XmlLayoutController to have a view model type defined."); return; } var viewModel = viewModelProperty.GetValue(xmlLayoutController, null); var listProperty = viewModel.GetType().GetProperty(dataSource); var listField = viewModel.GetType().GetField(dataSource); IList list = null; if (listProperty != null) { if (!(listProperty.PropertyType.IsGenericType && listProperty.PropertyType.GetGenericTypeDefinition() == typeof(ObservableList <>))) { Debug.LogWarning("[XmlLayout] Warning: Usage of the <List> element requires a property with a type of ObservableList."); return; } list = (IList)listProperty.GetValue(viewModel, XmlLayoutUtilities.BindingFlags, null, null, null); } else if (listField != null) { if (!(listField.FieldType.IsGenericType && listField.FieldType.GetGenericTypeDefinition() == typeof(ObservableList <>))) { Debug.LogWarning("[XmlLayout] Warning: Usage of the <List> element requires a property with a type of ObservableList."); return; } list = (IList)listField.GetValue(viewModel); } else { Debug.LogWarning("[XmlLayout] Warning: View Model does not contain a field or property for data source '" + dataSource + "'."); return; } var observableList = (IObservableList)list; if (list == null || observableList == null) { // no data yet return; } var parent = transformToAddChildrenTo.GetComponent <XmlElement>(); if (!parent.attributes.ContainsKey("id")) { XmlLayoutTimer.AtEndOfFrame(() => parent.SetAndApplyAttribute("id", observableList.guid), parent, true); } var itemTemplate = GetItemTemplate(xmlNode.InnerXml); var xmlLayoutListComponent = parent.GetComponent <XmlLayoutList>(); if (xmlLayoutListComponent == null) { xmlLayoutListComponent = parent.gameObject.AddComponent <XmlLayoutList>(); } xmlLayoutListComponent.itemTemplate = itemTemplate; xmlLayoutListComponent.DataSource = dataSource; xmlLayoutListComponent.baseSiblingIndex = currentXmlElement.transform.GetSiblingIndex(); xmlLayoutListComponent.list = observableList; xmlLayoutListComponent.isCalculatedList = !listProperty.IsAutoProperty(); xmlLayoutListComponent.itemAnimationDuration = currentXmlElement.attributes.ContainsKey("itemAnimationDuration") ? currentXmlElement.attributes.GetValue <float>("itemAnimationDuration") : 0.25f; xmlLayoutListComponent.itemShowAnimation = currentXmlElement.attributes.ContainsKey("itemShowAnimation") ? currentXmlElement.attributes.GetValue <string>("itemShowAnimation") : "None"; xmlLayoutListComponent.itemHideAnimation = currentXmlElement.attributes.ContainsKey("itemHideAnimation") ? currentXmlElement.attributes.GetValue <string>("itemHideAnimation") : "None"; currentListElement = xmlLayoutListComponent; if (ListElements.ContainsKey(observableList.guid)) { ListElements[observableList.guid] = xmlLayoutListComponent; } else { ListElements.Add(observableList.guid, xmlLayoutListComponent); } // Render the list items as per the view model for (var x = 0; x < list.Count; x++) { RenderListItem(list[x], dataSource, itemTemplate, observableList); } }