Drive ContentControl DataTemplate from a model data string
Posted: 21 Oct 2024, 12:01
Hey, I wanted to create a menu screen system, which shows the content for a screen based on that screen being the current one. Trailing NoesisGUI-UE5.3-3.2.4 Unreal Plug-in.
I've seen this forum posts describing the 3 methods of doing similar are: using visibility (Menu3D), ContentControl's & Data Templates (LayeredUI), or manually loading the xaml. I've proved each works ok, but I'd prefer to only load elements when required, and also to do so in a data driven way (so not with a code class that says with xaml it loads).
I've created this setup, and wanted to use model data to define a string (DataTemplateName), to look-up which data template it should show (for now these data templates are just text, but later I imagine they could be complex screens, defined in a separate resource dictionary & xamls).
Xaml:
C++ Model Data with a String:
My first question is whether it's possible to look-up a template using a string from a model, to match it's key name? Is there any special bind syntax for it? I've not seen a way to do that yet, so I tried making my own converter for it. This all looks to work in code (I see it finds the DataTemplate and returns it), but doesn't show. Do you know what I might be doing wrong? It's probably something simple I've not quite understood about how templates work perhaps?
Converter to convert String -> DataTemplate:
Result:

If this approach is completely wrong, please just let me know. My issue with the LayeredUI method, was that it requires a C++ class for the model object and data template to work, which feels a bit excessive & hard-coded, when you start having lots of screens.
Thanks, appreciate any help or feedback.
I've seen this forum posts describing the 3 methods of doing similar are: using visibility (Menu3D), ContentControl's & Data Templates (LayeredUI), or manually loading the xaml. I've proved each works ok, but I'd prefer to only load elements when required, and also to do so in a data driven way (so not with a code class that says with xaml it loads).
I've created this setup, and wanted to use model data to define a string (DataTemplateName), to look-up which data template it should show (for now these data templates are just text, but later I imagine they could be complex screens, defined in a separate resource dictionary & xamls).
Xaml:
Code: Select all
<Page.Resources>
<ResourceDictionary>
<DataTemplate x:Key="MyDataTemplate">
<TextBlock Text="MyDataTemplate" FontSize="30" Foreground="#00FFFF"/>
</DataTemplate>
<DataTemplate x:Key="MyDataTemplate2">
<TextBlock Text="MyDataTemplate2" FontSize="30" Foreground="#00FFFF"/>
</DataTemplate>
<local:UserControlTemplateConverter x:Key="ControlConverter" />
</ResourceDictionary>
</Page.Resources>
<Grid>
<StackPanel>
<!-- Content Controls with just static resources (all working ok) -->
<TextBlock Text="Static Resources: " Foreground="Red" />
<ContentControl ContentTemplate="{StaticResource MyDataTemplate}"/>
<ContentControl Content="{StaticResource MyDataTemplate}"/>
<ContentControl ContentTemplate="{StaticResource MyDataTemplate2}"/>
<ContentControl Content="{StaticResource MyDataTemplate2}"/>
<TextBlock Text="Model String: " Foreground="Red" />
<!-- Doesn't work, as trying to use a string as the template. Is there any syntax that can do that? -->
<ContentControl ContentTemplate="{Binding Path=DataTemplateName}"/>
<TextBlock Text="Model String using a converter: " Foreground="Red" />
<!-- I thought this should work, but it doesn't, nothing shows. What's wrong? -->
<ContentControl ContentTemplate="{Binding Path=DataTemplateName, Converter={StaticResource ControlConverter}}"/>
<ContentControl Content="{Binding Path=DataTemplateName, Converter={StaticResource ControlConverter}}"/>
Code: Select all
UCLASS(Blueprintable)
class UNoesisUserControlModelData : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString DataTemplateName = TEXT("MyDataTemplate");Converter to convert String -> DataTemplate:
Code: Select all
bool TryConvert(Noesis::BaseComponent* value, const Noesis::Type* targetType,
Noesis::BaseComponent* parameter, Noesis::Ptr<Noesis::BaseComponent>& result) override
{
Noesis::String TemplateName;
if (Noesis::Boxing::CanUnbox<Noesis::String>(value)) TemplateName = Noesis::Boxing::Unbox<Noesis::String>(value);
else return false;
Noesis::BaseComponent* BaseComponent = FindNodeResource(TemplateName.Str(), true); // I've also tested doing Noesis::DynamicCast<Noesis::DataTemplate*> returns a valid class.
if (BaseComponent)
{
result = Noesis::Boxing::Box(BaseComponent);
return true;
}
return false;
}
If this approach is completely wrong, please just let me know. My issue with the LayeredUI method, was that it requires a C++ class for the model object and data template to work, which feels a bit excessive & hard-coded, when you start having lots of screens.
Thanks, appreciate any help or feedback.