Search Unity

NData - MVVM framework for NGUI

Discussion in 'Assets and Asset Store' started by Art Of Bytes, Mar 15, 2012.

  1. Nyxo

    Nyxo

    Joined:
    Mar 12, 2013
    Posts:
    23
    Hi there,
    I'm still encountering situations where a databinding isn't firing off events immediately, and instead waiting until the next update where it will performs a check and responds at that point.

    A very simple example would be having an NguiItemsSourceBinding bound to the following:
    Code (csharp):
    1.  
    2. Collection myCollection;
    3.  
    4. private void OnClick()
    5. {
    6.   myCollection.Clear();
    7.   RefreshCollection(myCollection);
    8.   myCollection.SelectItem(1);
    9. }
    10.  
    What ends up happening when OnClick is called, is that after the collection is cleared and refreshed, the NguiItemsSourceBinding is in a "reset" state (I think it's reinstantiated?) where it isn't being notified about selection changes, so when the 3rd line is called to select something, the UI isn't updated to reflect that because NguiItemsSourceBinding hasn't attached it's methods to the OnSelectionChanged event yet (this is done in it's UpdateBinding method). I had gotten around this by manually calling NguiItemsSourceBinding.UpdateBinding(), but then I encountered the next problem:

    On the next Update call, the NguiItemsSourceBinding's Start() method is called, which in turn calls it's UpdateBinding method, which discards the selected item I had previously set! I know this isn't the best solution, but what I did to get around this was that I made NguiItemsSourceBinding.Start check for an "updated" flag before calling UpdateBinding, and a call to UpdateBinding SETS that flag. That way if UpdateBinding is called before Start, Start won't call UpdateBinding a 2nd time, blowing away any changes made in the meantime. That's a bit of a dirty workaround tho, so I'd really appreciate if something more clean/official was put in place to use instead.
     
  2. Nyxo

    Nyxo

    Joined:
    Mar 12, 2013
    Posts:
    23
    So, in a recent release, you fixed the nested templates issue (where a prefab is referencing itself, to allow a recursive hierarchy), and I switched my code over to use that, and I'm pretty sure (altho not 100%) that it was work at that point. However, I was recently testing something unconnected, and saw that the recursive hierarchy was no longer working. My workaround was to go back to having 2 duplicate prefabs, where the first references the second, and the second references the first.

    Looking at the code, I can see why it's not working, so hopefully you can fix it:
    In NguiItemsSourceBinding::OnItemInsert(), You first instantiate a GameObject from the template, then you check if the subtemplates of the new GameObject are referencing themselves and correct the reference as necessary.

    However, it's too late to be doing that check there, and here's why:
    When you call the template's Instantiate() method, the new GameObject is created, and the context is set before the Instantiate() method returns. This is important because during the setting of the context, all of the child GameObjects are being Instantiated using the not-corrected-yet Template. By the time the template correction code is being executed, the whole tree has already been Instantiated using the incorrect Templates.
     
  3. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Hi, I recently started using NData and I'm new to MVVM in general. I had a couple of style questions; the code below works but I'm not sure if it's really the "right" way to do things.

    First, I implemented a new Tooltip binding so that I could put the tooltip text into the ViewModel; is this the correct way to implement a custom Binding?

    Code (csharp):
    1. public class TooltipBinding : NguiBinding
    2. {
    3.     public string Format = "{0}";
    4.  
    5.     private EZData.Property<string> prop;
    6.  
    7.     public override void Start()
    8.     {
    9.         base.Start();
    10.         prop = GetContext(Path).FindProperty<string>(Path, this);
    11.     }
    12.  
    13.     void OnTooltip(bool show)
    14.     {
    15.         if (show)
    16.         {
    17.             var text = prop.GetValue();
    18.             UITooltip.ShowText(string.Format(Format, text));
    19.         }
    20.         else
    21.         {
    22.             UITooltip.ShowText(null);
    23.         }
    24.     }
    25. }
    Second, I have a StorageContainer that represents chests and things full of items in the game, and a UI for containers and now a ViewModel class to show the list of items. The ViewModel has an EZData.Collection of simplified item objects that get their values from the backing actual item class. Currently I manually refresh the ViewModel's Collection whenever the backing game item list changes, like this:

    Code (csharp):
    1. public class ContainerWindow : MonoBehaviour
    2. {
    3.     public StorageContainer container;
    4.     private InventoryViewModel ui;
    5.  
    6.     void Start()
    7.     {
    8.         ui = UI.Root.Inventory;
    9.         ui.container = container;
    10.     }
    11.  
    12.     void OnEnable()
    13.     {
    14.         container.hasChanged = true;
    15.     }
    16.  
    17.     void Update()
    18.     {
    19.         if (container.hasChanged)   // <--- gets set to true in the StorageContainer class when the contents change
    20.         {
    21.             container.hasChanged = false;
    22.             ui.RefreshList();
    23.         }
    24.     }
    25. }
    26.  
    27. //separate file...
    28. public class NewItemRow : EZData.Context
    29. {
    30.     Property string Name
    31.     Property int Amount
    32.     Property string TooltipText
    33.    
    34.     private BaseItem baseItem;
    35.  
    36.     public NewItemRow(BaseItem item, int count)
    37.     {
    38.         baseItem = item;
    39.         Name = item.itemName;
    40.         Amount = count;
    41.         TooltipText = item.GetTooltipText();
    42.     }    
    43. }
    44.  
    45. public class InventoryViewModel : EZData.Context
    46. {
    47.     Collection NewItemRow ItemList
    48.  
    49.     public StorageContainer container;
    50.  
    51.     public void RefreshList()
    52.     {
    53.         ItemList.Clear();
    54.         foreach (var item in container.Items)
    55.         {
    56.             ItemList.Add(new NewItemRow(item, container.CountOf(item)));
    57.         }
    58.     }
    59. }
    60.  
    Is this the right way to do this, or is there a better way to just automatically have the EZData.Collection stay in sync with the backing StorageContainer.Items list?
     
    Last edited: May 3, 2013
  4. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
  5. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198

    This binding will work, but it's not 100% correct. It doesn't handle context change right now. When context switch occurs, you should unbind from properties and repeat the action from your Start method.
    Proper way would be to override Unbind and UpdateBinding methods rather than start. Also, in case you want to be notified about the property value change, you might want to register OnChange handler (not needed in your case, but might be useful for other bindings):

    Code (csharp):
    1.  
    2.     public class TooltipBinding : NguiBinding
    3.     {
    4.         public string Format = "{0}";
    5.    
    6.         private EZData.Property<string> prop;
    7.    
    8.         public override void Unbind()
    9.         {
    10.           if (prop != null)
    11.             prop.OnChange -= OnChange;
    12.           prop = null;
    13.         }
    14.  
    15.         public override void UpdateBinding()
    16.         {
    17.             Unbind();
    18.             prop = GetContext(Path).FindProperty<string>(Path, this);
    19.             if (prop != null)
    20.               prop.OnChange += OnChange;
    21.         }
    22.  
    23.         void OnChange()
    24.         {
    25.         }  
    26.  
    27.         void OnTooltip(bool show)
    28.         {
    29.             if (show)
    30.             {
    31.                 var text = prop.GetValue();
    32.                 UITooltip.ShowText(string.Format(Format, text));
    33.             }
    34.             else
    35.             {
    36.                 UITooltip.ShowText(null);
    37.             }
    38.         }
    39.     }
    40.  


    That's pretty much a valid way of transferring data to viewmodel.Some other approaches are:
    - having an event in backing container that is triggered on change, and subscribe a viewmodel to it, so viewmodel can grab the data (somewhat similar to your solution).
    - modifying viewmodel content from the backing container directly when changes occur.
    - merging backing container with viewmodel collection and store the full items instead of simplified item objects in a single container.

    Feel free to use any solution you find appropriate.
     
  6. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Thanks for the help! I'm starting to get it now... last night I actually did change it to give the backing container an event for the view model to subscribe to, so I'm glad I was thinking correctly. ;) I really like your framework so far; it's made my GUI code much cleaner and more reusable!
     
  7. Enoch

    Enoch

    Joined:
    Mar 19, 2013
    Posts:
    198
    Hi,

    A couple of months ago I wrote a UI for a project that used NData. I am refactoring some UI work I did early with NData and an I hoping a for a more elegant officially NData supported solution than what I initially wrote. I initially needed to add a support for something I called a MasterPath. The idea being that I wanted the Path Property to be relative to a given NData ViewModel. This way I could build a Prefab with NData bindings all specifying a Local Path and reuse this Prefab in a larger NData Viewmodel hierarchy without having to change each individual Path entry. If the boolean flag was set on a NData Binding monobehavior It would then traverse the parent hierarchy until it found a Component I called NGuiMasterPath, which was a simple string with the rest of the Path information. It would then simply append that Path information to the beginning of any NguiBinding (or derivative) that had the flag checked to UseMasterPath.

    The whole idea being that I could setup up the bindings in the Prefab to have paths that were all relative to a single NGuiMasterPath component that I attached to the very Top/Root of the Prefab. At runtime the child paths were all changed to the proper absolute path within a ViewModel structure. The problem is that I had to make changes to a nearly every NGuiBinding class to use this. It makes keeping up with new NData versions a real pain.

    Is there a better way to accomplish this now? I reuse prebuilt prefabs a lot (think of a Vector3 where the bindings for X,Y,Z were all specified locally by setting the path to X,Y and Z respectively and at the Top of the prefab I set the rest of Path IE- Position or Rotation or Scale, etc). Is there someway official way to force that Path string to be relative instead of absolute? Thanks for any help you can give.

    Enoch
     
  8. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Hi, actually this solution with optional MasterPath component that is added as a prefix to all bindings down the hierarchy looks good, most likely it'll be included with the next update. However, there's a single little thing about it - the master paths can only be defined statically, and if somehow it will change during the run-time, newly instantiated items in its sub-hierarchy will receive a new prefix, that will cause an inconsistency. Did you encounter a need in changing the master path in a run-time? And how do you think, does it make sense to provide a correct way of changing it (by forced update of all bindings in sub-hierarchy for example)?

    Oh, also, since one of the recent versions, paths are resolved in a slightly different way, so if you want to tweak paths resolution, there's no need to modify every binding, but only base classes: NguiBinding and NguiMultibinding. See a routine called GetContext(string path); in those classes.
     
  9. Enoch

    Enoch

    Joined:
    Mar 19, 2013
    Posts:
    198
    I never had a need to have the MasterPath change at runtime, so no I never considered it. I am trying to imagine a use-case but I can't come up with anything off the top of my head. I did as a precaution save off the original path in case UpdateBinding (which is where I included the code to append the MasterPath) was called again, it wouldn't append the path again on top of the already appended path.

    Here are the changes I made to NguiBinding, your welcome to use all or none or as much as you'd like. It's from an earlier version so I don't know how much is different from then and now, I doubt too much though.

    Code (csharp):
    1.  
    2. public class NguiBinding : MonoBehaviour, EZData.IBinding
    3. {
    4.     public string Path;
    5.    
    6.     public IList<string> ReferencedPaths { get { return new List<string> { Path }; } }
    7.    
    8.     private Dictionary<int, NguiDataContext> _contexts = new Dictionary<int, NguiDataContext>();
    9.  
    10.     //Master Path specific code
    11.     protected string MasterPath;
    12.     protected string OriginalPath;
    13.     public bool UseMasterPath = false;
    14.     protected void UpdateMasterPath()
    15.     {        
    16.         if (UseMasterPath)
    17.         {
    18.             if (string.IsNullOrEmpty(OriginalPath))
    19.             {
    20.                 OriginalPath = Path;
    21.             }
    22.             else Path = OriginalPath;
    23.             GameObject g = gameObject;
    24.             string mpath = "";
    25.             NguiMasterPath masterPath = g.GetComponent<NguiMasterPath>();
    26.             while (g.transform.parent != null  masterPath == null)
    27.             {
    28.                 g = g.transform.parent.gameObject;
    29.                 masterPath = g.GetComponent<NguiMasterPath>();
    30.                 if (masterPath != null)
    31.                 {
    32.                     if (String.IsNullOrEmpty(mpath)) mpath = masterPath.MasterPath;
    33.                     else mpath = masterPath.MasterPath+"."+mpath;
    34.                     if (masterPath.UseParentMaster)
    35.                     {
    36.                         masterPath = null;
    37.                     }
    38.                 }
    39.             }
    40.             if (masterPath != null)
    41.             {
    42.                 MasterPath = mpath;
    43.                 if (MasterPath != null)
    44.                 {
    45.                     Path = MasterPath + "." + Path;
    46.                 }
    47.             }
    48.         }
    49.     }
    50.  
    51.     protected string AppendMasterPath(string path)
    52.     {
    53.         if (UseMasterPath)
    54.         {
    55.             if (!String.IsNullOrEmpty(MasterPath)) return MasterPath + "." + path;
    56.         }
    57.         return path;
    58.     }
    59.     public virtual void UpdateBinding()
    60.     {
    61.         UpdateMasterPath();
    62.     }
    63. ....
    64.  
    The NGuiMastPath Component that you placed at the top of the local hierarchy looked like this:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. [System.Serializable]
    5. [AddComponentMenu("NGUI/NData/MasterPath")]
    6. public class NguiMasterPath : MonoBehaviour
    7. {
    8.     public string MasterPath;
    9.     public bool UseParentMaster = false;
    10. }
    11.  
    I think the reason I needed to update a lot of the child classes of NguiBindings is because not many of them called base.UpdateBinding() in there overridden UpdateBinding() methods.

    Again feel free to use as much as you'd like or none at all. Thanks for the help. If you come up with something similar to this I won't have to keep updating after every code drop and that will make me happy :).
     
  10. Simie

    Simie

    Joined:
    Oct 26, 2012
    Posts:
    456
    Is there a way of using a binding for the Min/Max values of the slider binding? (MaxHealth, for example)

    Also, the sprite fill amount binding needs to be patched to the latest version of NGUI (All sprite types are now combined into one behaviour class)

    Great plugin!
     
  11. Nyxo

    Nyxo

    Joined:
    Mar 12, 2013
    Posts:
    23
    Is it possible to have a CommandBinding that passes the clicked-on GameObject (most likely a Button...) to the method that is called?

    ie. Say I have a Button with a CommandBinding on it, and the Path is set to "OnCommand". Inside the OnCommand() method, I'd like to receive the Button that was clicked on, so the signature would look like this:
    Code (csharp):
    1. private void OnCommand(GameObject sender);
    The reason being, that I need the Command Binding because I need access to the Context within the OnCommand() method, but I also need access to some Components that are on the button that is being clicked.

    Is something like this possible with the current framework? Or can I request the feature?
     
  12. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Hi guys, please welcome new NData update. 1.0.14 this time, here's the new stuff list:

    Note: Some very basic mechanism re-factoring was applied, so if you have your own custom bindings, you'll have to revise your code. Now instead of overriding Unbind/UpdateBinding you have to override Unbind/Bind methods pair (you can refer to NguiCustomBoundsNumericBinding.cs on how this override looks)

    Also:
    2 Simie: Min / Max paths is supported for slider binding. Also fill amount binding is corrected, thanks for the hint.
    2 Nyxo: Items source binding should work as expected, please let us know if there are more problems with it.
    2 Enoch: MasterPath is there, should be what you need.

    And of course, good news for everybody, more examples.
     
  13. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Regarding this one - commands by design don't have parameters. And usually when you handle a command, you should not have access to the view layer. However, if you need to know the game object that triggered command, you can try something like that:
    Code (csharp):
    1.  
    2. public class NguiOnClickBindingWithSource : NguiCommandBinding
    3. {
    4.     public string SourcePath;
    5.     private Property<GameObject> _source;
    6.  
    7.     protected override void Unbind()
    8.     {
    9.         base.Unbind();
    10.         _source = null;
    11.     }
    12.  
    13.     protected override void Bind()
    14.     {
    15.         base.Bind();
    16.         var context = GetContext(SourcePath);
    17.         if (context != null)
    18.             _source = context.FindProperty<string>(SourcePath, this);
    19.     }
    20.  
    21.     void OnClick()
    22.     {
    23.         _source.SetValue(gameObject);
    24.         if (_command != null)
    25.             _command.DynamicInvoke();
    26.     }
    27. }
    28.  
    This way you'll be able to use one of the properties in your context as a command argument. But, since you mentioned, that you need access to the components in a gameObject that triggered command, we'd recommend to check, maybe then it's possible to handle the situation in a custom UI component completely on a View layer without notifying the context. That would be some analog of code-behind in WPF.
     
  14. matiasjaure

    matiasjaure

    Joined:
    May 27, 2011
    Posts:
    39
    Hi Guys! I am working on a Windows metro project and using your plugin, We found lots of trouble because of all the changes made to Reflection in new .NET. Are you planning a version anytime soon?
     
  15. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Hi, no new version was planned for near future. However, in terms of Flash support investigation we are working on the alternative "reflectionless" way of resolving bindings. It could be helpful for other platforms too, but that's in deep research right now, and it's too soon to say anything for sure.

    Anyways what are your build settings and player settings? Maybe some test project, so we can reproduce the problem. We're really using only couple of very basic routines, like enumerating properties and getting their values. So we'd like to investigate this issues you have.

    Thanks for feedback.
     
  16. matiasjaure

    matiasjaure

    Joined:
    May 27, 2011
    Posts:
    39

    Yes, I am working on changin those reflection things you have used to the new framework. Problems are at NData/Core/Context.cs, I will send a version with new methods, if you can check it would be great.
     
  17. Arthur-Pai

    Arthur-Pai

    Joined:
    May 29, 2012
    Posts:
    6
    Hi, I bought this plugin, its great.
    I have project need nested table, i am used NData to implement, now.
    but i had two problem :
    1. my table item need expand and collapse sub table, but UITable is not reposition when it collapse, how to reach this function?
    2. Is it possible have multi list item template for item source binding?

    thank you.
     
  18. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Hi, sorry for late reply. Seems like there will be one more fix in items source binding for editing deeply nested tables. Could you please check if the attached binding version fixes the problem?
    As for the second request, could you please describe a use-case for that, for it is unclear what exactly you are trying to achieve?
    Thanks for your feedback.
     

    Attached Files:

  19. aikitect

    aikitect

    Joined:
    Dec 28, 2012
    Posts:
    28
    I read through this thread and saw that you made fixes to visibility bindings, but I am still having issues. Below is the hierarchy of the UI. "Step Two - Character Select" contains a Visibility Binding which depends on the ChildSetup.StepNumber.

    $hierarchy.png

    $inspector.png

    When I initialize the scene, the StepNumber is un-initialized, and in my Start() function in the game logic, I do the following:

    ViewModel.Root.ChildSetup.StepNumber++;

    That increments the StepNumber to 1. Now, the problem is, one UISprite that is a child of the "Step Two - Character Select" renders on screen even though the StepNumber is 1 and not 2. This visibility problem occurs in each one of my scenes, but ONLY in iOS. It works perfectly in the Editor. Do you have any idea why this might be happening? I only have one UIPanel in the entire scene.

    EDIT: Did some additional investigation and it turns out that the UISprite that is visible even when it's not supposed to be is always the background of a NGUI Image Button. Not sure if this is a coincidence or not. I am using NGUI 2.6.1e and Unity 4.1.3 and the latest version of NData.

    EDIT 2: Just confirmed that the problem is related to the NGUI Image Button. If I remove the Image Button component, the visibility works just fine. However, I'd like to be able to use the Image Button, so would you be able to suggest modifications to the source code that would fix this issue?

    EDIT 3: Isolated the issue to the MakePixelPerfect() call in NGUI Image Button's UpdateImage() function. If I comment it out, it fixes the issue. Do you have any idea why this happens, and if it is a NData or NGUI bug?
     
    Last edited: Jun 5, 2013
  20. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Hi, thanks for very detailed use-case description. It seems like one minor modification in NguiUtils.cs can help. If you change the function void SetNguiVisible(GameObject gameObject, bool visible) to the following, it should disable Image Button components in invisible objects and prevent them from affecting the sprite:
    Code (csharp):
    1.  
    2.         private static void SetNguiVisible(GameObject gameObject, bool visible)
    3.     {
    4.         foreach(var collider in gameObject.GetComponents<Collider>())
    5.         {
    6.             collider.enabled = visible;
    7.         }
    8.        
    9.         foreach(var widget in gameObject.GetComponents<UIWidget>())
    10.         {
    11.             widget.enabled = visible;
    12.         }
    13.        
    14.                 // Modification start
    15.         foreach(var button in gameObject.GetComponents<UIImageButton>())
    16.         {
    17.             button.enabled = visible;
    18.         }
    19.     }
    20.  
    If that works, just keep this modification till the next NData update, where it will be included.
     
  21. aikitect

    aikitect

    Joined:
    Dec 28, 2012
    Posts:
    28
    I actually tried that prior to you posting it, and it doesn't quite work. I substituted the UIImageButton with a UIButton and it worked, and the key difference between the two scripts is that the UIImageButton calls MakePixelPerfect(). I'm not very informed on what MakePixelPerfect() does so I don't know why it is causing the problem. Any ideas?
     
  22. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    MakePixelPerfect perfect changes local position and scale in such a way that one pixel from sprite texture is placed at exactly one pixel of a screen, so that no filtering is applied for this texture and it is crisp and looks to user exactly like designed by artist. It is however strange that disabling UIImageButton doesn't prevent it from calling MakePixelPerfect. Anyways, we'll take a closer look at this problem. Oh, also, if you could provide some test scene with this bug reproduced - it would significantly speed up the investigation.
     
  23. pxq

    pxq

    Joined:
    Jun 11, 2013
    Posts:
    2
    I think there is a bug in NguiTextBinding.

    I tried to print a log whenever the UILabel has changed the value.

    In NguiTextBinding.cs, I've edited the ApplyNewValue function to print out the logs

    Code (csharp):
    1.  
    2. protected virtual void ApplyNewValue(string newValue)
    3.     {  
    4.         if (_UiInputReceiver != null)
    5.         {
    6.             _UiInputReceiver.text = newValue;
    7.         }
    8.        
    9.         if (_UiLabelReceiver != null)
    10.         {
    11.             _UiLabelReceiver.text = newValue;
    12.             Debug.Log("ApplyNewValue was trigger with value " + newValue);
    13.         }
    14.     }
    15.  
    The logs were printed twice when a new value is applied. Is it a bug?
     
  24. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Hi, we checked it, seems like it's triggered only once. But if you'll have two labels bound to the same value it'll be printed once for each of them. Also if you make more than one change to the property it'll be printed every time value changes, like:
    Code (csharp):
    1.  
    2. MyValue = "My"; // will trigger a first change
    3. MyValue += "Value"; // will trigger a second change
    4.  
    You can check the attached example as a reference.
     

    Attached Files:

  25. pxq

    pxq

    Joined:
    Jun 11, 2013
    Posts:
    2
    I used ItemList in Ndata examples for testing this case. It still printed twice when an item was clicked.

    Does ItemListBinding affect to textbinding in this case?
     
  26. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    All bindings located within item templates will be updated not only when the bound value is changed, but also when the whole item is changed or initialized. In some use-cases, like when you add the item and then set the properties' values, ApplyNewValue can be called twice, first time on item adding to the list and then on its inside properties change. It's not critical, but if that's the case and you still want to avoid it, you can create an item object with all properties initialized and then add it to the list. That way ApplyNewValue will be called once for each binding.
     
  27. Simie

    Simie

    Joined:
    Oct 26, 2012
    Posts:
    456
    I'm having some trouble with NData on iOS, I'm getting a lot of errors like this:

    Code (csharp):
    1. Failed to find property SelectedShip.Position
    2. System.ExecutionEngineException: Attempting to JIT compile method '(wrapper delegate-invoke) System.Reflection.MonoProperty/Getter`2<EZData.VariableContext`1<ShipContext>, ShipContext>:invoke_ShipContext__this___VariableContext`1<ShipContext> (EZData.VariableContext`1<ShipContext>)' while running with --aot-only.
    3.  
    4.   at System.Reflection.MonoProperty.GetterAdapterFrame[VariableContext`1,ShipContext] (System.Reflection.Getter`2 getter, System.Object obj) [0x00000] in <filename unknown>:0
    5.   at System.Reflection.MonoProperty.GetValue (System.Object obj, System.Object[] index) [0x00000] in <filename unknown>:0
    6.   at EZData.Context.FindProperty[Vector3] (System.Object node, System.String path, IBinding binding) [0x00000] in <filename unknown>:0
    7.   at EZData.Context.FindProperty[Vector3] (System.String path, IBinding binding) [0x00000] in <filename unknown>:0
    8.   at NguiDataContext.FindProperty[Vector3] (System.String path, .NguiBaseBinding binding) [0x00000] in <filename unknown>:0
    9. UnityEngine.Debug:Internal_Log(Int32, String, Object)
    10. UnityEngine.Debug:LogError(Object)
    11. NguiDataContext:FindProperty(String, NguiBaseBinding)
    12. NguiVector3Binding:Bind(NguiDataContext)
    13. NguiVector3Binding:Bind()
    14. NguiBaseBinding:UpdateBinding()
    15. NguiBaseBinding:Start()
    16. NguiProjectedPositionBinding:Start()
    I've tried putting

    Code (csharp):
    1. var t = new EZData.VariableContext<ShipContext>(null);
    2.  
    somewhere in the code to force it to AOT compile, but it doesn't seem to make a difference. Have I missed something in the docs about this?
     
  28. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Hi, if that's happening on the actual device, there are some player settings that may affect this. Please, refer to the player settings here: http://tools.artofbytes.com/wp-content/uploads/2012/09/iOS-Player-Settings.png, highlighted properties are critical.
    And forcing AOT compilation by explicit generic usage is no longer required since NData version 1.0.9. So, that must be the player settings. Please, let us know if it doesn't help.


     
  29. Simie

    Simie

    Joined:
    Oct 26, 2012
    Posts:
    456
    Thanks for the reply :)

    Switching to the .NET subset seems to have fixed the errors, but ideally I'd like to be on the full .NET 2.0 profile. Why is that not supported when using NData?
     
  30. Victorb

    Victorb

    Joined:
    May 31, 2012
    Posts:
    13
    Hi. I recently purchased NData and I am in the process of creating Contexts for existing game object scripts and the appropriate UI objects to display the context data.

    I am having some trouble displaying my quest list properly.

    GameContext contains QuestPoolContext contains Quests which is an EZData.Collection<QuestContext> and a QuestContext also has an EZData.Collection<QuestContext> property named SubQuests.

    I have NguiListItemTemplate scripts and prefabs properly set up so that I can view a list of quests with their subquests indented underneath them.

    My scripts are populating the path Game.QuestPool.Quests with the properly initialized QuestContext objects, with proper SubQuests collections.

    When I use the Game.QuestPool.Quests path in the NguiItemsSourceBinding, the quests stack and indent properly, reading from top-down and left-right.

    My scripts also copy references to these contexts into another collection, where quests can be available to an individual player.

    When I change the binding path to Game.Player.QuestLog.Available.Quests (which is a separate instance of EZData.Collection<QuestContext> which contains pointers to the same QuestContext objects which are in Game.QuestPool.Quests), then the NGUI objects in the prefabs get generated and populated properly, but their position data is all screwed up.

    Instead of cascading top-down and indenting left-right, they are distributed all across the top of the screen, at the same vertical height, but overlapping themselves horizontally.


    What could cause position of NGUI objects to change when the only difference is the Collection they are pulling the Items from? Even the items themselves are exactly the same object in both cases.


    Is it a problem that I am making multiple references to the same context? Would it be required instead for me to make a new EZData.Context object cloned from the first one instead of referencing the same memory from two Collections?
     
  31. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    It's because of Type.GetProperty() and Type.GetValue() usage, currently there's no way around it.
     
  32. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Hi, It's absolutely fine to have same items in several collections, and from your description it looks like it should work, so it's most likely a bug. You mentioned changing the binding path. Are you doing it in run-rime? If so, then it can be it, we rarely use run-time paths changing, and it might be unstable. Otherwise, it may be the bug in items insertion into the table, depending on how the collection items creation/initialization sequence. Anyways we'll take a look into it. If you could provide some test scene with bug reproduce it would be excellent. But even the code of collections content initialization would be very helpful. Thanks for feedback.
     
  33. Arthur-Pai

    Arthur-Pai

    Joined:
    May 29, 2012
    Posts:
    6
    This is work. i am using you way, now.
    I was use other way before, but like you solution.
    Its by active NGUI Tween Scale's update table properties, and add follow code in the update function of Tween Scale, let it call each parent table.

    Code (csharp):
    1.  
    2.             var parent = NguiUtils.GetComponentInParentsExcluding<UITable>(gameObject);
    3.             while (parent != null)
    4.             {
    5.                 var parentLookup = NguiUtils.GetComponentInParentsExcluding<UITable>(
    6.                     parent.gameObject);
    7.                 if (parentLookup == null)
    8.                     parent.repositionNow = true;
    9.                 else
    10.                     parent.Reposition();
    11.                 parent = parentLookup;
    12.             }
    About question 2, I am solved.
    I am using your Visiblity Binding, Local Position Binding and Transform Variant Binding to control widget of the item.
    The result of my desire is like follow, it can expand node to see it's child item, and child item may be one line of two line.
    $TableExpand.png
     
  34. quangpham

    quangpham

    Joined:
    Apr 22, 2013
    Posts:
    3
    In the ItemsList example, do we have any way to get gameObject when list item is selected in Ndata?

    For example, when an ListItem is selected, we can get the gameObject of it in List/Table/...
     
  35. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    There already was a similar question somewhere in this thread, here's the response anyways:
    Commands by design don't have parameters. And usually when you handle a command, you should not have access to the view layer. However, if you need to know the game object that triggered command, you can try something like that:

    Code (csharp):
    1.  
    2.     public class NguiOnClickBindingWithSource : NguiCommandBinding
    3.     {
    4.         public string SourcePath;
    5.         private Property<GameObject> _source;
    6.      
    7.         protected override void Unbind()
    8.         {
    9.             base.Unbind();
    10.             _source = null;
    11.         }
    12.      
    13.         protected override void Bind()
    14.         {
    15.             base.Bind();
    16.             var context = GetContext(SourcePath);
    17.             if (context != null)
    18.                 _source = context.FindProperty<string>(SourcePath, this);
    19.         }
    20.      
    21.         void OnClick()
    22.         {
    23.             _source.SetValue(gameObject);
    24.             if (_command != null)
    25.                 _command.DynamicInvoke();
    26.         }
    27.     }
    28.  
    This way you'll be able to use one of the properties in your context as a command argument. But we'd recommend to check, maybe it's possible to handle the situation in a custom UI component completely on a View layer without notifying the context. That would be some analog of code-behind in WPF.
     
  36. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Hi all. Please welcome the updated NData: http://u3d.as/content/art-of-bytes/ndata-for-ngui/2KC.
    Release notes are here as usual: http://tools.artofbytes.com/ndata/release-notes
    There are some fixes for advanced use-cases. And also couple of new examples. But the most significant improvement is the MonoBehaviourContext class. You can now have regular unity components and use them as contexts too, like this:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class MyComponent : EZData.MonoBehaviourContext
    5. {
    6.     public NguiRootContext View;
    7.    
    8.     #region Property MyProperty
    9.     private readonly EZData.Property<string> _privateMyPropertyProperty = new EZData.Property<string>();
    10.     public EZData.Property<string> MyPropertyProperty { get { return _privateMyPropertyProperty; } }
    11.     public string MyProperty
    12.     {
    13.     get    { return MyPropertyProperty.GetValue();    }
    14.     set    { MyPropertyProperty.SetValue(value); }
    15.     }
    16.     #endregion
    17.    
    18.     void Awake()
    19.     {
    20.         View.SetContext(this);
    21.     }
    22. }
    23.  
    You can still have old-style contexts classes derived from EZData.Context. And of-course you can freely aggregate Context-derived classes in MonoBehaviourContext-derived components' and vice versa. It can really make things less redundant and more slim in certain cases.

    New version should be stable, but anyways if you notice that something is broken after update, please, let us know.
    Always yours, Art of Bytes team.
     
  37. Nezabyte

    Nezabyte

    Joined:
    Aug 21, 2012
    Posts:
    110
    Are we doing this correctly / best way? We have a PersistentData class to store data that is shared across all scenes and is initialized when the game starts. The class initializes other classes (just School right now) that need to have their data brought along with it.

    We ran into an issue last night where I wrote the initial data for School within the constructor of School. Since the usual binding code is within the Awake function, NData sees no changes and doesn't update the GUI. We then moved the School initial data to the Start function of PersistentData (since School isn't a MonoBehavior, and we don't want it to be one anyway).

    $school.PNG $schoolmanagement.PNG $persistentdata.PNG
     
  38. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    It is the valid way of having some common contexts across the scenes. And in fact you can feed NData pre-initialized contexts, all bindings apply the initial property value when resolved, just as if OnChange was triggered. Code looks correct, and from the first impression should work. The only suspicious place is the SchoolManagementViewModel.Awake it looks for a component PersistentData in "Persistence" object. Make sure it finds it and there's not-null School object in it. Then it also may be bindings paths, if you want to display Fame property in the view, path should be "Fame" and not "School.Fame", just in case.

    Other than that it's hard to say, you need to localize the problem, if there's a valid context you are providing to View.SetContext, and view is not null - then it's something related to NData. Otherwise - it goes wrong before getting to NData scope. Oh, also you are assigning persistentData.school to "Context" variable (that is not seen on the screenshot), and then you provide value of uninitialized "context" variable to view.
     
  39. Nezabyte

    Nezabyte

    Joined:
    Aug 21, 2012
    Posts:
    110
    Ahh, so it should work even if the data is initialized via School constructor before the context is set. Thanks, we'll revert to the original solution and run a few tests. Will followup later...

    Sorry for the confusion for the Context variable. I was in the middle of renaming to "context", but I forgot to rename in the Awake function before I uploaded screenshots here.
     
    Last edited: Jul 15, 2013
  40. quangpham

    quangpham

    Joined:
    Apr 22, 2013
    Posts:
    3
    Hi,

    I try to bind the current index of a collection (for example a current picture of a gallery). I tried to use set the Path for textbinding like Collection.SelectedIndex+1. It didn't work.

    Is it a good way to do?

    Thank you
     
  41. quangpham

    quangpham

    Joined:
    Apr 22, 2013
    Posts:
    3
    It seems that all visibility bindings are not working in iOS device. I have tried VisibilityCheckboxes with my ipad. The sprites don't appear when checkboxes are checked. However, when I test with iOS simulator, it works well.

    I am using Ndata 1.0.15, Ngui 2.6.1e, iPad 3, iOS 6.1.3
     
  42. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    "Collection.SelectedIndex+1" is not a valid path, if you want a modified value, you could have an integer property SelectedPictureNumber next to collection that is updated on SelectedIndex change. like:
    Collection.SelectedIndex.OnChange += () => { SelectedPictureNumber = Collection.SelectedIndex + 1; };

    Also, the iOS device bindings fix is already on the way. Those AOT-related bugs are really frustrating, when some random valid C# code doesn't work, unless you make it more explicit.
     
  43. schragnasher

    schragnasher

    Joined:
    Oct 7, 2012
    Posts:
    117
    Hello i am having an issue with ndata. I have a popup list from ngui that i am trying to populate with a list of items. Everything seems to be working ok, but when switching scenes i get an error. I load up the scene with the popup widget and its ok, then i switch to a different scene, which unloads the last one, everything is ok, then i reload the scene with the popup and i get this error:

    Code (csharp):
    1. MissingReferenceException: The object of type 'NguiTextBinding' has been destroyed but you are still trying to access it.
    2. Your script should either check if it is null or you should not destroy the object.
    3. UnityEngine.Component.get_gameObject () (at C:/BuildAgent/work/7535de4ca26c26ac/Runtime/ExportGenerated/Editor/UnityEngineComponent.cs:170)
    4. NguiBaseBinding.UpdateMasterPath () (at Assets/NData/NGUI/NData/NguiBaseBinding.cs:35)
    5. NguiBaseBinding.UpdateBinding () (at Assets/NData/NGUI/NData/NguiBaseBinding.cs:75)
    6. NguiBaseBinding.OnContextChange () (at Assets/NData/NGUI/NData/NguiBaseBinding.cs:93)
    7. EZData.VariableContext`1[Gladiator].set_Value (.Gladiator value) (at Assets/NData/NGUI/NData/Core/VariableContext.cs:56)
    8. EZData.Collection`1[Gladiator].set_SelectedItem (.Gladiator value) (at Assets/NData/NGUI/NData/Core/Collection.cs:250)
    9. EZData.Collection`1[Gladiator].SetSelectedItem (Int32 index, .Gladiator item) (at Assets/NData/NGUI/NData/Core/Collection.cs:342)
    10. EZData.Collection`1[Gladiator].SelectItem (Int32 index) (at Assets/NData/NGUI/NData/Core/Collection.cs:106)
    11. NguiPopupListSourceBinding.OnSelectionChange (System.String selectedItem) (at Assets/NData/NGUI/NData/NguiPopupListSourceBinding.cs:107)
    12. UnityEngine.GameObject:SendMessage(String, Object, SendMessageOptions)
    13. UIPopupList:set_selection(String) (at Assets/NGUI/Scripts/Interaction/UIPopupList.cs:180)
    14. NguiPopupListSourceBinding:OnItemInsert(Int32, Context) (at Assets/NData/NGUI/NData/NguiPopupListSourceBinding.cs:51)
    15. NguiItemsSourceBinding:Bind() (at Assets/NData/NGUI/NData/NguiItemsSourceBinding.cs:58)
    16. NguiPopupListSourceBinding:Bind() (at Assets/NData/NGUI/NData/NguiPopupListSourceBinding.cs:33)
    17. NguiBaseBinding:UpdateBinding() (at Assets/NData/NGUI/NData/NguiBaseBinding.cs:77)
    18. NguiBaseBinding:Start() (at Assets/NData/NGUI/NData/NguiBaseBinding.cs:88)
     
  44. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    Checking this. Does it happen with popup list source binding only or you noticed similar errors for other bindings too?
     
  45. Nezabyte

    Nezabyte

    Joined:
    Aug 21, 2012
    Posts:
    110
    Is there a binding for when you select an option from a popup list to call a function? I only see OnClick binding, which seems to only trigger when you click on the dropdown itself.
     
  46. tripknotix

    tripknotix

    Joined:
    Apr 21, 2011
    Posts:
    744
    having an issue with using UISlider and NguiSliderBinding,

    In my scene i have the slider and a label, both hooked up to the NDATA for MasterVolume that ive created. (ive tested this with both persistent property and just property formatting)

    in my code i'm using MasterAudio, which uses statics for many things, including playing sounds.

    Inside of the MasterAudio class, in a static method for playing a sound, i would do volume = MyViewModel.Context.MasterVolume;

    It seems that during the initial start of everything, they both are using the exact same NDATA property, but as soon as i start trying to access the NDATA from inside of the static method for playing sounds. its like they are using 2 separate NDATA properties even though they are both using MasterVolume property.

    Has anyone recognized this issue before, and could shed some light perhaps on known issues with using NDATA and static methods.
     
  47. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    There's no such binding. You can always subscribe to Collection OnSelectionChanged event. However, if you need that to use selected item property as a new value in some other control, you could just bind to "Collection.SelectedItem.MyProperty". In general, you don't need to handle events to push stuff around the UI, NData does all synchronization automatically, you just need to specify, which control displays what. So, the bindings are mostly data bindings. And command bindings are more like an exception.
     
  48. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    No bug reproduce so far, tried to switch between two different scenes, one of them contains popup list. No sign of errors. What is the exact way you are using to load another scene, regular LoadLevel or async or aditive, are there "don't destroy on load" objects? Would be perfect, if you could provide a sample project with the bug reproduce.
     
  49. Art Of Bytes

    Art Of Bytes

    Joined:
    Jun 8, 2011
    Posts:
    198
    This setup is not 100% clear from description. Code of your contexts with all properties could clarify the situation.
     
  50. Nezabyte

    Nezabyte

    Joined:
    Aug 21, 2012
    Posts:
    110
    Thanks, I'll look into it that event. What we're trying to do is maintain the state of a dropdown field. So for example, there are two dropdown fields, one to select a person (assigned to property Persons), and the other to select what they're going to train (assigned to property Skills).

    As selections are made (assigning to property SkillToTrain) per person in the GUI, the state of the Skill dropdown GUI field is "saved"...so when the Person dropdown is switched back to someone that already had a SkillToTrain property assigned, the selected dropdown field will switch to that automatically.