在TabControl中展示多种控件类型

3

大家好,我已经创建了一个TabControl来容纳包含单个控件类型的TabitemMainWindow.xaml文件的标记如下所示:

...
<TabControl x:Name="tabControl" 
            ItemsSource="{Binding Path=Workspaces}" 
            SelectedIndex="{Binding SelectedIndex}"
            IsSynchronizedWithCurrentItem="true" 
            HorizontalContentAlignment="Stretch" 
            VerticalContentAlignment="Stretch" 
            HorizontalAlignment="Stretch" 
            VerticalAlignment="Stretch" 
            TabStripPlacement="Top" 
            Margin="5,0,5,0">
    <TabControl.ItemContainerStyle>
        <Style TargetType="TabItem">
            <Setter Property="Header" Value="{Binding Path=DisplayName}"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="VerticalContentAlignment" Value="Stretch"/>
        </Style>
    </TabControl.ItemContainerStyle>
    <TabControl.ContentTemplate>
        <DataTemplate>
            <Views:ResourceControl DataContext="{Binding}" 
                                   HorizontalAlignment="Stretch" 
                                   VerticalAlignment="Stretch"/>
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>
...

这对于我的Views:ResourceControl非常有效,但我现在想扩展在TabControl中显示的控件类型。为了做到这一点,我创建了控件,它们都链接到继承自基础'WorkspaceViewModel' 的视图模型,并且我已经创建了下面的资源文件。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:ViewModels="clr-namespace:ResourceStudio.ViewModels"
                    xmlns:Views="clr-namespace:ResourceStudio.Views">
    <DataTemplate DataType="{x:Type ViewModels:ResourceDataViewModel}">
        <Views:ResourceControl />
    </DataTemplate>
    <DataTemplate DataType="{x:Type ViewModels:StartPageViewModel}">
        <Views:StartPageControl/>
    </DataTemplate>
    <DataTemplate x:Key="WorkspacesTemplate">
        <TabControl x:Name="tabControl" 
                    IsSynchronizedWithCurrentItem="true" 
                    ItemsSource="{Binding}" 
                    SelectedIndex="{Binding SelectedIndex}"
                    HorizontalContentAlignment="Stretch" 
                    VerticalContentAlignment="Stretch" 
                    HorizontalAlignment="Stretch" 
                    VerticalAlignment="Stretch" 
                    TabStripPlacement="Top" 
                    Margin="5,0,5,0">
            <TabControl.ItemContainerStyle>
                <Style TargetType="TabItem">
                    <Setter Property="Header" Value="{Binding Path=DisplayName}"/>
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                    <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                </Style>
            </TabControl.ItemContainerStyle>
        </TabControl>
    </DataTemplate>
</ResourceDictionary>

现在在 MainWindow.xaml 中我有:

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="MainWindowResources.xaml" />
        </ResourceDictionary.MergedDictionaries>
        <Style x:Key="DescriptionHeaderStyle" TargetType="Label">
            <Setter Property="FontSize" Value="22" />
            <Setter Property="HorizontalAlignment" Value="Center" />
        </Style>
    </ResourceDictionary>
</Window.Resources>
...
<ContentControl
    Content="{Binding Path=Workspaces}" 
    ContentTemplate="{StaticResource WorkspacesTemplate}">
</ContentControl>

绑定似乎已经注册到我想要显示的不同视图上,并且选项卡也会显示相应的标题。然而,实际控件直到我加载另一个表单后才会显示出来。似乎 SelectedIndex 没有绑定,当切换/加载选项卡时视图也没有更新。 我该如何更改 WorkspaceTemplate 来解决这个问题? 感谢您的时间。
编辑。请求关于 MainViewModel 的信息。
public class MainWindowViewModel : WorkspaceViewModel 
{
    private readonly IDialogService dialogService;
    private WorkspaceViewModel selectedWorkspace;
    private ObservableCollection<WorkspaceViewModel> workspaces;
    private Dictionary<string, string> resourceDictionary;

    public MainWindowViewModel()
    {
        base.DisplayName = "SomeStringName";
        resourceDictionary = new Dictionary<string, string>();
        dialogService = ServiceLocator.Resolve<IDialogService>();
        Contract.Requires(dialogService != null);
    }

    ...

    private void OnWorkspacesChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null && e.NewItems.Count != 0)
            foreach (WorkspaceViewModel workspace in e.NewItems)
                workspace.RequestClose += this.OnWorkspaceRequestClose;

        if (e.OldItems != null && e.OldItems.Count != 0)
            foreach (WorkspaceViewModel workspace in e.OldItems)
                workspace.RequestClose -= this.OnWorkspaceRequestClose;
    }

    private void OnWorkspaceRequestClose(object sender, EventArgs e)
    {
        WorkspaceViewModel workspace = sender as WorkspaceViewModel;
        workspace.Dispose();
        int currentIndex = Workspaces.IndexOf(workspace);
        this.Workspaces.Remove(workspace);
        if (this.Workspaces.Count > 0)
            this.SetActiveWorkspace(Workspaces[currentIndex - 1]);
    }

    private void SetActiveWorkspace(WorkspaceViewModel workspace)
    {
        Debug.Assert(this.Workspaces.Contains(workspace));
        ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.Workspaces);
        if (collectionView != null)
            collectionView.MoveCurrentTo(workspace);
    }

    public WorkspaceViewModel SelectedWorkspace
    {
        get { return selectedWorkspace; }
        set { selectedWorkspace = value; }
    }   

    private int selectedIndex = 0;
    public int SelectedIndex
    {
        get { return selectedIndex; }
        set
        {
            if (selectedIndex == value)
                return;
            selectedIndex = value;
            OnPropertyChanged("SelectedIndex");
        }
    }

    /// <summary>
    /// Returns the collection of available workspaces to display.
    /// A 'workspace' is a ViewModel that can request to be closed.
    /// </summary>
    public ObservableCollection<WorkspaceViewModel> Workspaces
    {
        get
        {
            if (workspaces == null)
            {
                workspaces = new ObservableCollection<WorkspaceViewModel>();
                workspaces.CollectionChanged += this.OnWorkspacesChanged;
            }
            return workspaces;
        }
    }
    ...
}

能否展示一下你包含“SelectedIndex”属性的VM代码,当你说正在加载表单时,确切是指什么? - AsitK
@AsitK 问题已经更新。感谢您的时间... - MoonKnight
1个回答

1
我使用了您的代码,遇到了类似的问题。不确定是否是同一个问题。当我的应用程序加载时,它显示了第一个选项卡(0th)及其正确的内容。当选择第二个内容区域时,它变成空白并且没有反应。我将Content="{Binding Path=Workspaces}"移到了TabControl中。即:Content="{Binding}"
<TabControl ItemsSource="{Binding Workspaces}"/> 

它开始显示选项卡的内容。我使用了您虚拟机的更简化版本,只包含WorkSpaces集合。


太棒了。谢谢你。虽然我不太确定为什么这个方法能够工作而上面的方法不能。应该是绑定级联需要向上遍历元素树,但是这个方法却似乎是向下遍历的?! - MoonKnight
1
针对您的原始绑定,我在输出窗口中检查了绑定错误,并发现WorkspaceVM上没有找到SelectedIndex属性,即它正在错误的VM上查找。此外,如果没有显示选定内容并且我最小化窗口,将键盘焦点移到其他窗口并返回它,那么它会显示所选选项卡的内容..奇怪..!!!! 新的绑定确保您的TabControl的DataContext是MainWindowVM,并从中获取SelectedIndex。看起来将IEnumberable作为DataContext不是一个好主意.. - AsitK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接