ToggleTransitionPresenter destroys DataContext

WPF Studio, Themes, and Shared Library for WPF Forum

Posted 13 years ago by Markus Springweiler
Version: 11.1.0545
Platform: .NET 4.0
Environment: Windows 7 (64-bit)
Avatar
Having upgraded from 541 to 545 every single place where I use a ToggleTransitionPresenter now has broken bindings because the DataContext of Content and AlternateContent doesn't point to the surrounding DataContext anymore but the respective control, which is very wrong.

Comments (10)

Posted 13 years ago by Markus Springweiler
Avatar
Even the binding to IsAlternateContentVisible doesn't work because the DataContext already is wrong.
Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Markus,

We did have one other report of an issue with the ToggleTransitionPresenter data context "breaking". We never received a reply though, so we were no able to determine the problem.

The last change to the ToggleTransitionPresenter control was made in build 0543, which removed a call to UpdateLayout of an inner ContentPresenter (which shouldn't affect the DataContext).

There was another change to it's base class TransitionPresenter, which corrected an exception of "visual already has a parent". The TransPresenter is a ContentPresenter itself, but also leverages two internal ContentPresenters. The TransPresenter's content is effectively passed down and presented by one of these inner ContentPresenters.

The issue that was corrected was that TransPresenter would create the controls from the associated DataTemplate, even though it would never be used. In addition, the inner ContentPresenter would create the DataTemplate and actually present it. If the content is a visual (that just happens to be presented via a DataTemplate), this could result in a "visual already has a parent" exception.

Taking a simple test like so:
<StackPanel DataContext="ContextMain">
    <StackPanel.Resources>
        <DataTemplate x:Key="MyTemplate">
            <TextBlock Text="{Binding}" />
        </DataTemplate>
    </StackPanel.Resources>
    <ToggleButton x:Name="toggle" Content="Toggle Content" />

    <ContentPresenter Content="ContextOne" ContentTemplate="{StaticResource MyTemplate}" />
    <ContentPresenter DataContext="ContextTwo" Content="{Binding}" ContentTemplate="{StaticResource MyTemplate}" />
    <ContentPresenter DataContext="ContextThree" Content="{Binding DataContext, RelativeSource={RelativeSource Self}}" ContentTemplate="{StaticResource MyTemplate}" />
    <ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource MyTemplate}" />

    <Separator Height="1" HorizontalAlignment="Stretch" />
        
    <shared:ToggleTransitionPresenter IsAlternateContentVisible="{Binding IsChecked, ElementName=toggle}"
                                        Content="ContextOne" ContentTemplate="{StaticResource MyTemplate}"
                                        AlternateContent="AltContextOne" AlternateContentTemplate="{StaticResource MyTemplate}" />
    <shared:ToggleTransitionPresenter IsAlternateContentVisible="{Binding IsChecked, ElementName=toggle}"
                                        DataContext="ContextTwo" 
                                        Content="{Binding}" ContentTemplate="{StaticResource MyTemplate}"
                                        AlternateContent="{Binding}" AlternateContentTemplate="{StaticResource MyTemplate}" />
    <shared:ToggleTransitionPresenter IsAlternateContentVisible="{Binding IsChecked, ElementName=toggle}"
                                        DataContext="ContextThree" 
                                        Content="{Binding DataContext, RelativeSource={RelativeSource Self}}" ContentTemplate="{StaticResource MyTemplate}"
                                        AlternateContent="{Binding DataContext, RelativeSource={RelativeSource Self}}" AlternateContentTemplate="{StaticResource MyTemplate}" />
    <shared:ToggleTransitionPresenter IsAlternateContentVisible="{Binding IsChecked, ElementName=toggle}"
                                        Content="{Binding}" ContentTemplate="{StaticResource MyTemplate}"
                                        AlternateContent="{Binding}" AlternateContentTemplate="{StaticResource MyTemplate}" />
    <shared:ToggleTransitionPresenter IsAlternateContentVisible="{Binding IsChecked, ElementName=toggle}"
                                        Content="{Binding}" ContentTemplate="{StaticResource MyTemplate}"
                                        AlternateContent="AltContextFive" AlternateContentTemplate="{StaticResource MyTemplate}" />
</StackPanel>
Using the latest build, the output is:

Quote:
ContextOne
ContextMain
ContextThree
ContextMain
------------------------
ContextOne
ContextMain
ContextThree
ContextMain
ContextMain


If you press the toggle button, then you end up with:

Quote:
ContextOne
ContextMain
ContextThree
ContextMain
------------------------
AltContextOne
ContextMain
ContextThree
ContextMain
AltContextFive


So that works as expected as it matches how the ContentPresenter works and changes the content appropriately when toggled. If I use the version from build 0541 then I see the exact same results.

If you can please put together a small sample project that reproduces your issue and email it over then we can take a closer look. Be sure to remove any executables or change the extension of the zip file to ensure it gets past our email filters.


Actipro Software Support

Posted 13 years ago by Markus Springweiler
Avatar
Here you go: The following worked perfectly well for the last 2 years including 541, but now is entirely broken, including (as already written) the binding on IsAlternateContentVisible not working (all bindings throw binding failures in VS output window).
<Window x:Class="ToogleTransitionPresenterBug.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Mail="clr-namespace:System.Net.Mail;assembly=System"
        xmlns:shared="http://schemas.actiprosoftware.com/winfx/xaml/shared">
    <Window.DataContext>
        <!--since MailMessage does not implement INotifyPropertyChanged please just change IsBodyHtml here
        (and restart or just watch the designer preview changing after recompile) -->
        <Mail:MailMessage Body="this is the body" Subject="and here the subject" IsBodyHtml="True" />
    </Window.DataContext>

    <shared:ToggleTransitionPresenter IsAlternateContentVisible="{Binding IsBodyHtml}">
        <shared:ToggleTransitionPresenter.AlternateContent>
            <Border Background="Red">
                <TextBlock Text="{Binding Subject}" VerticalAlignment="Center" TextAlignment="Center" />
            </Border>
        </shared:ToggleTransitionPresenter.AlternateContent>

        <Border Background="Green">
            <TextBlock Text="{Binding Body}" VerticalAlignment="Center" TextAlignment="Center" />
        </Border>
    </shared:ToggleTransitionPresenter>
</Window>
Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Markus,

I apologize for the delay, our colo has been down most of the day.

Thanks for the sample. I was able to track down the issue, which was related to our fix for the "visual already has a parent" exception. The fix for the exception was to force the TransitionPresenter/ToggleTransitionPresenter to use an empty DataTemplate. Since this DataTemplate was never actually used, it had no visual affect. This change was made in an override for the ContentPresenter.ChooseTemplate method.

The ContentPresenter class (which is the based class of TransitionPresenter and ToggleTransitionPresenter) will actually change the DataContext based on the DataTemplate selected (based on ChooseTemplate). The code effectively looks like:
template = this.ChooseTemplate();
if (template != UIElementContentTemplate)
    base.DataContext = this.Content;
else
    base.ClearValue(FrameworkElement.DataContextProperty);
Since we changed ChooseTemplate to always return an empty DataTemplate, it forced this code to always fall into the if statement. In previous builds, your sample would fall into the else statement. With the DataContext cleared, it was free to inherit it's data context from it's parent. So the key to the DataContext breaking change is when Content is a visual, which is when UIElementContentTemplate.

I've updated the code so that if UIElementContentTemplate is returned from ContentPresenter.ChooseTemplate, then the TransitionPresenter and ToggleTransitionPresenter will use that. Otherwise it will continue to use the empty DataTemplate. This still works around the "visual already has a parent" exception, but still allows it to inherit the DataContext when the Content is a visual.

This fix will be included in the next maintenance release. The only work around until then would be to use DataTemplates in your sample, like so:
<shared:ToggleTransitionPresenter Content="{Binding}" AlternateContent="{Binding}" IsAlternateContentVisible="{Binding IsBodyHtml}">
    <shared:ToggleTransitionPresenter.ContentTemplate>
        <DataTemplate>
            <Border Background="Green">
                <TextBlock Text="{Binding Body}" VerticalAlignment="Center" TextAlignment="Center" />
            </Border>
        </DataTemplate>
    </shared:ToggleTransitionPresenter.ContentTemplate>
    <shared:ToggleTransitionPresenter.AlternateContentTemplate>
        <DataTemplate>
            <Border Background="Red">
                <TextBlock Text="{Binding Subject}" VerticalAlignment="Center" TextAlignment="Center" />
            </Border>
        </DataTemplate>
    </shared:ToggleTransitionPresenter.AlternateContentTemplate>
</shared:ToggleTransitionPresenter>
Sorry for any inconvenience.


Actipro Software Support

Posted 13 years ago by Markus Springweiler
Avatar
While this at first sight looked like a smooth workaround, I can't use it everywhere because some times there exist Controls with names which are referenced by code: Surrounding the Control with <DataTemplate> breaks this.

Quote:
This fix will be included in the next maintenance release.

Is there a rough time estimate for this?
Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Markus,

Unfortunately, we just released a maintenance release and the next version will likely be 2011.2, but that is still several weeks out.


Actipro Software Support

Posted 13 years ago by Markus Springweiler
Avatar
Your wizard control suffers the same problem: Inactive pages are bound to itself and therefore are flooding VS debug output with binding errors AND indirectly doing changes to my viewmodel while switching pages because of this.

I'm not willing to wait for my subscription to expire while there exist such breaking changes (=bugs).
Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Markus,

We have ported the change back to 2011.1 and uploaded a revised private build of 545 that has it. If you write our support address we can give you the URL to that private build that will tide you over until 2011.2.


Actipro Software Support

Posted 13 years ago by Markus Springweiler
Avatar
Thank you, very obliging.

Does this include the wizard problems or should I create a separate bug report in the according forum?
Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
This fixes both since the Wizard uses the TransitionPresenter internally, so no need to create a separate bug report.


Actipro Software Support

The latest build of this product (v24.1.1) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.