web-dev-qa-db-ja.com

TabControlをViewModelのコレクションにバインドするにはどうすればよいですか?

基本的には、MainViewModel.csにあります。

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

ただし、タブを作成するだけでなく、MVVMを維持しながらタブコンテンツを適切なビューモデルにロードしてリンクできるようにする必要があります。

基本的に、どのようにしてユーザーコントロールをタブアイテムのコンテンツとしてロードし、そのユーザーコントロールを適切なビューモデルに接続させることができますか?これを難しくしているのは、ViewModelが実際のビューアイテムを構築することになっていないということです。それともできますか?

基本的に、これは適切なMVVMでしょうか。

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

なぜなら、私はViewModel内からView(AddressControl)を構築しているからです。私にとっては、これはMVVMのように聞こえます。

64
michael

これはMVVMではありません。ビューモデルにUI要素を作成しないでください。

Tabの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>
126
Will

Prismでは、通常、タブコントロールを領域にして、バインドされたタブページコレクションを制御する必要がないようにします。

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

リージョンMainRegionに登録することで、ビューを追加できるようになりました。

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

そして、ここでプリズムの専門を見ることができます。ビューは、ViewModelによってインスタンス化されます。私の場合、Inversion of Controlコンテナー(UnityやMEFなど)を使用してViewModelを解決します。 ViewModelは、コンストラクター注入を介して注入されたビューを取得し、それ自体をビューのデータコンテキストとして設定します。

別の方法は、ビューのタイプをリージョンコントローラーに登録することです。

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 );
}

ビューのタイプを登録しているため、ビューは正しい領域に配置されます。

21
PVitt

UIとViewModelを分離するコンバーターがありますが、それが以下のポイントです。

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

TabはTabItemViewModelの列挙型であり、TabItemConverterはそれを実際のUIに変換します。

TabItemConverterで、値を取得し、必要なユーザーコントロールを返します。

1
acai

おそらくそう:

<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は互換性がありますか?

0
BigShady