Susanna.Rowland
Topic Author
Posts: 11
Joined: 07 Apr 2021, 15:20

ContentTemplate binding with a converter is not displaying the content

12 Aug 2021, 17:01

I have a ContentPresenter and need to use a converter on the ContentTemplate binding. This converter returns a DataTemplate retrieved from a ResourceDictionary. It works fine in C#, but in C++ it is just displaying the viewmodel class name as text instead of the expected content. If I set the DataTemplate directly in the xaml, it works, so it seems there is an issue where the content is not being loaded correctly where a binding and converter is used.
<ContentPresenter Width="200" Height="200" Content="{Binding}" ContentTemplate="{Binding Data, Converter={StaticResource DataConverter}}"/>
Is this a known issue? I couldn't find anything that seemed to match in the bug tracker. If not, I will add a ticket for it.
Any suggestions on how to fix/workaround this issue would be greatly appreciated.
 
User avatar
sfernandez
Site Admin
Posts: 2793
Joined: 22 Dec 2011, 19:20

Re: ContentTemplate binding with a converter is not displaying the content

12 Aug 2021, 21:37

I made a simple test and the converter seems to work as expected and the ContentPresenter receives the corresponding template:
struct DataConverter: public BaseValueConverter
{
    Ptr<DataTemplate> t;
    DataConverter() t(MakePtr<DataTemplate>()) { }

    bool TryConvert(BaseComponent*, const Type*, BaseComponent*, Ptr<BaseComponent>& result) override
    {
        result = t;
        return true;
    }

    NS_IMPLEMENT_INLINE_REFLECTION_(DataConverter, BaseValueConverter, "Testing.DataConverter")
};
Is the DataConverter even called in your case?
Is the Data property exposed in the bound view model?
 
Susanna.Rowland
Topic Author
Posts: 11
Joined: 07 Apr 2021, 15:20

Re: ContentTemplate binding with a converter is not displaying the content

13 Aug 2021, 11:34

Yes the DataProperty is bound, and the converter's TryConvert is called, sets the result and returns true. I have stepped through the code at run time to confirm this is definitely the case.
Below is my TryConvert function. m_Resources is an exposed property for the resource dictionary we wish to search.
bool C_DataConverter::TryConvert(Noesis::BaseComponent* value,
									Noesis::Type const*,
									Noesis::BaseComponent*,
									Noesis::Ptr<BaseComponent>& result)
{
	Noesis::String resourceKey = Noesis::Boxing::Unbox<Noesis::String>(value);

	if ((!resourceKey.Empty()) && (m_Resources != nullptr) && (m_Resources->Contains(resourceKey.Str())))
    	{
		if (m_Resources->Find(resourceKey.Str(), result))
		{
			return true;
		}
	}
	return false;
}
 
User avatar
sfernandez
Site Admin
Posts: 2793
Joined: 22 Dec 2011, 19:20

Re: ContentTemplate binding with a converter is not displaying the content

13 Aug 2021, 17:30

I'm not able to reproduce the problem, in my case the DataTemplate is correctly applied to the ContentPresenter.ContentTemplate and then used to render the content.

Could you please attach our Inspector tool and see if the ContentPresenter has a valid template assigned to the ContentTemplate property.
 
Susanna.Rowland
Topic Author
Posts: 11
Joined: 07 Apr 2021, 15:20

Re: ContentTemplate binding with a converter is not displaying the content

16 Aug 2021, 15:06

I have checked using the inspector, and I can see it is using the default template rather than the content template provided by the converter.
From what I can see stepping through the code, the data context for the content presenter is not getting set until part way through OnPreApplyTemplate, after it has checked for a content template, found nothing and resorted to the default template. Only when the data context is set, does it call the converter and set the content template, so this is occurring too late to be used in this update. The trouble is, because this is all triggered as part of the Measure function call, the attempt to request another measure to update the view fails, and it doesn't try again, so the content template is never displayed.
If I modify ContentPresenter.cpp to move the call to set the data context up to before we attempt to retrieve the content template (in OnPreApplyTemplate), everything works as expected and I see my content.
I'm not sure how it could have worked for you with this setup, unless you were somehow setting the data context sooner, or managing to trigger a second call to measure?

Here is the diff so you can see what I mean:
@@ -246,6 +246,9 @@
 
         DataTemplate* oldDataTemplate = static_cast<DataTemplate*>(GetFrameworkTemplate());
 
+        // set DataContext to the content, so template can bind to properties of the content
+        SetDataContext(content);
+
         DataTemplate* dataTemplate = GetContentTemplate();
         if (dataTemplate == 0)
         {
@@ -278,8 +281,6 @@
         {
             // ContentTemplate[Selector] or implicit template used
 
-            // set DataContext to the content, so template can bind to properties of the content
-            SetDataContext(content);
             mFlags.localDataContext = true;
 
             if (dataTemplate != oldDataTemplate)
@@ -316,8 +317,6 @@
                     SetSingleVisualChild(0);
                 }
 
-                // update DataContext with the latest content
-                SetDataContext(content);
                 mFlags.localDataContext = true;
 
                 EnsureDefaultTemplate();
 
User avatar
sfernandez
Site Admin
Posts: 2793
Joined: 22 Dec 2011, 19:20

Re: ContentTemplate binding with a converter is not displaying the content

16 Aug 2021, 18:02

It should work because the DataContext does not change, you are setting Content to {Binding} so the Data property resolves against the same context.

The code you proposed is not valid because DataContext should only be set by ContentPresenter when using a DataTemplate, otherwise it should inherit from the parent.
 
Susanna.Rowland
Topic Author
Posts: 11
Joined: 07 Apr 2021, 15:20

Re: ContentTemplate binding with a converter is not displaying the content

16 Aug 2021, 18:29

Then I will continue to investigate. In its simplest form, the issue I am having is that the DataTemplate is not being set until after the ContentProvider tries to use it.
If you have any ideas as to why this is happening, I would be very appreciative. I will update if I am able to find any leads.
 
User avatar
sfernandez
Site Admin
Posts: 2793
Joined: 22 Dec 2011, 19:20

Re: ContentTemplate binding with a converter is not displaying the content

16 Aug 2021, 18:47

I have to test one more thing, maybe this is happening when using that ContentPresenter inside another DataTemplate or ContentTemplate. Is that your case?
 
Susanna.Rowland
Topic Author
Posts: 11
Joined: 07 Apr 2021, 15:20

Re: ContentTemplate binding with a converter is not displaying the content

16 Aug 2021, 18:57

It is inside a ControlTemplate which is used to populate a ListBox, although I did try extracting ContentPresenter and having it independent of that, and I still got the same result so had assumed it wasn't related
 
Susanna.Rowland
Topic Author
Posts: 11
Joined: 07 Apr 2021, 15:20

Re: ContentTemplate binding with a converter is not displaying the content

17 Aug 2021, 13:19

I found a solution with no code changes! I needed to add
DataContext="{Binding}"
to the ContentPresenter xaml, otherwise it was only being set from the Content property after failing to get the template. I still think there is a bug here as it works correctly without this in blend, but at least this gives me a workaround.

For reference here are the 2 versions:
- Works in blend but not with Noesis
<ContentPresenter Width="200" Height="200" Content="{Binding}" ContentTemplate="{Binding Data, Converter={StaticResource DataConverter}}"/>
- Works with Noesis
<ContentPresenter Width="200" Height="200" DataContext="{Binding}" Content="{Binding}" ContentTemplate="{Binding Data, Converter={StaticResource DataConverter}}"/>

Who is online

Users browsing this forum: Ahrefs [Bot], Semrush [Bot] and 0 guests