User avatar
tkaczz
Topic Author
Posts: 16
Joined: 01 May 2018, 20:39

[Solved] How to bind nested ObservableCollections in Unity/C#

21 May 2018, 18:08

I'm learning NoesisGUI but I'm stuck with problem from subject. No matter what tried I it's simply not working. I tried based on these examples It creates correct number of recipe entries but I can't see any thumbnails etc.

And here how it looks in Unity (I know it's not preety, I'm trying to learn these things)
public class CraftingPresenter : MonoBehaviour {
    private Page craftingView;

	(Few commands etc. ...)

    public ObservableCollection<RecipeEntry> CraftItemEntries { get; private set; }

    private void Start() {
        InitView();
        InitObservables();
        SetUI();
    }

    private void InitView() {
        craftingView = (Page)Noesis.GUI.LoadXaml("Assets/Scripts/Views/CraftingView.xaml");

        mainViewPresenter.CurrentMainViewState
            .Where(state => state == MainViewStates.Crafting)
            .Subscribe(_ => {
                mainViewPresenter.MainContent.Content = craftingView;
            })
            .AddTo(this);
    }

    private void InitObservables() {
        CraftItemEntries = new ObservableCollection<RecipeEntry>();
    }

    private void SetUI() {
        var listView = (ListView)craftingView.FindName("RecipesList");
        listView.DataContext = this;

        var actions = (UniformGrid)craftingView.FindName("Actions");
        actions.DataContext = this;
    }

    public void UpdateRecipesList(Dictionary<ItemTypes, int> selectedIngridients) {
        var availableRecipes = recipesDatabase.FindMatchingRecipes(selectedIngridients);

        CraftItemEntries.Clear();

        foreach (var recipe in availableRecipes) {
            var newRecipeEntry = new RecipeEntry();

            foreach (var ingridient in recipe.Ingridients) {
                var itemEntry = inventoryPresenter.FindItemEntry(ingridient.Key);
                newRecipeEntry.Ingridients.Add(itemEntry);
            }
            foreach (var product in recipe.RecipeProducts) {
                var currentProduct = itemEntriesFactory.CreateItemEntry(
                    product.Key,
                    product.Value
                );

                newRecipeEntry.Products.Add(currentProduct);
            }

            CraftItemEntries.Add(newRecipeEntry);
        }
    }
}

public class ItemEntry {
    public ItemTypes ItemType { get; private set; }
    public string Name { get; private set; }
    public TextureSource Thumbnail { get; private set; }
    public int ItemCount { get; private set; }
    public string Description { get; private set; }

    public ItemEntry(
        ItemTypes itemType,
        TextureSource itemTexture,
        int itemCount,
        string description
    ) {
        ItemType = itemType;
        Name = itemType.ToString();
        Thumbnail = itemTexture;
        ItemCount = itemCount;
        Description = description;
    }
}

public class ItemEntriesFactory {
    (...)

    public ItemEntry CreateItemEntry(ItemTypes itemType, int quantity) {
        var itemFromDB = itemsDatabase.FindItemRow(itemType);

        return new ItemEntry(
            itemFromDB.PrimaryKey,
            new TextureSource(itemFromDB.Thumbnail),
            quantity,
            itemFromDB.Description
        );
    }
}
And Xaml
       <ScrollViewer
            DockPanel.Dock="Top"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled"
            ScrollViewer.VerticalScrollBarVisibility="Auto">
            <ListView
                Name="RecipesList"
                ItemsSource="{Binding CraftItemEntries}"
                SelectionMode="Multiple">
                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid Columns="1" />
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ListView ItemsSource="{Binding Ingridients}">
                            <ListView.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <UniformGrid Rows="1" />
                                </ItemsPanelTemplate>
                            </ListView.ItemsPanel>
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <Grid MinHeight="100">
                                        <Image
                                            MinWidth="50"
                                            MinHeight="50"
                                            Margin="5"
                                            VerticalAlignment="Center"
                                            Source="{Binding Thumbnail}" />
                                        <TextBlock
                                            HorizontalAlignment="Right"
                                            VerticalAlignment="Bottom"
                                            Text="{Binding ItemCount}" />
                                    </Grid>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                     </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </ScrollViewer>
This is what I want to achieve (It's embedded inside other view)
Image
Last edited by tkaczz on 25 May 2018, 14:19, edited 1 time in total.
 
User avatar
tkaczz
Topic Author
Posts: 16
Joined: 01 May 2018, 20:39

Re: How to bind nested ObservableCollections in Unity/C#

23 May 2018, 12:27

I made workaround by creating ObservableCollection for ingridients displayed on top, and second in middle that displays recipes. But question from topic title still exists.
 
User avatar
tkaczz
Topic Author
Posts: 16
Joined: 01 May 2018, 20:39

Re: How to bind nested ObservableCollections in Unity/C#

25 May 2018, 14:18

With big help of Log Verbosity set to "Bindings", console printed very helpful message:
[NOESIS] Type 'RecipeProducts' does not contain a property named 'Products'
[NOESIS] Binding failed: Path=Products, Source=RecipeProducts(''), Target=ListBox(''), TargetProperty=ItemsControl.ItemsSource
After changing Products -> Products { get; set; } everything worked
 
User avatar
sfernandez
Site Admin
Posts: 2984
Joined: 22 Dec 2011, 19:20

Re: [Solved] How to bind nested ObservableCollections in Unity/C#

30 May 2018, 19:46

Nice to hear you made progress.

Just some tips...

1. You can set the DataContext once in the View root and it will be inherited down the tree of elements. You don't probably need to look for individual named elements and assign it every time (like you did with "RecipesList" and "Actions").

2. A ListView control provides its own ScrollViewer so you don't need to wrap it with another ScrollViewer.

3. If your list is not going to show several columns of information for each item (like in a DataGrid), you can use a ListBox for better performance.

4. The data items provided as ItemsSource in a list behave as DataContext for their corresponding visual item in the list. So bindings specified in the ItemTemplate should be available as properties of the data item, otherwise bindings won't resolve.
 
User avatar
tkaczz
Topic Author
Posts: 16
Joined: 01 May 2018, 20:39

Re: [Solved] How to bind nested ObservableCollections in Unity/C#

10 Jun 2018, 14:43

Nice to hear you made progress.

Just some tips...

1. You can set the DataContext once in the View root and it will be inherited down the tree of elements. You don't probably need to look for individual named elements and assign it every time (like you did with "RecipesList" and "Actions").

2. A ListView control provides its own ScrollViewer so you don't need to wrap it with another ScrollViewer.

3. If your list is not going to show several columns of information for each item (like in a DataGrid), you can use a ListBox for better performance.

4. The data items provided as ItemsSource in a list behave as DataContext for their corresponding visual item in the list. So bindings specified in the ItemTemplate should be available as properties of the data item, otherwise bindings won't resolve.
I'm beginner in Unity and NoesisGUI, so I really appreaciate your tips, thank you.

Who is online

Users browsing this forum: Ahrefs [Bot] and 9 guests