Nir Hasson
Topic Author
Posts: 53
Joined: 10 Nov 2013, 21:20
Contact:

Programmatically Creating Elements from DataTemplate

08 Dec 2013, 19:31

Hi,

I need to generate multiple elements dynamically based on real time data and display them on some kind of host controller - WrapPannel in my example.
Each custom element has it's own name, id and display image which I load and create dynamically.

I created a a data template element and I want to fill the host container with multiple different instances.

1. What will be the best approach for this problem ?
2. How can I create a Frameworkelement based on this DataTemplate on runtime ?
3. How can I use the DataTemplate consept with ItemsControl ? I tried create a DataBinding to ItemsSource as well as adding items manually to the Items attribute but without success.

[EDIT]
I use the native SDK.
I need a quick sample that shows how to bind data to ItemsSource from independent application framework

One more related question to this -
When I create Gui resources dynamically such as TextBlocks, Buttons, etc I'm using the new keyword.
Should I use different allocation mechanism ? Who is in charge to free these elements ? The application code or Noesis SDK ?
 
User avatar
sfernandez
Site Admin
Posts: 1911
Joined: 22 Dec 2011, 19:20

Re: Programmatically Creating Elements from DataTemplate

09 Dec 2013, 20:46

What you are trying to achieve is usually done with data binding.

You will have a data model (some class inheriting from BaseComponent) which exposes (via a reflection property) the list of dynamically created items (a Collection property). Each dynamically created item will be represented by a class (inheriting frpm BaseComponent) which exposes (via reflection) the properties you want to bind to your data template: name, id, image...

In your example it would be something like this:
class DataModel: public BaseComponent
{
public:
    DataModel(): _items(*new Collection()) { }

    Collection* GetItems() const { return _items; }

private:
    Ptr<Collection> _items;

    NS_IMPLEMENT_INLINE_REFLECTION(DataModel, BaseComponent)
    {
        NsMeta<TypeId>("DataModel");
        NsProp("Items", &DataModel::GetItems);
    }
};

struct DataItem: public BaseComponent
{
    DataItem(const NsChar* n, NsInt32 i, ImageSource* img):
         _name(n), _id(i), _image(img)
    {
    }

    NsString _name;
    NsInt32 _id;
    Ptr<ImageSource> _image;

    NS_IMPLEMENT_INLINE_REFLECTION(DataItem, BaseComponent)
    {
        NsMeta<TypeId>("DataItem");
        NsProp("Name", &DataModel::_name);
        NsProp("Id", &DataModel::_id);
        NsProp("Image", &DataModel::_image);
    }
};
The xaml that describes the DataTemplate and presents your items could be like this:
<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Name="LayoutRoot">

    <Grid.Resources>
        <DataTemplate x:Key="MyItemTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="20"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="50"/>
                </Grid.ColumnDefinitions>
                <Image Source="{Binding Image}" Grid.Column="0" Margin="0,0,10,0"/>
                <TextBlock Text="{Binding Name}" FontWeight="Bold" Grid.Column="1"/>
                <TextBlock Text="{Binding Id}" TextAlignment="Right" Grid.Column="2"/>
            </Grid>
        </DataTemplate>
    </Grid.Resources>

    <ListBox Width="150" Height="100"
        ItemsSource="{Binding Items}"
        ItemTemplate="{StaticResource MyItemTemplate}"/>

</Grid>
When the application starts, you can assign the Data Model instance in the DataContext property of the xaml root:
Ptr<DataModel> data = *new DataModel();
Collection* items = data->GetItems();

// NsVector<Ptr<ImageSource> > images;
Ptr<DataItem> item1 = *new DataItem("Item1", 1, images[0].GetPtr());
items->Add(item1.GetPtr());
Ptr<DataItem> item2 = *new DataItem("Item2", 2, images[1].GetPtr());
items->Add(item2.GetPtr());
Ptr<DataItem> item3 = *new DataItem("Item3", 3, images[2].GetPtr());
items->Add(item3.GetPtr());

FrameworkElement* root = FindName<FrameworkElement>("LayoutRoot");
root->SetDataContext(data);
Take a look at Bindings native tutorial for more examples of data models and bindings.
 
User avatar
sfernandez
Site Admin
Posts: 1911
Joined: 22 Dec 2011, 19:20

Re: Programmatically Creating Elements from DataTemplate

09 Dec 2013, 20:53

Hi,
One more related question to this -
When I create Gui resources dynamically such as TextBlocks, Buttons, etc I'm using the new keyword.
Should I use different allocation mechanism ? Who is in charge to free these elements ? The application code or Noesis SDK ?
All BaseComponent inherited objects are reference counted. This references can be managed manually by AddReference()/Release() or you can use our smart pointer class: Ptr.

More information can be found in our documentation inside the SDK here:
- Doc/Core.Kernel.ComponentReferenceCounting.html
- Doc/Core.Kernel.ComponentSmartPointer.html
 
User avatar
jsantos
Site Admin
Posts: 2903
Joined: 20 Jan 2012, 17:18
Contact:

Re: Programmatically Creating Elements from DataTemplate

10 Dec 2013, 00:02

The recommended way is using smart pointers:
Ptr<Button> sphere = *new Button();
NOTE THE * BEFORE THE NEW OPERATOR

That way the button is removed whenever its last reference is destroyed. It is the easier way to avoid problems in C++ when using Dependency Objects.

By the way, the documents mentioned by sfernandez are not directly referenced by the index. We will fix this in the next version (1.1.3).
 
Nir Hasson
Topic Author
Posts: 53
Joined: 10 Nov 2013, 21:20
Contact:

Re: Programmatically Creating Elements from DataTemplate

10 Dec 2013, 09:20

Thank you very much !
The Collection sample really helped me getting progressed with that.
I''ll keep in mind the recommandation of using smart pointers. For now I use raw data pointers and manage the Ref count myself..
 
Nir Hasson
Topic Author
Posts: 53
Joined: 10 Nov 2013, 21:20
Contact:

Re: Programmatically Creating Elements from DataTemplate

10 Dec 2013, 15:56

I'm working on the DataModel example you suggested and I'm trying to extend it.

However it seems that there is some kind of cache file that holds the Binding attributes names.
I tried to rename the xaml Binding of the text block from Id to Desc as described below.

Original XAML Code:
<TextBlock Text="{Binding Id}" TextAlignment="Right" Grid.Column="2"/>
Original Source Code:
NsProp("Id", &DataItem::m_strDesc);
Updated XAML Code:
<TextBlock Text="{Binding Desc}" TextAlignment="Right" Grid.Column="2"/>
Updated Source Code:
NsProp("Desc", &DataItem::m_strDesc);
The updated version is not working. How can I solve it ?
 
User avatar
sfernandez
Site Admin
Posts: 1911
Joined: 22 Dec 2011, 19:20

Re: Programmatically Creating Elements from DataTemplate

11 Dec 2013, 11:13

When a XAML file is builded to a UI resource (using our BuildTool.exe), the xml code is parsed and converted to Noesis objects. The only place I can think of keeping the old property name is in the UI resource, so my guess is you didn't rebuild your XAML file after modifying the code, otherwise we could be facing a bug here.
 
Nir Hasson
Topic Author
Posts: 53
Joined: 10 Nov 2013, 21:20
Contact:

Re: Programmatically Creating Elements from DataTemplate

11 Dec 2013, 11:35

I'm using a simple batch file for the UI Resources "compilation" process.
This batch file is doing the following:
1. Delete the entire package folder and subfolders of my application
under <SDK>\Data\Packages\[Package_Name].
2. Copy the package content and resources to the target package folder.
3. Run the BuildTool with the following options.
%BUILD_TOOL% scan --autoadd
%BUILD_TOOL% build
4. Copy back the generated binary cache files and updated guids file to my media folder.

I also tried deleting the entire generated binary folder under <SDK>\Bin\Data\DX9\[Package_Name].
None of these worked..
Do you have some other persistent storage that you use to store symbols in ? Maybe the registry of some other kind of data base in the user folder ?
 
User avatar
sfernandez
Site Admin
Posts: 1911
Joined: 22 Dec 2011, 19:20

Re: Programmatically Creating Elements from DataTemplate

11 Dec 2013, 12:41

No other cache/registry stores XAML data apart from UI resources.

If you revert the NsProp name back to "Id" and the binding path in the xaml, does the TextBlock show the text again?
 
User avatar
jsantos
Site Admin
Posts: 2903
Joined: 20 Jan 2012, 17:18
Contact:

Re: Programmatically Creating Elements from DataTemplate

11 Dec 2013, 13:33

I'm using a simple batch file for the UI Resources "compilation" process.
This batch file is doing the following:
1. Delete the entire package folder and subfolders of my application
under <SDK>\Data\Packages\[Package_Name].
2. Copy the package content and resources to the target package folder.
3. Run the BuildTool with the following options.
%BUILD_TOOL% scan --autoadd
%BUILD_TOOL% build
4. Copy back the generated binary cache files and updated guids file to my media folder.
Note that this way you are rebuilding the entire data base each time. As you increase the number of xamls this can be quite inefficient. A simple "build" command is able to detect changes and start and incremental build.

Who is online

Users browsing this forum: dstroup, Google [Bot] and 1 guest