0

In case this is relevant; I am using Caliburn.Micro and Castle.Windsor in my WPF application.

I have a LoginView with a LoginViewModel, which is launched on application startup without dramas. Then, once the user logs in, from within the LoginViewModel, I launch a SplashScreen, and the Main app view like so;

IoC.Get<IWindowManager>().ShowWindow(IoC.Get<SplashScreenViewModel>());

IoC.Get<IWindowManager>().ShowWindow(IoC.Get<MainViewModel>());

The first call works, the second throws an exception inside Caliburn.Micro where it calls;

Window.Show();

The InvalidOperationException message is: "Window must be the root of the tree. Cannot add Window as a child of Visual."

My understanding is that Window.Show() puts the Window at the root of the Visual Tree... also, more or less this same code is working fine in a prior revision of this codebase, but I've done some major refactoring in other areas, and now this has cropped up.

I'm guessing there's some fail in a related refactor around my use of Castle.Windsor or Caliburn.Micro but this error message is not helping me work out what that might be...

Any suggestions?

RJ Lohan
  • 6,497
  • 3
  • 34
  • 54

2 Answers2

0

I still cannot determine exactly what was causing this exception, but I have resolved the issue by modifying my (very broad) ViewModel component registrations from;

Register(Classes.FromAssembly(GetType().Assembly)
            .Where(x => x.Name.EndsWith("ViewModel"))
            .WithService.Self()
            .WithService.AllInterfaces()
            .Configure(x => x.LifeStyle.Is(LifestyleType.Transient)));

To;

Register(Classes.FromAssembly(GetType().Assembly)
            .Where(x => x.Name.EndsWith("ViewModel"))
            .WithService.Self()
            .WithService.DefaultInterfaces()
            .Configure(x => x.LifeStyle.Is(LifestyleType.Transient)));

Seems that using AllInterfaces was producing a complex and unneccessary dependency tree. There was no harm in this case in slimming down the dependencies somewhat by using DefaultInterfaces instead and that has resolved this exception, which I am speculating was related to a bad cyclic relationship between some View instances.

RJ Lohan
  • 6,497
  • 3
  • 34
  • 54
0

In my case, the "Window must be the root of the tree" error was caused by trying to bind the ItemsSource of a ListBox to a collection of DependencyObjects which happened to have a Window instance at index zero.

When establishing a collection binding, WPF examines the contents of the bound collection to ascertain if it will need to use an 'item container' for each item--versus whether you intend the contents of the collection to be shown as native UIElements (without a container). I believe this involves testing the first element in various ways.

This is a nice optimization in WPF: don't generate item containers if the data can be displayed directly. But if you do actually intend to treat the items in the collection as data (i.e. your app is a debugging aid or reflection tool), and if the data can contain a Window, then WPF's silent decision-making will get it wrong, because a Window can't be placed in the tree without an item container.

It's not just Window instances that can be a problem here. Perhaps more serious is that, if you were planning to insert those UIElements from your collection in another visual tree later on, this would also fail, since UIElements can't be in more than one visual tree.

The solution is simple: Just specify an ItemTemplate, which will force WPF to use item containers.

<ListBox ItemsSource="{Binding}">
    <ListBox.ItemTemplate>
        <DataTemplate DataType="DependencyObject">
            <TextBlock Text="{Binding}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

[edit:] The above code may not be enough of a solution. Please see https://stackoverflow.com/a/3844808/147511

Community
  • 1
  • 1
Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108