-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
- .NET Core Version: 5.0.301
- Windows version: 10 (20H2)
- Does the bug reproduce also in WPF for .NET Framework 4.8?: Yes
Problem description:
ContentControl doesn't update ControlTemplate when moved to another container (parent changes). Is it a bug or the correct action?
Minimal repro:
I have two StackPanels. Each of them has defined a DataTemplate of the same LineItem type. When I move ContentControl from one to the other container I suspect that the view of the items will change. But it doesn't happen.
Create "WPF App (.NET 5)" or "WPF App (.NET Framework)" project and override MainWindow files:
<Window x:Class="WpfApp7core.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp7core"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="600">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Column 1" HorizontalAlignment="Center"/>
<TextBlock Text="Column 2" HorizontalAlignment="Center" Grid.Column="1"/>
<StackPanel x:Name="column1" Grid.Column="0" Grid.Row="1">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type local:LineItem}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="4,0"/>
<TextBlock Text="{Binding Value}" Foreground="Blue"/>
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<!-- The place where new ContentControls are added. -->
</StackPanel>
<StackPanel x:Name="column2" Grid.Column="1" Grid.Row="1">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type local:LineItem}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Margin="4,0"/>
<TextBlock Text="{Binding Value}" Foreground="Green"/>
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<!-- The place where new ContentControls are added. -->
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.ColumnSpan="2">
<Button Content=" Create in 1 " Click="Create1_Click"/>
<Button Content=" Create in 2 " Click="Create2_Click"/>
<Button Content=" Move to > " Click="MoveTo2_Click"/>
<Button Content=" Move to < " Click="MoveTo1_Click"/>
</StackPanel>
</Grid>
</Window>namespace WpfApp7core
{
public class LineItem
{
public string Name { get; set; }
public string Value { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Create1_Click(object sender, RoutedEventArgs e)
{
var li = new LineItem { Name = "Left", Value = Guid.NewGuid().ToString() };
column1.Children.Add(new ContentControl { Content = li });
}
private void Create2_Click(object sender, RoutedEventArgs e)
{
var li = new LineItem { Name = "Right", Value = Guid.NewGuid().ToString() };
column2.Children.Add(new ContentControl { Content = li });
}
private void MoveTo2_Click(object sender, RoutedEventArgs e)
{
foreach (FrameworkElement item in Enumerable.Range(0, column1.Children.Count).Select(i => column1.Children[i]).Cast<FrameworkElement>().ToList())
{
RemoveFromParent(item); // Remove from column1
column2.Children.Add(item); // Add to column2
}
}
private void MoveTo1_Click(object sender, RoutedEventArgs e)
{
foreach (FrameworkElement item in Enumerable.Range(0, column2.Children.Count).Select(i => column2.Children[i]).Cast<FrameworkElement>().ToList())
{
RemoveFromParent(item); // Remove from column2
column1.Children.Add(item); // Add to column1
}
}
public static void RemoveFromParent(FrameworkElement child)
{
switch (child.Parent)
{
case Panel panel: panel.Children.Remove(child); break;
case Decorator decorator: decorator.Child = null; break;
case ContentControl contentControl: contentControl.Content = null; break;
case ContentPresenter contentPresenter: contentPresenter.Content = null; break;
default: throw new Exception($"Unknown parent type {child.Parent.GetType()}");
}
}
}
}Expected behaviour:
Each items on the left should be blue, and each on the right should be green.
Unwanted solution:
I found one solution. When I assign Content to null and then assign the previous value, ContentControl will update its view.
foreach (FrameworkElement item in Enumerable.Range(0, column1.Children.Count).Select(i => column1.Children[i]).Cast<FrameworkElement>().ToList())
{
RemoveFromParent(item);
column2.Children.Add(item);
if (item is ContentControl cc)
{
var prev = cc.Content;
cc.Content = null;
cc.Content = prev;
}
}But this solution becomes complicated when you have an extensive view tree.
