/// <summary> /// Sets view values parsed from XUML. /// </summary> private static void SetViewValues(View view, XElement xumlElement, View parent, ValueConverterContext context) { if (view == null) { return; } var viewTypeData = GetViewTypeData(view.ViewTypeName); foreach (var attribute in xumlElement.Attributes()) { string viewFieldPath = attribute.Name.LocalName; string viewFieldValue = attribute.Value; // ignore namespace specification if (String.Equals(viewFieldPath, "xmlns", StringComparison.OrdinalIgnoreCase)) { continue; } // check if the field value is allowed to be be set from xuml bool notAllowed = viewTypeData.FieldsNotSetFromXuml.Contains(viewFieldPath); if (notAllowed) { Utils.LogError("[MarkLight] {0}: Unable to assign value \"{1}\" to view field \"{2}.{3}\". Field not allowed to be set from XUML.", view.GameObjectName, viewFieldValue, view.ViewTypeName, viewFieldPath); continue; } // check if value contains a binding if (ViewFieldBinding.ValueHasBindings(viewFieldValue)) { view.AddBinding(viewFieldPath, viewFieldValue); continue; } // check if we are setting a state-value int stateIndex = viewFieldPath.IndexOf('-', 0); if (stateIndex > 0) { // check if we are setting a sub-state, i.e. the state of the target view var stateViewField = viewFieldPath.Substring(stateIndex + 1); var state = viewFieldPath.Substring(0, stateIndex); bool isSubState = stateViewField.StartsWith("-"); if (isSubState) { stateViewField = stateViewField.Substring(1); } // setting the state of the source view view.AddStateValue(state, stateViewField, attribute.Value, context, isSubState); continue; } // get view field data var viewFieldData = view.GetViewFieldData(viewFieldPath); if (viewFieldData == null) { Utils.LogError("[MarkLight] {0}: Unable to assign value \"{1}\" to view field \"{2}\". View field not found.", view.GameObjectName, viewFieldValue, viewFieldPath); continue; } // check if we are setting a view action handler if (viewFieldData.ViewFieldTypeName == "ViewAction") { viewFieldData.SourceView.AddViewActionEntry(viewFieldData.ViewFieldPath, viewFieldValue, parent); continue; } // we are setting a normal view field view.SetValue(attribute.Name.LocalName, attribute.Value, true, null, context, true); } }
/// <summary> /// Creates view of specified type. /// </summary> public static View CreateView(string viewName, View layoutParent, View parent, ValueConverterContext context = null, string theme = "", string id = "", string style = "", IEnumerable <XElement> contentXuml = null) { // Creates the views in the following order: // CreateView(view) // Foreach child // CreateView(child) // SetViewValues(child) // Foreach contentView // CreateView(contentView) // SetViewValues(contentView) // SetViewValues(view) // SetThemeValues(view) // TODO store away and re-use view templates // use default theme if no theme is specified if (String.IsNullOrEmpty(theme)) { theme = ViewPresenter.Instance.DefaultTheme; } // initialize value converter context if (context == null) { context = ValueConverterContext.Default; } // create view from XUML var viewTypeData = GetViewTypeData(viewName); if (viewTypeData == null) { return(null); } // get view type var viewType = GetViewType(viewName); if (viewType == null) { viewType = typeof(View); } // create view game object with required components var go = new GameObject(viewTypeData.ViewName); if (typeof(UIView).IsAssignableFrom(viewType)) { go.AddComponent <RectTransform>(); } go.transform.SetParent(layoutParent.transform, false); // create view behavior and initialize it var view = go.AddComponent(viewType) as View; view.LayoutParent = layoutParent; view.Parent = parent; view.Id = id; view.Style = style; view.Theme = theme; view.Content = view; view.ViewXumlName = viewName; view.ValueConverterContext = context; // set component fields foreach (var componentField in viewTypeData.ComponentFields) { if (viewTypeData.ExcludedComponentFields.Contains(componentField)) { continue; // exclude component } var componentFieldInfo = viewType.GetField(componentField); Component component = null; if (componentField == "Transform") { component = go.transform; } else if (componentField == "RectTransform") { component = go.transform as RectTransform; } else { component = go.AddComponent(componentFieldInfo.FieldType); } componentFieldInfo.SetValue(view, component); } // set view action fields foreach (var viewActionField in viewTypeData.ViewActionFields) { var viewActionFieldInfo = viewTypeData.GetViewField(viewActionField); viewActionFieldInfo.SetValue(view, new ViewAction(viewActionField)); } // set dependency fields foreach (var dependencyField in viewTypeData.DependencyFields) { var dependencyFieldInfo = viewTypeData.GetViewField(dependencyField); var dependencyFieldInstance = TypeHelper.CreateViewField(dependencyFieldInfo.FieldType); dependencyFieldInfo.SetValue(view, dependencyFieldInstance); dependencyFieldInstance.ParentView = view; dependencyFieldInstance.ViewFieldPath = dependencyField; dependencyFieldInstance.IsMapped = !String.Equals(viewTypeData.GetMappedViewField(dependencyField), dependencyField); } // parse child XUML and for each child create views and set their values foreach (var childElement in viewTypeData.XumlElement.Elements()) { var childViewIdAttr = childElement.Attribute("Id"); var childViewStyleAttr = childElement.Attribute("Style"); var childThemeAttr = childElement.Attribute("Theme"); var childContext = GetValueConverterContext(context, childElement, view.GameObjectName); var childView = CreateView(childElement.Name.LocalName, view, view, childContext, childThemeAttr != null ? childThemeAttr.Value : theme, childViewIdAttr != null ? childViewIdAttr.Value : String.Empty, GetChildViewStyle(view.Style, childViewStyleAttr), childElement.Elements()); SetViewValues(childView, childElement, view, childContext); } // search for a content placeholder ContentPlaceholder contentContainer = view.Find <ContentPlaceholder>(true, view); var contentLayoutParent = view; if (contentContainer != null) { contentLayoutParent = contentContainer.LayoutParent; view.Content = contentLayoutParent; // remove placeholder GameObject.DestroyImmediate(contentContainer.gameObject); } // parse content XUML and for each content child create views and set their values if (contentXuml != null) { // create content views foreach (var contentElement in contentXuml) { var contentElementIdAttr = contentElement.Attribute("Id"); var contentElementStyleAttr = contentElement.Attribute("Style"); var contentThemeAttr = contentElement.Attribute("Theme"); var contentContext = GetValueConverterContext(context, contentElement, view.GameObjectName); var contentView = CreateView(contentElement.Name.LocalName, contentLayoutParent, parent, contentContext, contentThemeAttr != null ? contentThemeAttr.Value : theme, contentElementIdAttr != null ? contentElementIdAttr.Value : String.Empty, GetChildViewStyle(view.Style, contentElementStyleAttr), contentElement.Elements()); SetViewValues(contentView, contentElement, parent, contentContext); } } // set view references foreach (var referenceField in viewTypeData.ReferenceFields) { // is this a reference to a view? var referencedView = view.Find <View>(x => String.Equals(x.Id, referenceField, StringComparison.OrdinalIgnoreCase), true, view); if (referencedView != null) { var referenceFieldInfo = viewType.GetField(referenceField); referenceFieldInfo.SetValue(view, referencedView); } } // set view default values view.SetDefaultValues(); // set internal view values that appear inside the root element of the XUML file SetViewValues(view, viewTypeData.XumlElement, view, context); // set theme values var themeData = GetThemeData(theme); if (themeData != null) { foreach (var themeElement in themeData.GetThemeElementData(view.ViewTypeName, view.Id, view.Style)) { var themeValueContext = new ValueConverterContext(context); if (themeData.BaseDirectorySet) { themeValueContext.BaseDirectory = themeData.BaseDirectory; } if (themeData.UnitSizeSet) { themeValueContext.UnitSize = themeData.UnitSize; } SetViewValues(view, themeElement.XumlElement, view, themeValueContext); } } return(view); }
/// <summary> /// Sets value of field. /// </summary> public object SetValue(object inValue, HashSet <ViewFieldData> callstack, bool updateDefaultState = true, ValueConverterContext context = null, bool notifyObservers = true) { if (callstack.Contains(this)) { return(null); } callstack.Add(this); if (!IsOwner) { var targetView = GetTargetView(); if (targetView == null) { Debug.LogError(String.Format("[MarkLight] {0}: Unable to assign value \"{1}\" to view field \"{2}\". View along path is null.", SourceView.GameObjectName, inValue, ViewFieldPath)); return(null); } return(targetView.SetValue(TargetViewFieldPath, inValue, updateDefaultState, callstack, null, notifyObservers)); } // check if path has been parsed if (!IsPathParsed) { // attempt to parse path if (!ParseViewFieldPath()) { // path can't be resolved at this point if (SevereParseError) { // severe parse error means the path is incorrect Debug.LogError(String.Format("[MarkLight] {0}: Unable to assign value \"{1}\". {2}", SourceView.GameObjectName, inValue, Utils.ErrorMessage)); } // unsevere parse errors can be expected, e.g. value along path is null return(null); } } object value = inValue; if (context == null) { if (SourceView.ValueConverterContext != null) { context = SourceView.ValueConverterContext; } else { context = ValueConverterContext.Default; } } // get converted value if (ValueConverter != null) { var conversionResult = ValueConverter.Convert(value, context); if (!conversionResult.Success) { Debug.LogError(String.Format("[MarkLight] {0}: Unable to assign value \"{1}\" to view field \"{2}\". Value converion failed. {3}", SourceView.GameObjectName, value, ViewFieldPath, conversionResult.ErrorMessage)); return(null); } value = conversionResult.ConvertedValue; } // set value object oldValue = ViewFieldPathInfo.SetValue(SourceView, value); // notify observers if the value has changed if (notifyObservers) { // set isSet-indicator SetIsSet(); bool valueChanged = value != null ? !value.Equals(oldValue) : oldValue != null; if (valueChanged) { NotifyValueObservers(callstack); // find dependent view fields and notify their value observers SourceView.NotifyDependentValueObservers(ViewFieldPath); } } return(value); }
/// <summary> /// Creates view of specified type. /// </summary> public static T CreateView <T>(View layoutParent, View parent, ValueConverterContext context = null, string themeName = "", string id = "", string style = "", IEnumerable <XElement> contentXuml = null) where T : View { Type viewType = typeof(T); return(CreateView(viewType.Name, layoutParent, parent, context, themeName, id, style, contentXuml) as T); }
/// <summary> /// Converts XUML attribute to a view value. /// </summary> public virtual ConversionResult Convert(object value, ValueConverterContext context) { return(new ConversionResult(value)); }