簡體   English   中英

如何將 TabControl 綁定到 ViewModel 集合?

[英]How do I bind a TabControl to a collection of ViewModels?

基本上我在我的 MainViewModel.cs 中有:

ObservableCollection<TabItem> MyTabs { get; private set; }

但是,我需要以某種方式不僅能夠創建標簽,而且能夠在維護 MVVM 的同時加載標簽內容並將其鏈接到相應的視圖模型。

基本上,我怎樣才能讓用戶控件作為 tabitem 的內容加載並讓該用戶控件連接到適當的視圖模型。 使這變得困難的部分是 ViewModel 不應該構造實際的視圖項,對嗎? 或者可以嗎?

基本上,這是否適合 MVVM:

UserControl address = new AddressControl();
NotificationObject vm = new AddressViewModel();
address.DataContext = vm;
MyTabs[0] = new TabItem()
{
    Content = address;
}

我只是問,因為好吧,我正在從 ViewModel 中構建一個視圖(AddressControl),對我來說這聽起來像是 MVVM 禁忌。

這不是 MVVM。 你不應該在你的視圖模型中創建 UI 元素。

您應該將選項卡的 ItemsSource 綁定到您的 ObservableCollection,並且該模型應該包含有關應創建的選項卡的信息的模型。

以下是代表標簽頁的 VM 和模型:

public sealed class ViewModel
{
    public ObservableCollection<TabItem> Tabs {get;set;}
    public ViewModel()
    {
        Tabs = new ObservableCollection<TabItem>();
        Tabs.Add(new TabItem { Header = "One", Content = "One's content" });
        Tabs.Add(new TabItem { Header = "Two", Content = "Two's content" });
    }
}
public sealed class TabItem
{
    public string Header { get; set; }
    public string Content { get; set; }
}

這是綁定在窗口中的外觀:

<Window x:Class="WpfApplication12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <ViewModel
            xmlns="clr-namespace:WpfApplication12" />
    </Window.DataContext>
    <TabControl
        ItemsSource="{Binding Tabs}">
        <TabControl.ItemTemplate>
            <!-- this is the header template-->
            <DataTemplate>
                <TextBlock
                    Text="{Binding Header}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.ContentTemplate>
            <!-- this is the body of the TabItem template-->
            <DataTemplate>
                <TextBlock
                    Text="{Binding Content}" />
            </DataTemplate>
        </TabControl.ContentTemplate>
    </TabControl>
</Window>

(注意,如果你想在不同的標簽中使用不同的東西,使用DataTemplates 。每個標簽的視圖模型應該是它自己的類,或者創建一個自定義的DataTemplateSelector來選擇正確的模板。)

數據模板中的 UserControl:

<TabControl
    ItemsSource="{Binding Tabs}">
    <TabControl.ItemTemplate>
        <!-- this is the header template-->
        <DataTemplate>
            <TextBlock
                Text="{Binding Header}" />
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <!-- this is the body of the TabItem template-->
        <DataTemplate>
            <MyUserControl xmlns="clr-namespace:WpfApplication12" />
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

在 Prism 中,您通常讓標簽控制一個區域,這樣您就不必控制綁定的標簽頁集合。

<TabControl 
    x:Name="MainRegionHost"
    Regions:RegionManager.RegionName="MainRegion" 
    />

現在可以通過將自己注冊到區域 MainRegion 來添加視圖:

RegionManager.RegisterViewWithRegion( "MainRegion", 
    ( ) => Container.Resolve<IMyViewModel>( ).View );

在這里您可以看到 Prism 的特色。 View 由 ViewModel 實例化。 就我而言,我通過控制反轉容器(例如 Unity 或 MEF)解析 ViewModel。 ViewModel 通過構造函數注入獲取注入的 View 並將自身設置為 View 的數據上下文。

另一種方法是將視圖的類型注冊到區域控制器中:

RegionManager.RegisterViewWithRegion( "MainRegion", typeof( MyView ) );

使用這種方法允許您稍后在運行時創建視圖,例如通過控制器:

IRegion region = this._regionManager.Regions["MainRegion"];

object mainView = region.GetView( MainViewName );
if ( mainView == null )
{
    var view = _container.ResolveSessionRelatedView<MainView>( );
    region.Add( view, MainViewName );
}

因為您已經注冊了視圖的類型,所以視圖被放置在正確的區域中。

我有一個轉換器來解耦 UI 和 ViewModel,這就是以下幾點:

<TabControl.ContentTemplate>
    <DataTemplate>
        <ContentPresenter Content="{Binding Tab,Converter={StaticResource TabItemConverter}"/>
    </DataTemplate>
</TabControl.ContentTemplate>

Tab 是我的 TabItemViewModel 中的枚舉,TabItemConverter 將其轉換為真實的 UI。

在 TabItemConverter 中,只需獲取值並返回您需要的用戶控件。

可能是這樣:

<UserControl x:Class="Test_002.Views.MainView"
         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:local="clr-namespace:Test_002.Views"
         xmlns:generalView="clr-namespace:Test_002.Views.General"
         xmlns:secVIew="clr-namespace:Test_002.Views.Security"
         xmlns:detailsView="clr-namespace:Test_002.Views.Details"
         mc:Ignorable="d" 
         d:DesignHeight="400" d:DesignWidth="650">
<Grid>
    <DockPanel>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" Margin="2,5">
            <Button Command="{Binding btnPrev}" Content="Prev"/>
            <Button Command="{Binding btnNext}" Content="Next"/>
            <Button Command="{Binding btnSelected}" Content="Selected"/>
        </StackPanel>
        <TabControl>
            <TabItem Header="General">
                <generalView:GeneralView></generalView:GeneralView>
            </TabItem>
            <TabItem Header="Security">
                <secVIew:SecurityView></secVIew:SecurityView>
            </TabItem>
            <TabItem Header="Details">
                <detailsView:DetailsView></detailsView:DetailsView>
            </TabItem>
        </TabControl>
    </DockPanel>
</Grid>

認為這是最簡單的方法。 MVVM 兼容嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM