Get content from ContentControl / DataTemplate
Posted: 15 May 2024, 19:23
Hello!
I would like to know how I can do something in the best way.
Consider that I have complex content, which is displayed more than once on the same screen.
Here I will exemplify some TextBlocks that need to be changed by c++ without binding, but they are more complex things.
I made an example at HelloWorld in c++.
This would be the example with non-duplicate content.
MainWindow.xaml
Main.cpp: (Original content of the example, I just changed the MainWindow class with a very basic example)
Now consider that this StackPanel with these TextBlock needs to go to the other grid locations (0,1), (1,0), (1,1).
The way I can do it today is very bad, for example:
XAML:
CPP:
So I started trying to create some type of data that could be reused, such as:
CPP:
However, some strange things happened.
1 - The "MyTextBlock1/2/3/4" pointer is the same between all 4 structs.
The MyTextBlock1 pointer is the same in all structs.
The MyTextBlock2 pointer is the same in all structs, and so on.
I was wondering why this happens, and I came to the conclusion that when I do FindName<ContentControl>(name)->GetContentTemplate();
I'm getting the template itself, and not the elements created from the template within the ContentControl, so I always get the same pointer regardless of which ContentControl I indicate.
Would it be this? Because if I do it in my OnInitialized():
component1.MyTextBlock1->SetText("My Test Text");
It sets the text in MyTextBlock1 of all 4 elements.
So how do I actually get the element inside my ContentControl that was created by the DataTemplate?
It's possible?
2 - Why after I used ContentControl, my Render() loop no longer changes the text?
Could it be because of what I explained above? Does it take the element pointer in the template, and not the one created in the final element, so it changes the text in it and is not actually applied to the created element?
Thanks.
I would like to know how I can do something in the best way.
Consider that I have complex content, which is displayed more than once on the same screen.
Here I will exemplify some TextBlocks that need to be changed by c++ without binding, but they are more complex things.
I made an example at HelloWorld in c++.
This would be the example with non-duplicate content.
MainWindow.xaml
Code: Select all
<Window
x:Class="HelloWorld.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:HelloWorld"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="NoesisGUI - Hello, World!"
Background="#FF124C7A"
FontFamily="./#Aero Matics"
FontSize="36"
mc:Ignorable="d">
<Grid Width="600" Height="480">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Row="0"
Grid.Column="0"
Margin="10">
<TextBlock Name="MyTextBlock1" Text="Test 1" />
<TextBlock Name="MyTextBlock2" Text="Test 2" />
<TextBlock Name="MyTextBlock3" Text="Test 3" />
<TextBlock Name="MyTextBlock4" Text="Test 4" />
<TextBlock Name="MyTextBlock5" Text="Test 5" />
</StackPanel>
</Grid>
</Window>Main.cpp: (Original content of the example, I just changed the MainWindow class with a very basic example)
Code: Select all
class MainWindow final : public Window
{
public:
MainWindow()
{
Initialized() += MakeDelegate(this, &MainWindow::OnInitialized);
Loaded() += MakeDelegate(this, &MainWindow::OnLoaded);
}
private:
void OnLoaded(Noesis::BaseComponent* /*sender*/, const Noesis::RoutedEventArgs& /*e*/)
{
GetDisplay()->Render() += [this](NoesisApp::Display* /*_display*/)
{
MyNumber++;
MyTextBlock1->SetText(std::to_string(MyNumber).data());
MyTextBlock2->SetText(std::to_string(MyNumber * 10).data());
MyTextBlock3->SetText(std::to_string(MyNumber * 20).data());
MyTextBlock4->SetText(std::to_string(MyNumber * 30).data());
MyTextBlock5->SetText(std::to_string(MyNumber * 40).data());
};
}
void OnInitialized(BaseComponent*, const Noesis::EventArgs&)
{
SetDataContext(this);
MyTextBlock1 = FindName<TextBlock>("MyTextBlock1");
MyTextBlock2 = FindName<TextBlock>("MyTextBlock2");
MyTextBlock3 = FindName<TextBlock>("MyTextBlock3");
MyTextBlock4 = FindName<TextBlock>("MyTextBlock4");
MyTextBlock5 = FindName<TextBlock>("MyTextBlock5");
}
TextBlock* MyTextBlock1{ nullptr };
TextBlock* MyTextBlock2{ nullptr };
TextBlock* MyTextBlock3{ nullptr };
TextBlock* MyTextBlock4{ nullptr };
TextBlock* MyTextBlock5{ nullptr };
unsigned long long MyNumber = 0;
private:
NS_IMPLEMENT_INLINE_REFLECTION_(MainWindow, Window, "HelloWorld.MainWindow")
};
The way I can do it today is very bad, for example:
XAML:
Code: Select all
<Window
x:Class="HelloWorld.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:HelloWorld"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="NoesisGUI - Hello, World!"
Background="#FF124C7A"
FontFamily="./#Aero Matics"
FontSize="36"
mc:Ignorable="d">
<Grid Width="600" Height="480">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Row="0"
Grid.Column="0"
Margin="10">
<TextBlock Name="MyTextBlock01" Text="Test 1" />
<TextBlock Name="MyTextBlock02" Text="Test 2" />
<TextBlock Name="MyTextBlock03" Text="Test 3" />
<TextBlock Name="MyTextBlock04" Text="Test 4" />
<TextBlock Name="MyTextBlock05" Text="Test 5" />
</StackPanel>
<StackPanel
Grid.Row="0"
Grid.Column="1"
Margin="10">
<TextBlock Name="MyTextBlock11" Text="Test 1" />
<TextBlock Name="MyTextBlock12" Text="Test 2" />
<TextBlock Name="MyTextBlock13" Text="Test 3" />
<TextBlock Name="MyTextBlock14" Text="Test 4" />
<TextBlock Name="MyTextBlock15" Text="Test 5" />
</StackPanel>
<StackPanel
Grid.Row="1"
Grid.Column="0"
Margin="10">
<TextBlock Name="MyTextBlock21" Text="Test 1" />
<TextBlock Name="MyTextBlock22" Text="Test 2" />
<TextBlock Name="MyTextBlock23" Text="Test 3" />
<TextBlock Name="MyTextBlock24" Text="Test 4" />
<TextBlock Name="MyTextBlock25" Text="Test 5" />
</StackPanel>
<StackPanel
Grid.Row="1"
Grid.Column="1"
Margin="10">
<TextBlock Name="MyTextBlock31" Text="Test 1" />
<TextBlock Name="MyTextBlock32" Text="Test 2" />
<TextBlock Name="MyTextBlock33" Text="Test 3" />
<TextBlock Name="MyTextBlock34" Text="Test 4" />
<TextBlock Name="MyTextBlock35" Text="Test 5" />
</StackPanel>
</Grid>
</Window>
Code: Select all
class MainWindow final : public Window
{
public:
MainWindow()
{
Initialized() += MakeDelegate(this, &MainWindow::OnInitialized);
Loaded() += MakeDelegate(this, &MainWindow::OnLoaded);
}
private:
void OnLoaded(Noesis::BaseComponent* /*sender*/, const Noesis::RoutedEventArgs& /*e*/)
{
GetDisplay()->Render() += [this](NoesisApp::Display* /*_display*/)
{
MyNumber++;
MyTextBlock01->SetText(std::to_string(MyNumber).data());
MyTextBlock02->SetText(std::to_string(MyNumber * 10).data());
MyTextBlock03->SetText(std::to_string(MyNumber * 20).data());
MyTextBlock04->SetText(std::to_string(MyNumber * 30).data());
MyTextBlock05->SetText(std::to_string(MyNumber * 40).data());
MyTextBlock11->SetText(std::to_string(MyNumber).data());
MyTextBlock12->SetText(std::to_string(MyNumber * 10).data());
MyTextBlock13->SetText(std::to_string(MyNumber * 20).data());
MyTextBlock14->SetText(std::to_string(MyNumber * 30).data());
MyTextBlock15->SetText(std::to_string(MyNumber * 40).data());
MyTextBlock21->SetText(std::to_string(MyNumber).data());
MyTextBlock22->SetText(std::to_string(MyNumber * 10).data());
MyTextBlock23->SetText(std::to_string(MyNumber * 20).data());
MyTextBlock24->SetText(std::to_string(MyNumber * 30).data());
MyTextBlock25->SetText(std::to_string(MyNumber * 40).data());
MyTextBlock31->SetText(std::to_string(MyNumber).data());
MyTextBlock32->SetText(std::to_string(MyNumber * 10).data());
MyTextBlock33->SetText(std::to_string(MyNumber * 20).data());
MyTextBlock34->SetText(std::to_string(MyNumber * 30).data());
MyTextBlock35->SetText(std::to_string(MyNumber * 40).data());
};
}
void OnInitialized(BaseComponent*, const Noesis::EventArgs&)
{
SetDataContext(this);
MyTextBlock01 = FindName<TextBlock>("MyTextBlock01");
MyTextBlock02 = FindName<TextBlock>("MyTextBlock02");
MyTextBlock03 = FindName<TextBlock>("MyTextBlock03");
MyTextBlock04 = FindName<TextBlock>("MyTextBlock04");
MyTextBlock05 = FindName<TextBlock>("MyTextBlock05");
MyTextBlock11 = FindName<TextBlock>("MyTextBlock11");
MyTextBlock12 = FindName<TextBlock>("MyTextBlock12");
MyTextBlock13 = FindName<TextBlock>("MyTextBlock13");
MyTextBlock14 = FindName<TextBlock>("MyTextBlock14");
MyTextBlock15 = FindName<TextBlock>("MyTextBlock15");
MyTextBlock21 = FindName<TextBlock>("MyTextBlock21");
MyTextBlock22 = FindName<TextBlock>("MyTextBlock22");
MyTextBlock23 = FindName<TextBlock>("MyTextBlock23");
MyTextBlock24 = FindName<TextBlock>("MyTextBlock24");
MyTextBlock25 = FindName<TextBlock>("MyTextBlock25");
MyTextBlock31 = FindName<TextBlock>("MyTextBlock31");
MyTextBlock32 = FindName<TextBlock>("MyTextBlock32");
MyTextBlock33 = FindName<TextBlock>("MyTextBlock33");
MyTextBlock34 = FindName<TextBlock>("MyTextBlock34");
MyTextBlock35 = FindName<TextBlock>("MyTextBlock35");
}
TextBlock* MyTextBlock01{ nullptr };
TextBlock* MyTextBlock02{ nullptr };
TextBlock* MyTextBlock03{ nullptr };
TextBlock* MyTextBlock04{ nullptr };
TextBlock* MyTextBlock05{ nullptr };
TextBlock* MyTextBlock11{ nullptr };
TextBlock* MyTextBlock12{ nullptr };
TextBlock* MyTextBlock13{ nullptr };
TextBlock* MyTextBlock14{ nullptr };
TextBlock* MyTextBlock15{ nullptr };
TextBlock* MyTextBlock21{ nullptr };
TextBlock* MyTextBlock22{ nullptr };
TextBlock* MyTextBlock23{ nullptr };
TextBlock* MyTextBlock24{ nullptr };
TextBlock* MyTextBlock25{ nullptr };
TextBlock* MyTextBlock31{ nullptr };
TextBlock* MyTextBlock32{ nullptr };
TextBlock* MyTextBlock33{ nullptr };
TextBlock* MyTextBlock34{ nullptr };
TextBlock* MyTextBlock35{ nullptr };
unsigned long long MyNumber = 0;
private:
NS_IMPLEMENT_INLINE_REFLECTION_(MainWindow, Window, "HelloWorld.MainWindow")
};
Code: Select all
<Window
x:Class="HelloWorld.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:HelloWorld"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="NoesisGUI - Hello, World!"
Background="#FF124C7A"
FontFamily="./#Aero Matics"
FontSize="36"
mc:Ignorable="d">
<Window.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="12" />
<Setter Property="TextAlignment" Value="Center" />
</Style>
<DataTemplate x:Key="MyCustomElementDataTemplate">
<StackPanel Margin="10">
<TextBlock Name="MyTextBlock1" Text="Test 1" />
<TextBlock Name="MyTextBlock2" Text="Test 2" />
<TextBlock Name="MyTextBlock3" Text="Test 3" />
<TextBlock Name="MyTextBlock4" Text="Test 4" />
<TextBlock Name="MyTextBlock5" Text="Test 5" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid Width="600" Height="480">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentControl Grid.Row="0" Grid.Column="0" x:Name="MyCustomElementContentControl1" ContentTemplate="{StaticResource MyCustomElementDataTemplate}" />
<ContentControl Grid.Row="0" Grid.Column="1" x:Name="MyCustomElementContentControl2" ContentTemplate="{StaticResource MyCustomElementDataTemplate}" />
<ContentControl Grid.Row="1" Grid.Column="0" x:Name="MyCustomElementContentControl3" ContentTemplate="{StaticResource MyCustomElementDataTemplate}" />
<ContentControl Grid.Row="1" Grid.Column="1" x:Name="MyCustomElementContentControl4" ContentTemplate="{StaticResource MyCustomElementDataTemplate}" />
</Grid>
</Window>
Code: Select all
struct MyComplexComponent
{
TextBlock* MyTextBlock1{ nullptr };
TextBlock* MyTextBlock2{ nullptr };
TextBlock* MyTextBlock3{ nullptr };
TextBlock* MyTextBlock4{ nullptr };
TextBlock* MyTextBlock5{ nullptr };
};
class MainWindow final : public Window
{
public:
MainWindow()
{
Initialized() += MakeDelegate(this, &MainWindow::OnInitialized);
Loaded() += MakeDelegate(this, &MainWindow::OnLoaded);
}
private:
void OnLoaded(Noesis::BaseComponent* /*sender*/, const Noesis::RoutedEventArgs& /*e*/)
{
GetDisplay()->Render() += [this](NoesisApp::Display* /*_display*/)
{
MyNumber++;
component1.MyTextBlock1->SetText(std::to_string(MyNumber).data());
component2.MyTextBlock1->SetText(std::to_string(MyNumber).data());
component3.MyTextBlock1->SetText(std::to_string(MyNumber).data());
component4.MyTextBlock1->SetText(std::to_string(MyNumber).data());
printf("Call %llu\n", MyNumber);
};
}
void InitializeComponent(MyComplexComponent* component, const char* name)
{
ContentControl* cc = FindName<ContentControl>(name);
DataTemplate* dp = cc->GetContentTemplate();
component->MyTextBlock1 = DynamicCast<Noesis::TextBlock*>(dp->FindName("MyTextBlock1"));
component->MyTextBlock2 = DynamicCast<Noesis::TextBlock*>(dp->FindName("MyTextBlock2"));
component->MyTextBlock3 = DynamicCast<Noesis::TextBlock*>(dp->FindName("MyTextBlock3"));
component->MyTextBlock4 = DynamicCast<Noesis::TextBlock*>(dp->FindName("MyTextBlock4"));
component->MyTextBlock5 = DynamicCast<Noesis::TextBlock*>(dp->FindName("MyTextBlock5"));
}
void OnInitialized(BaseComponent*, const Noesis::EventArgs&)
{
SetDataContext(this);
InitializeComponent(&component1, "MyCustomElementContentControl1");
InitializeComponent(&component2, "MyCustomElementContentControl2");
InitializeComponent(&component3, "MyCustomElementContentControl3");
InitializeComponent(&component4, "MyCustomElementContentControl4");
}
MyComplexComponent component1;
MyComplexComponent component2;
MyComplexComponent component3;
MyComplexComponent component4;
unsigned long long MyNumber = 0;
private:
NS_IMPLEMENT_INLINE_REFLECTION_(MainWindow, Window, "HelloWorld.MainWindow")
};
1 - The "MyTextBlock1/2/3/4" pointer is the same between all 4 structs.
The MyTextBlock1 pointer is the same in all structs.
The MyTextBlock2 pointer is the same in all structs, and so on.
I was wondering why this happens, and I came to the conclusion that when I do FindName<ContentControl>(name)->GetContentTemplate();
I'm getting the template itself, and not the elements created from the template within the ContentControl, so I always get the same pointer regardless of which ContentControl I indicate.
Would it be this? Because if I do it in my OnInitialized():
component1.MyTextBlock1->SetText("My Test Text");
It sets the text in MyTextBlock1 of all 4 elements.
So how do I actually get the element inside my ContentControl that was created by the DataTemplate?
It's possible?
2 - Why after I used ContentControl, my Render() loop no longer changes the text?
Could it be because of what I explained above? Does it take the element pointer in the template, and not the one created in the final element, so it changes the text in it and is not actually applied to the created element?
Thanks.