3

This is my MainWindow code, which contains a TabControl:

<Window x:Class="TabControlNS.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:TabControlNS"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TabControl Visibility="Visible">
            <TabItem Header="Item2">
            </TabItem>
            <TabItem Header="Item1">
                <ContentControl Loaded="ContentControl_Loaded">
                    <local:View/>
                </ContentControl>        
            </TabItem>              
            <TabItem Header="Item3"/>
            <TabItem Header="Item4"/>
        </TabControl>

    </Grid>
</Window>

<UserControl x:Class="TabControlNS.View"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:TabControlNS"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid x:Name="grid">

    </Grid>
</UserControl>

And here's the code behind:

public partial class View : UserControl
{
    public System.Windows.Forms.Integration.WindowsFormsHost host;
    public View()
    {
        InitializeComponent();
        IsKeyboardFocusWithinChanged += View_IsKeyboardFocusWithinChanged;

        host = new System.Windows.Forms.Integration.WindowsFormsHost();
        var mtbDate = new System.Windows.Forms.MaskedTextBox("00/00/0000");
        grid.Children.Add(host);
        this.Loaded += View_Loaded;
        this.Unloaded += View_Unloaded;
    }

    private double LoadMinusUnloadCount = 0;

    private void View_Loaded(object sender, RoutedEventArgs e)
    {
        LoadMinusUnloadCount++;   
    }

    private void View_Unloaded(object sender, RoutedEventArgs e)
    {
        LoadMinusUnloadCount--;
    }

    private void View_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        Console.WriteLine($"View.IsKeyboardFocusedWithin = {IsKeyboardFocusWithin}");
    }
}

Since the local:View control is embedded inside "Item1", then View_Loaded will be called twice ( once when the Window is starting up, another when the "Item1" is coming to view). No Unloaded event has been called in between.

According to Same Bent of Microsoft, this is clearly a bug

I encourage you to report bugs about things that are clearly wrong, such as

  • Unloaded events with no matching Loaded
  • Two Loaded events with no intervening Unloaded
  • Element clearly visible and active but no Loaded

Or is it? Can Microsoft make such an elementary bug with such a control? How to explain this?

Graviton
  • 81,782
  • 146
  • 424
  • 602
  • worth looking at: https://stackoverflow.com/questions/3421303/loaded-event-of-a-wpf-user-control-fire-two-times – Bizhan Jun 04 '18 at 11:32
  • 1
    When a TabItem is added explicitly, it's indeed loaded immediately. This shouldn't happen if you use the ItemsSource property though. – mm8 Jun 04 '18 at 11:44

1 Answers1

4

I'm not sure if it's a bug, but there are really several controls in WPF behaviors like this.

The controls that will raise Loaded twice without Unloaded are:

  • TabControl
  • Popup

When the controls are added to the visual tree, the Loaded event will be raised to all of its children. And when the child shows or opens, the Loaded event will raise again. When the child closes, the Unloaded event will raise once. And when the TabControl or Popup was removed from the visual tree, the Unloaded event will be raised again.


When a tab control is added to a visual tree:

  • TabControl Loaded -> TabItem0 Loaded -> TabItem1 Loaded -> …

When the tab control changed tab:

  • Old Active Tab Unloaded -> New Active Tab Loaded

This means that all TabItems will be Loaded twice and Unloaded twice.

The Popup has a similar behavior.

When a popup is added to a visual tree:

  • Popup Loaded -> Child Loaded

When the popup is opened:

  • Child Loaded

When the popup is closed:

  • Child Unloaded

When the popup is Removed from a visual tree:

  • Popup Unloaded

But if the popup is opened and is removed from a visual tree:

  • Popup Unload -> Child Unloaded (This may be not very expected.)

Actually, I believe that WPF has many bugs. My friends and I post them on GitHub:

walterlv
  • 2,366
  • 13
  • 73