Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ContentPresenter should not become a Parent #4894

Open
11 of 24 tasks
MartinZikmund opened this issue Jan 13, 2021 · 1 comment
Open
11 of 24 tasks

ContentPresenter should not become a Parent #4894

MartinZikmund opened this issue Jan 13, 2021 · 1 comment
Assignees
Labels
difficulty/challenging 🤯 Categorizes an issue for which the difficulty level is reachable with internals understanding kind/bug Something isn't working project/layout 🧱 Categorizes an issue or PR as relevant to layouting and containers (Measure/Arrange, Collections,..)

Comments

@MartinZikmund
Copy link
Member

MartinZikmund commented Jan 13, 2021

Current behavior

When Content of ContentPresenter is set, the ContentPresenter becomes its parent, hence the content then inherits ContentPresenter's DataContext.

Expected behavior

  • ContentPresenter should not become anybody's parent
  • Even then, if the content is already part of the visual tree, setting the ContentPresenter.Content will throw an exception (as the control is already displayed)
  • ContentPresenter.Content should not inherit DataContext after being set (and should retain its pre-existing DataContext if such exists), but should inherit it if the DataContext changes later while it is set 🤪
  • ContentPresenter's content should still be part of its visual tree (discoverable via VisualTreeHelper

How to reproduce it (as minimally and precisely as possible)

TestControl.cs:

public class TestControl : ContentControl
{
}

MainPage.xaml:

<Page.Resources>
    <Style TargetType="local:TestControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:TestControl">
                    <Grid></Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>
<StackPanel>
    <Button Click="Move_Click">Move to ContentPresenter</Button>
    <Button Click="ChangeDataContext_Click">Change ContentPresenter's DataContext</Button>
    <Button Click="CheckParent_Click">Check Parent</Button>
    <Button Click="SetNewButton_Click">Set new content</Button>
    <local:TestControl x:Name="ContentOwner">
        <local:TestControl.Content>
            <Button Content="{Binding}" x:Name="OwnedButton" />
        </local:TestControl.Content>
    </local:TestControl>
    <ContentPresenter x:Name="Presenter" />
</StackPanel>

MainPage.xaml.cs:

public sealed partial class MainPage : Page
{
    public MainPage()
    {            
        this.InitializeComponent();
        OwnedButton.DataContextChanged += OwnedButton_DataContextChanged;
        ContentOwner.DataContext = "Owner DataContext";
        Presenter.DataContext = "Presenter DataContext";
    }

    private void OwnedButton_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
    {
        System.Diagnostics.Debug.WriteLine("Button's data context is now " + args.NewValue);
    }

    private void Move_Click(object sender, RoutedEventArgs e)
    {
        Presenter.Content = OwnedButton;
    }

    private void CheckParent_Click(object sender, RoutedEventArgs e)
    {
        if (VisualTreeHelper.GetChildrenCount(Presenter) > 0)
        {
            var child = VisualTreeHelper.GetChild(Presenter, 0);
            System.Diagnostics.Debug.WriteLine($"ContentPresenter contains {(child as FrameworkElement).Name}." +
                         " Its parent is {(child as FrameworkElement)?.Parent}");
        }
    }

    private async void SetNewButton_Click(object sender, RoutedEventArgs e)
    {
        var button = new Button() { Name = "CodeBehindButton", Content = "Code-behind button" };
        Presenter.Content = button;
    }

    private void ChangeDataContext_Click(object sender, RoutedEventArgs e)
    {
        Presenter.DataContext = Guid.NewGuid().ToString();
    }
}

Click around the sample and note the debug output. This should show for example that the data context of the button does not change when the button is moved to the content presenter, but it does change if the data context of the content presenter is updated afterwards. Also note that I still had to modify the template of the TestControl not to have the Content in the visual tree.

Workaround

When setting the ContentPresenter.Content, also set its DataContext to the same value as your content should have. This way it will inherit it normally.

Environment

Nuget Package:

  • Uno.UI / Uno.UI.WebAssembly / Uno.UI.Skia
  • Uno.WinUI / Uno.WinUI.WebAssembly / Uno.WinUI.Skia
  • Uno.SourceGenerationTasks
  • Uno.UI.RemoteControl / Uno.WinUI.RemoteControl
  • Other:

Nuget Package Version(s):

Affected platform(s):

  • iOS
  • Android
  • WebAssembly
  • WebAssembly renderers for Xamarin.Forms
  • macOS
  • Skia
    • WPF
    • GTK (Linux)
    • Tizen
  • Windows
  • Build tasks
  • Solution Templates

IDE:

  • Visual Studio 2017 (version: )
  • Visual Studio 2019 (version: )
  • Visual Studio for Mac (version: )
  • Rider Windows (version: )
  • Rider macOS (version: )
  • Visual Studio Code (version: )

Relevant plugins:

  • Resharper (version: )

Anything else we need to know?

@MartinZikmund MartinZikmund added kind/bug Something isn't working triage/untriaged Indicates an issue requires triaging or verification project/layout 🧱 Categorizes an issue or PR as relevant to layouting and containers (Measure/Arrange, Collections,..) and removed triage/untriaged Indicates an issue requires triaging or verification labels Jan 13, 2021
MartinZikmund added a commit to MartinZikmund/Uno that referenced this issue Jan 13, 2021
- Workaround for unoplatform#4894 in TabView - sets the ContentPresenter's DataContext so that it matches the DataContext of the TabViewItem.
MartinZikmund added a commit to MartinZikmund/Uno that referenced this issue Jan 17, 2021
- Workaround for unoplatform#4894 in TabView - sets the ContentPresenter's DataContext so that it matches the DataContext of the TabViewItem.
MartinZikmund added a commit to MartinZikmund/Uno that referenced this issue Jan 19, 2021
- Workaround for unoplatform#4894 in TabView - sets the ContentPresenter's DataContext so that it matches the DataContext of the TabViewItem.
@jeromelaban jeromelaban added the difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. label Feb 15, 2021
@MartinZikmund MartinZikmund added difficulty/challenging 🤯 Categorizes an issue for which the difficulty level is reachable with internals understanding and removed difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. labels Jun 2, 2021
@MartinZikmund MartinZikmund changed the title ContentPresenter should not become a Parent ContentPresenter should not become a Parent Jul 10, 2023
@Youssef1313
Copy link
Member

Youssef1313 commented Dec 10, 2023

We should also verify the behavior for this scenario:

<ContentPresenter DataContext="42">
    <ContentPresenter.ContentTemplate>
        <DataTemplate>
	    <TextBlock Text="{Binding}" />
	</DataTemplate>
    </ContentPresenter.ContentTemplate>
</ContentPresenter>

(Verify whether ContentPresenter becomes the parent of TextBlock or not)
Note that in the above scenario, TextBlock may inherit DataContext from ContentPresenter incorrectly, then it clears it back on Loaded, which will make the TextBlock appears empty as in WinUI. However, this is still a problem because DataContext shouldn't have been inherited in the first place, and the Text should be empty before Loaded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty/challenging 🤯 Categorizes an issue for which the difficulty level is reachable with internals understanding kind/bug Something isn't working project/layout 🧱 Categorizes an issue or PR as relevant to layouting and containers (Measure/Arrange, Collections,..)
Projects
None yet
Development

No branches or pull requests

3 participants