Search code examples
c#wpfmvvmtabcontroldatacontext

How to bind the datacontext of a tabcontrol content to an instance of an viewmodel in a observablecollection


I want to bind the content of my TabControl to an instance of my StepViewModel in a ObservableCollection Steps.

My ProcessViewModel:

pubic class ProcessViewModel : ViewModelBase
{
    public ObservableCollection<StepViewModel> Steps
    {
        get { return _steps; }
        set { _steps = value; OnPropertyChanged("Steps"); }
    }
    public StepViewModel SelectedStep
    {
        // like above...
    }
}

My StepViewModel (DataContext should be the StepVMs in Steps of ProcessVM):

public class StepViewModel : ViewModelBase
{
    public string Name { get {...} set {...} }
    public object Media { get {...} set {...} }
    //...
}

My TabControl (DataContext is ProcessViewModel):

<C1:C1TabControl
    ItemsSource="{Binding Steps}"
    SelectedItem="{Binding SelectedStep}"
    SelectionChanged="{tcSteps_OnSelectionChanged">
           <C1:C1TabControl.ContentTemplate>
                <DataTemplate>
                    <local:StepView
                        DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vmns:ProcessViewModel}}, Path=SelectedStep}"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch">
                    </local:StepView>
                </DataTemplate>
            </C1:C1TabControl.ContentTemplate>
</C1:C1TabControl>

The Compiler delivers the following output message:

"System.Windows.Data Error: 40 : BindingExpression path error: 'SelectedStep' property not found on 'object' ''StepViewModel' (HashCode=32952144)'. BindingExpression:Path=SelectedStep; DataItem='StepViewModel' (HashCode=32952144); target element is 'StepView' (Name='StepView'); target property is 'DataContext' (type 'Object')"

Does anyone know how I can solve the? Thanks!


Solution

  • It looks like there could be a few small issues with the RelativeSource on your DataContext binding for the StepView. Since the template is not part of the visual tree, I don't think you can use FindAncestor. You can use a StaticResource as a pointer to your main DataContext (eg http://www.codeproject.com/Articles/27432/Artificial-Inheritance-Contexts-in-WPF), but I think it might be simpler to just search by ElementName instead in this case. That method would look something like this:

    Update your TabControl to have a name, so it is searchable in bindings by ElementName

    <C1:C1TabControl
        x:Name="MyTabControl"
        ItemsSource="{Binding Steps}"
        SelectedItem="{Binding SelectedStep}"
        SelectionChanged="{tcSteps_OnSelectionChanged">
    

    Update your StepView to look for the TabControl's DataContext by ElementName

        <local:StepView DataContext="{Binding ElementName=MyTabControl, Path=DataContext.SelectedStep}" 
                        HorizontalAlignment="Stretch" 
                        VerticalAlignment="Stretch">
        </local:StepView>