简体   繁体   中英

Unable to locate DataContext of parent object

I'm trying to make a TabControl , populated with few UserControl s each with a separate ViewModel.

I've declared it in my XAML like this:

<TabControl Margin="10,10,10,40" ItemsSource="{Binding Test}">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name}" FontSize="16" Padding="5"/>
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>

As you can see I'm trying to obtain the name of the tab through a property located in it's View Model:

public class GeneralTabViewModel : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public GeneralTabViewModel()
    {
        Name = "Name";
    }

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

The View Model associated with the window that holds the TabControl is declared as follows:

public class SettingsViewModel
{
    public ObservableCollection<UserControl> Test { get; set; }

    public SettingsViewModel()
    {
        Test = new ObservableCollection<UserControl>
        {
            new GeneralTab(),
            new GeneralTab()
        };
    }
}

Building the program like this yields an empty Header for the 2 tabs.

How can I bind the TextBlock 's Text property to the Name property located in the View Model of the UserControl ?

If I change the ItemSource type from ObservableCollection<UserControl> to ObservableCollection<GeneralTabViewModel> I will be able to access the property, but how am I going to visualize the UserControl than?

It is wrong to have UserControls in your SettingsViewModel. It should instead have a collection of GeneralTabViewModels:

public class SettingsViewModel
{
    public ObservableCollection<GeneralTabViewModel> Test { get; }

    public SettingsViewModel()
    {
        Test = new ObservableCollection<GeneralTabViewModel>
        {
            new GeneralTabViewModel { Name = "Tab 1" },
            new GeneralTabViewModel { Name = "Tab 2" }
        };
    }
}

You would then use the UserControl in the ItemTemplate of your TabControl:

<TabControl Margin="10,10,10,40" ItemsSource="{Binding Test}">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <GeneralTab TabName="{Binding Name}"/>
        </DataTemplate>
    </TabControl.ItemTemplate>
</TabControl>

In order to make ths work, it is important that GeneralTab does not explicitly set its DataContext property. It may look like this:

<UserControl x:Class="YourNamespace.GeneralTab" ...>
    <Grid>
        <TextBlock FontSize="16" Padding="5"
                   Text="{Binding TabName,
                          RelativeSource={RelativeSource AncestorType=UserControl}}"/>
    </Grid>
</UserControl>

with this dependency property in its code behind:

public static readonly DependencyProperty TabNameProperty =
    DependencyProperty.Register(nameof(TabName), typeof(string), typeof(GeneralTab));

public string TabName
{
    get { return (string)GetValue(TabNameProperty); }
    set { SetValue(TabNameProperty, value); }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM