0

I am using CodePlex wpfmdi container for my WPF application.

I need to bind MdiContainer's children to a viewModel property.

<mdi:MdiContainer Name="Container" Grid.Row="1" Background="GhostWhite" Children="{Binding Path=Container}"/>

If I do this I am getting this error:

Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.Collections.ObjectModel.ObservableCollection`1[WPF.MDI.MdiChild]'

This is what the Children property in MdiContainer looks like:

public ObservableCollection<MdiChild> Children { get; set; }

What am I doing wrong?

thatguy
  • 21,059
  • 6
  • 30
  • 40
Beqa Latsabidze
  • 212
  • 4
  • 12

1 Answers1

0

The Children property is not exposed as a dependency property, which means you cannot bind it. Furthermore, it is initialized once in the constructor of the MdiContainer type and then a handler is added to the CollectionChanged event of the underlying ObservableCollection<MdiChild>. It is never updated or removed.

Therefore, although the Children property has a setter, it will break the control if you use it to set a different collection. This also implies that you cannot simply create attached properties to expose a bindable Children dependency property.

Apart from that, MdiChild is a Control, so it actually contradicts the purpose of your view model. If you expose a collection of user interface controls from your view model this conflicts with the MVVM pattern. View models should not have any knowledge about the view. However, the MDI controls do not seem to follow the usual WPF practices for custom controls, so there is not much room for improvement here, data templating is not supported, the MdiContainer is a UserControl and there are very limited dependency properties.

If you really want to continue working with this control with your current approach, you could:

  • Create a custom attached behavior to synchronize your view model collection with the Children collection of the MdiContainer and vice-versa, see XAML behaviors in WPF.

  • Use the Loaded event to assign the Children collection to your view model property.

    <mdi:MdiContainer Name="Container" Grid.Row="1" Background="GhostWhite" Loaded="MdiContainer_OnLoaded">
    
    private void MdiContainer_OnLoaded(object sender, RoutedEventArgs e)
    {
       var mdiContainer = (MdiContainer)sender;
       var dataContext = (Main)mdiContainer.DataContext;
    
       if (dataContext == null)
          return;
    
       dataContext.Children = mdiContainer.Children;
    }
    
  • Use an EventTrigger on the Loaded event with a custom trigger action that sets the Children collection. This is just a different variant of the previous approach that does not require code-behind.

    The new XAML behaviors for WPF package, which replaces the legacy Blend behaviors from the System.Windows.Interactivity namespace already includes such a trigger action. Install the Microsoft.Xaml.Behaviors.Wpf NuGet package and use this:

    <mdi:MdiContainer Name="Container" Grid.Row="1" Background="GhostWhite">
       <behaviors:Interaction.Triggers>
          <behaviors:EventTrigger EventName="Loaded">
             <behaviors:ChangePropertyAction TargetObject="{Binding DataContext, ElementName=Container}"
                                             PropertyName="Children"
                                             Value="{Binding Children, ElementName=Container}"/>
          </behaviors:EventTrigger>
       </behaviors:Interaction.Triggers>
    </mdi:MdiContainer>
    

Note that with these approaches, you either synchronize to your own collection or you work directly with the collection of the MdiContainer that you passed to your view model. These are only workarounds. If you would want to implement this in a clean and MVVM compliant way, I think you would need to extend or fix the control itself, which is rather costly and not recommendable, since it seems to be dead anyway.

thatguy
  • 21,059
  • 6
  • 30
  • 40