简体   繁体   中英

How to link a viewmodel to a usercontrol inside a tabcontrol using PRISM, Mef, DI?

I am writing an app with a MS Office style tab ribbon on top. This tabcontrol is placed in a region in the shell which is set up by the Mef.

Code in shell.xaml:

...
<ContentControl Name="TabRegion" prism:RegionManager.RegionName="TabRegion"/>
...

Code in TabRegionView.xaml:

<UserControl x:Class="Ensor.Studio.View.TabRegionView"
             etc...>
    <TabControl Margin="5" BorderThickness="0" Background="White" ItemsSource="{Binding Path=Tabs}">
        <TabControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Title}"/>
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <DataTemplate>
                <ContentControl Content="{Binding Content}"/>
            </DataTemplate>
        </TabControl.ContentTemplate>         
    </TabControl>
</UserControl>

Code in tab content: ProjectTabView.xaml

<UserControl x:Class="Ensor.Studio.View.Tabs.ProjectTabView"
             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:ap="clr-namespace:Ensor.Studio.ViewModel.CustomControls"
             xmlns:vm="clr-namespace:Ensor.Studio.ViewModel.Tabs"
             mc:Ignorable="d" 
             d:DesignHeight="110" d:DesignWidth="800">

    <StackPanel Orientation="Horizontal">
        <Button Name="button_ProjectBrowse" Command="{Binding ProjectBrowseCommand}" 
                     Style="{StaticResource BigTabButton}" Margin="5,10                  
                     ap:ButtonProperties.Image="pack:.../ButtonIcons/open.png" 
                     Content="Browse"/>
        <Rectangle Margin="5,10" Stroke="Silver"/>
...

When I run the app, this tabcontrol renders fine, but the usercontrols are not linked with the right view model... When the shell initializes the following error pops up in the output:

System.Windows.Data Error: 40 : BindingExpression path error: 'ProjectBrowseCommand' property not found on 'object' ''TabRegionViewModel' (HashCode=37840511)'. BindingExpression:Path=ProjectBrowseCommand; DataItem='TabRegionViewModel' (HashCode=37840511); target element is 'Button' (Name='button_ProjectBrowse'); target property is 'Command' (type 'ICommand')

Which means that the view looked for a ProjectBrowseCommand in the TabRegionViewModel, which is the viewmodel of the whole tab control, while it actually should have looked in ProjectTabViewModel.

But the app keeps running so when I click on this first tab which makes the content of this tab appear and the following error pops up:

System.Windows.Data Error: 40 : BindingExpression path error: 'ProjectBrowseCommand' property not found on 'object' ''ProjectTabView' (Name='')'. BindingExpression:Path=ProjectBrowseCommand; DataItem='ProjectTabView' (Name=''); target element is 'Button' (Name='button_ProjectBrowse'); target property is 'Command' (type 'ICommand')

Which means this time it looked for a ProjectBrowseCommand in the backend code of the view of the specific tab.

What I'm actually trying to do is link the usercontrol in the ProjectTab to ProjectTabViewModel. This is a class with multiple dependencies so I'd rather not use DataContext = new ProjectTabViewModel(...);

So my question is, why did the view look in the wrong classes to find it's bindings. And how can I hook it up to ProjectTabViewModel with Dependency Injection?

What I would do is bind the ItemsSource to a ViewModel property of eg ObservableCollection<object> ( object can also be a base class for all tabs ViewModel).

Then use DataTemplate to tell WPF how to represents the different ViewModels:

For example likes this:


public class ViewModel
{
    public ObservableCollection<object> Tabs { get; } = new ObservableCollection<object>();

    public ViewModel()
    {
        Tabs.Add(new FirstViewModel());
    }
}

<TabControl ItemsSource="{Binding Path=Tabs}"></TabControl>

Resources:

<DataTemplate DataType="{x:Type viewModels:FirstViewModel} ">
    <views:FirstView/>
</DataTemplate>

UPDATE

Using dependency injection:


public class ViewModel
{
    public ObservableCollection<TabViewModelBase> Tabs { get; } = new ObservableCollection<object>();

    public ViewModel(IEnumerable<TabViewModelBase> tabs)
    {
        Tabs.Add(tabs);
    }
}

public class FirstViewModel : TabViewModelBase
{
    
}

I assume that you know how to configure a container in the root of your application.

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